/* * Copyright (C) 2012 Giuliano Grassi * Copyright (C) 2012 Ralf Sager * Copyright (C) 2012 Tobias Brunner * Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. See . * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. */ #include #include #include #include "charonservice.h" #include "android_jni.h" #include "kernel/android_ipsec.h" #include "kernel/android_net.h" #include #include #include #include #include #define ANDROID_DEBUG_LEVEL 1 typedef struct private_charonservice_t private_charonservice_t; /** * private data of charonservice */ struct private_charonservice_t { /** * public interface */ charonservice_t public; /** * CharonVpnService reference */ jobject vpn_service; }; /** * Single instance of charonservice_t. */ charonservice_t *charonservice; /** * hook in library for debugging messages */ extern void (*dbg)(debug_t group, level_t level, char *fmt, ...); /** * Logging hook for library logs, using android specific logging */ static void dbg_android(debug_t group, level_t level, char *fmt, ...) { va_list args; if (level <= ANDROID_DEBUG_LEVEL) { char sgroup[16], buffer[8192]; char *current = buffer, *next; snprintf(sgroup, sizeof(sgroup), "%N", debug_names, group); va_start(args, fmt); vsnprintf(buffer, sizeof(buffer), fmt, args); va_end(args); while (current) { /* log each line separately */ next = strchr(current, '\n'); if (next) { *(next++) = '\0'; } __android_log_print(ANDROID_LOG_INFO, "charon", "00[%s] %s\n", sgroup, current); current = next; } } } METHOD(charonservice_t, bypass_socket, bool, private_charonservice_t *this, int fd, int family) { JNIEnv *env; jmethodID method_id; androidjni_attach_thread(&env); method_id = (*env)->GetMethodID(env, android_charonvpnservice_class, "protect", "(I)Z"); if (!method_id) { goto failed; } if (!(*env)->CallBooleanMethod(env, this->vpn_service, method_id, fd)) { DBG1(DBG_CFG, "VpnService.protect() failed"); goto failed; } androidjni_detach_thread(); return TRUE; failed: androidjni_exception_occurred(env); androidjni_detach_thread(); return FALSE; } /** * Initialize the charonservice object */ static void charonservice_init(JNIEnv *env, jobject service) { private_charonservice_t *this; static plugin_feature_t features[] = { PLUGIN_CALLBACK(kernel_net_register, kernel_android_net_create), PLUGIN_PROVIDE(CUSTOM, "kernel-net"), PLUGIN_CALLBACK(kernel_ipsec_register, kernel_android_ipsec_create), PLUGIN_PROVIDE(CUSTOM, "kernel-ipsec"), }; INIT(this, .public = { .bypass_socket = _bypass_socket, }, .vpn_service = (*env)->NewGlobalRef(env, service), ); charonservice = &this->public; lib->plugins->add_static_features(lib->plugins, "androidbridge", features, countof(features), TRUE); lib->settings->set_int(lib->settings, "charon.plugins.android_log.loglevel", ANDROID_DEBUG_LEVEL); } /** * Deinitialize the charonservice object */ static void charonservice_deinit(JNIEnv *env) { private_charonservice_t *this = (private_charonservice_t*)charonservice; (*env)->DeleteGlobalRef(env, this->vpn_service); free(this); charonservice = NULL; } /** * Handle SIGSEGV/SIGILL signals raised by threads */ static void segv_handler(int signal) { dbg_android(DBG_DMN, 1, "thread %u received %d", thread_current_id(), signal); exit(1); } /** * Initialize charon and the libraries via JNI */ JNI_METHOD(CharonVpnService, initializeCharon, void) { struct sigaction action; /* logging for library during initialization, as we have no bus yet */ dbg = dbg_android; /* initialize library */ if (!library_init(NULL)) { library_deinit(); return; } if (!libhydra_init("charon")) { libhydra_deinit(); library_deinit(); return; } if (!libipsec_init()) { libipsec_deinit(); libhydra_deinit(); library_deinit(); return; } charonservice_init(env, this); if (!libcharon_init("charon") || !charon->initialize(charon, PLUGINS)) { libcharon_deinit(); charonservice_deinit(env); libipsec_deinit(); libhydra_deinit(); library_deinit(); return; } /* add handler for SEGV and ILL etc. */ action.sa_handler = segv_handler; action.sa_flags = 0; sigemptyset(&action.sa_mask); sigaction(SIGSEGV, &action, NULL); sigaction(SIGILL, &action, NULL); sigaction(SIGBUS, &action, NULL); action.sa_handler = SIG_IGN; sigaction(SIGPIPE, &action, NULL); /* start daemon (i.e. the threads in the thread-pool) */ charon->start(charon); } /** * Deinitialize charon and all libraries */ JNI_METHOD(CharonVpnService, deinitializeCharon, void) { libcharon_deinit(); charonservice_deinit(env); libipsec_deinit(); libhydra_deinit(); library_deinit(); }