+/*----- A custom noise source ---------------------------------------------*/
+
+static void javanoise(rand_pool *r)
+{
+ JNIEnv *jni = jni_tripe;
+ jclass cls;
+ jmethodID mid;
+ jbyteArray v;
+ jbyte *p;
+ jsize n;
+
+ noise_devrandom(r);
+
+ assert(jni);
+ cls = (*jni)->FindClass(jni, RANDCLS); assert(cls);
+ mid = (*jni)->GetStaticMethodID(jni, cls, "getSeed", "(I)[B"); assert(mid);
+ v = (*jni)->CallStaticObjectMethod(jni, cls, mid, 32);
+ if (v) {
+ n = (*jni)->GetArrayLength(jni, v);
+ p = (*jni)->GetByteArrayElements(jni, v, 0);
+ rand_add(r, p, n, n);
+ (*jni)->ReleaseByteArrayElements(jni, v, p, JNI_ABORT);
+ }
+ if ((*jni)->ExceptionOccurred(jni)) {
+ (*jni)->ExceptionDescribe(jni);
+ (*jni)->ExceptionClear(jni);
+ }
+}
+
+static const rand_source javasource = { javanoise, noise_timer };
+
+/*----- Embedding the TrIPE server ----------------------------------------*/
+
+static void lock_tripe(JNIEnv *jni)
+{
+ jclass cls = (*jni)->FindClass(jni, LOCKCLS); assert(cls);
+ (*jni)->MonitorEnter(jni, cls);
+}
+
+static void unlock_tripe(JNIEnv *jni)
+{
+ jclass cls = (*jni)->FindClass(jni, LOCKCLS); assert(cls);
+ (*jni)->MonitorExit(jni, cls);
+}
+
+#define STATES(_) \
+ _(INIT) \
+ _(RESOLVE) \
+ _(KEYS) \
+ _(BIND) \
+ _(READY) \
+ _(RUNNING)
+
+enum {
+#define DEFTAG(st) st,
+ STATES(DEFTAG)
+#undef DEFTAG
+ MAXSTATE
+};
+
+static const char *statetab[] = {
+#define DEFNAME(st) #st,
+ STATES(DEFNAME)
+#undef DEFNAME
+};
+
+static unsigned state = INIT;
+static int clientsk = -1;
+
+static const char *statename(unsigned st)
+{
+ if (st >= MAXSTATE) return ("<invalid>");
+ else return (statetab[st]);
+}
+
+static int ensure_state(JNIEnv *jni, unsigned want)
+{
+ unsigned cur;
+
+ lock_tripe(jni);
+ cur = state;
+ unlock_tripe(jni);
+
+ if (cur != want) {
+ except(jni, STERR, "server is in state %s (%u), not %s (%u)",
+ statename(cur), cur, statename(want), want);
+ return (-1);
+ }
+ return (0);
+}
+
+JNIEXPORT void JNICALL JNIFUNC(base_1init)(JNIEnv *jni, jobject cls)
+{
+ int fd[2];
+ int i;
+
+ for (i = 0; i < N(fd); i++) fd[i] = -1;
+
+ lock_tripe(jni);
+ jni_tripe = jni;
+ if (ensure_state(jni, INIT)) goto end;
+
+ if (socketpair(PF_UNIX, SOCK_STREAM, 0, fd)) {
+ except_syserror(jni, SYSERR, errno, "failed to create socket pair");
+ goto end;
+ }
+
+ clientsk = fd[0]; fd[0] = -1;
+
+ rand_noisesrc(RAND_GLOBAL, &javasource);
+ rand_seed(RAND_GLOBAL, MAXHASHSZ);
+ lp_init();
+ a_create(fd[1], fd[1], AF_NOTE | AF_WARN | AF_TRACE); fd[1] = -1;
+ a_switcherr();
+ p_addtun(&tun_java); p_setdflttun(&tun_java);
+ p_init();
+ kx_init();
+
+ state++;
+
+end:
+ for (i = 0; i < N(fd); i++) if (fd[i] != -1) close(fd[i]);
+ jni_tripe = 0;
+ unlock_tripe(jni);
+}
+
+JNIEXPORT void JNICALL JNIFUNC(setup_1resolver)(JNIEnv *jni, jobject cls)
+{
+ lock_tripe(jni);
+ if (ensure_state(jni, RESOLVE)) goto end;
+
+ if (a_init())
+ { except(jni, INITERR, "failed to initialize resolver"); return; }
+
+ state++;
+
+end:
+ unlock_tripe(jni);
+}
+
+JNIEXPORT void JNICALL JNIFUNC(load_1keys)(JNIEnv *jni, jobject cls,
+ jobject privstr, jobject pubstr,
+ jobject tagstr)
+{
+ const char *priv = 0, *pub = 0, *tag = 0;
+
+ lock_tripe(jni);
+ if (ensure_state(jni, KEYS)) return;
+
+ priv = get_cstring(jni, privstr); if (!priv) goto end;
+ pub = get_cstring(jni, pubstr); if (!pub) goto end;
+ tag = get_cstring(jni, tagstr); if (!tag) goto end;
+
+ if (km_init(priv, pub, tag))
+ { except(jni, INITERR, "failed to load initial keys"); goto end; }
+
+ state++;
+
+end:
+ put_cstring(jni, privstr, priv);
+ put_cstring(jni, pubstr, pub);
+ put_cstring(jni, tagstr, tag);
+ unlock_tripe(jni);
+}
+
+JNIEXPORT void JNICALL JNIFUNC(unload_1keys)(JNIEnv *jni, jobject cls)
+{
+ lock_tripe(jni);
+ if (ensure_state(jni, KEYS + 1)) goto end;
+
+ km_clear();
+
+ state--;
+
+end:
+ unlock_tripe(jni);
+}
+
+JNIEXPORT void JNICALL JNIFUNC(bind)(JNIEnv *jni, jobject cls,
+ jbyteArray hoststr, jbyteArray svcstr)
+{
+ const char *host = 0, *svc = 0;
+ struct addrinfo hint, *ai = 0;
+ int err;
+
+ lock_tripe(jni);
+ if (ensure_state(jni, BIND)) goto end;
+
+ if (hoststr) { host = get_cstring(jni, hoststr); if (!host) goto end; }
+ svc = get_cstring(jni, svcstr); if (!svc) goto end;
+
+ hint.ai_socktype = SOCK_DGRAM;
+ hint.ai_family = AF_UNSPEC;
+ hint.ai_protocol = IPPROTO_UDP;
+ hint.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
+ err = getaddrinfo(host, svc, &hint, &ai);
+ if (err) {
+ except(jni, NAMEERR, "failed to resolve %c%s%c, port `%s': %s",
+ host ? '`' : '<', host ? host : "nil", host ? '\'' : '>',
+ svc, gai_strerror(err));
+ goto end;
+ }
+
+ if (p_bind(ai))
+ { except(jni, INITERR, "failed to bind master socket"); goto end; }
+
+ state++;
+
+end:
+ if (ai) freeaddrinfo(ai);
+ put_cstring(jni, hoststr, host);
+ put_cstring(jni, svcstr, svc);
+ unlock_tripe(jni);
+}
+
+JNIEXPORT void JNICALL JNIFUNC(unbind)(JNIEnv *jni, jobject cls)
+{
+ lock_tripe(jni);
+ if (ensure_state(jni, BIND + 1)) goto end;
+
+ p_unbind();
+
+ state--;
+
+end:
+ unlock_tripe(jni);
+}
+
+JNIEXPORT void JNICALL JNIFUNC(mark)(JNIEnv *jni, jobject cls, jint seq)
+{
+ lock_tripe(jni);
+ a_notify("MARK", "%d", seq, A_END);
+ unlock_tripe(jni);
+}
+
+JNIEXPORT void JNICALL JNIFUNC(run)(JNIEnv *jni, jobject cls)
+{
+ lock_tripe(jni);
+ if (ensure_state(jni, READY)) goto end;
+ assert(!jni_tripe);
+ jni_tripe = jni;
+ state = RUNNING;
+ unlock_tripe(jni);
+
+ lp_run();
+
+ lock_tripe(jni);
+ jni_tripe = 0;
+ state = READY;
+
+end:
+ unlock_tripe(jni);
+}
+