/* * Android iodine client runner — uses VpnService TUN fd (no open_tun). */ #include #include #include #include #include #include #include #include #include #include "client.h" #include "common.h" #define IODINE_LOG_TAG "IodineNative" #define ALOGI(...) __android_log_print(ANDROID_LOG_INFO, IODINE_LOG_TAG, __VA_ARGS__) #define ALOGW(...) __android_log_print(ANDROID_LOG_WARN, IODINE_LOG_TAG, __VA_ARGS__) static pthread_t iodine_thread; static volatile int iodine_running; static int iodine_dns_fd = -1; struct iodine_run_args { int tun_fd; char nameserv_host[256]; char topdomain[256]; char password[33]; }; static void iodine_sighandler(int sig) { (void) sig; client_stop(); } static void *iodine_thread_main(void *arg) { struct iodine_run_args *params = (struct iodine_run_args *) arg; int tun_fd = params->tun_fd; char *nameserv_host = params->nameserv_host; char *topdomain = params->topdomain; char *password = params->password; free(params); struct sockaddr_storage nameservaddr; int nameservaddr_len; int dns_fd = -1; int retval = 1; client_init(); client_set_selecttimeout(4); client_set_lazymode(1); client_set_topdomain(topdomain); client_set_password(password); client_set_hostname_maxlen(0xFF); nameservaddr_len = get_addr(nameserv_host, DNS_PORT, AF_UNSPEC, 0, &nameservaddr); if (nameservaddr_len < 0) { ALOGW("nameserver lookup failed for %s", nameserv_host); iodine_running = 0; return NULL; } client_set_nameserver(&nameservaddr, nameservaddr_len); dns_fd = open_dns_from_host(NULL, 0, nameservaddr.ss_family, AI_PASSIVE); if (dns_fd < 0) { ALOGW("open_dns_from_host failed errno=%d", errno); iodine_running = 0; return NULL; } iodine_dns_fd = dns_fd; signal(SIGINT, iodine_sighandler); signal(SIGTERM, iodine_sighandler); ALOGI("iodine handshake %s via %s tun_fd=%d", topdomain, nameserv_host, tun_fd); if (client_handshake(dns_fd, 1, 1, 3072) != 0) { ALOGW("iodine handshake failed"); goto cleanup; } ALOGI("iodine tunnel active"); iodine_running = 1; client_tunnel(tun_fd, dns_fd); retval = 0; cleanup: if (dns_fd >= 0) { close_dns(dns_fd); iodine_dns_fd = -1; } iodine_running = 0; ALOGI("iodine thread exit retval=%d", retval); return NULL; } static int start_iodine_thread(int tun_fd, const char *nameserv, const char *topdomain, const char *password) { if (iodine_running) { return 0; } struct iodine_run_args *params = calloc(1, sizeof(*params)); if (!params) { return -1; } params->tun_fd = tun_fd; strncpy(params->nameserv_host, nameserv, sizeof(params->nameserv_host) - 1); strncpy(params->topdomain, topdomain, sizeof(params->topdomain) - 1); strncpy(params->password, password, sizeof(params->password) - 1); if (pthread_create(&iodine_thread, NULL, iodine_thread_main, params) != 0) { free(params); return -1; } pthread_detach(iodine_thread); return 0; } static void stop_iodine_thread(void) { if (!iodine_running) { return; } client_stop(); if (iodine_dns_fd >= 0) { shutdown(iodine_dns_fd, SHUT_RDWR); } iodine_running = 0; } JNIEXPORT jboolean JNICALL Java_com_foxx_androidcast_remoteaccess_IodineNative_start( JNIEnv *env, jclass clazz, jint tun_fd, jstring nameserv, jstring topdomain, jstring password) { (void) clazz; if (tun_fd < 0 || !nameserv || !topdomain || !password) { return JNI_FALSE; } const char *ns = (*env)->GetStringUTFChars(env, nameserv, NULL); const char *td = (*env)->GetStringUTFChars(env, topdomain, NULL); const char *pw = (*env)->GetStringUTFChars(env, password, NULL); if (!ns || !td || !pw) { if (ns) (*env)->ReleaseStringUTFChars(env, nameserv, ns); if (td) (*env)->ReleaseStringUTFChars(env, topdomain, td); if (pw) (*env)->ReleaseStringUTFChars(env, password, pw); return JNI_FALSE; } int rc = start_iodine_thread((int) tun_fd, ns, td, pw); (*env)->ReleaseStringUTFChars(env, nameserv, ns); (*env)->ReleaseStringUTFChars(env, topdomain, td); (*env)->ReleaseStringUTFChars(env, password, pw); return rc == 0 ? JNI_TRUE : JNI_FALSE; } JNIEXPORT void JNICALL Java_com_foxx_androidcast_remoteaccess_IodineNative_stop(JNIEnv *env, jclass clazz) { (void) env; (void) clazz; stop_iodine_thread(); } JNIEXPORT jboolean JNICALL Java_com_foxx_androidcast_remoteaccess_IodineNative_isRunning(JNIEnv *env, jclass clazz) { (void) env; (void) clazz; return iodine_running ? JNI_TRUE : JNI_FALSE; }