1
0
mirror of git://f0xx.org/ac/ac-mobile-android synced 2026-07-29 01:47:35 +03:00
Files
ac-mobile-android/ndk/jni/iodine_jni.c
Anton Afanasyeu 2619848d1b Implement native iodine client and fix RA heartbeat URL resolution.
Bundle iodine 0.7.0 with JNI TUN bridge; apply BE iodine credentials on connect; canonical heartbeat URL fallback.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-12 19:44:38 +02:00

166 lines
4.8 KiB
C

/*
* Android iodine client runner — uses VpnService TUN fd (no open_tun).
*/
#include <android/log.h>
#include <errno.h>
#include <jni.h>
#include <netdb.h>
#include <pthread.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#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;
}