#include <stdlib.h>
#include <string.h>
-#include <jni.h>
-
-#include <sys/types.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/select.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
+#include <sys/types.h>
#include <sys/un.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <dirent.h>
+
+#include <jni.h>
+
+//#include <linux/if.h>
+#include <linux/if_tun.h>
#include <mLib/align.h>
#include <mLib/bits.h>
#include <catacomb/ghash.h>
+#define TUN_INTERNALS
+#include <tripe.h>
+
#undef sun
/*----- Magic class names and similar -------------------------------------*/
/* The name decoration is horrific. Hide it. */
-#define JNIFUNC(f) Java_uk_org_distorted_tripe_jni_package_00024_##f
+#define JNIFUNC(f) Java_uk_org_distorted_tripe_sys_package_00024_##f
/* The little class for bundling up error codes. */
-#define ERRENTRY "uk/org/distorted/tripe/jni/package$ErrorEntry"
+#define ERRENTCLS "uk/org/distorted/tripe/sys/package$ErrorEntry"
+
+/* The `sys' package class. */
+#define SYSCLS "uk/org/distorted/tripe/sys/package"
+
+/* The server lock class. */
+#define LOCKCLS "uk/org/distorted/tripe/sys/package$ServerLock"
/* The `stat' class. */
-#define STAT "uk/org/distorted/tripe/jni/package$FileInfo"
+#define STATCLS "uk/org/distorted/tripe/sys/package$FileInfo"
+
+/* Standard Java classes. */
+#define FDCLS "java/io/FileDescriptor"
+#define STRCLS "java/lang/String"
+#define RANDCLS "java/security/SecureRandom"
/* Exception class names. */
#define NULLERR "java/lang/NullPointerException"
-#define TYPEERR "uk/org/distorted/tripe/jni/package$NativeObjectTypeException"
+#define TYPEERR "uk/org/distorted/tripe/sys/package$NativeObjectTypeException"
#define SYSERR "uk/org/distorted/tripe/sys/package$SystemError"
+#define NAMEERR "uk/org/distorted/tripe/sys/package$NameResolutionException"
+#define INITERR "uk/org/distorted/tripe/sys/package$InitializationException"
#define ARGERR "java/lang/IllegalArgumentException"
+#define STERR "java/lang/IllegalStateException"
#define BOUNDSERR "java/lang/IndexOutOfBoundsException"
-/*----- Miscellaneous utilities -------------------------------------------*/
+/*----- Essential state ---------------------------------------------------*/
-static void put_cstring(JNIEnv *jni, jbyteArray v, const char *p)
- { if (p) (*jni)->ReleaseByteArrayElements(jni, v, (jbyte *)p, JNI_ABORT); }
+static JNIEnv *jni_tripe = 0;
+
+/*----- Miscellaneous utilities -------------------------------------------*/
static void vexcept(JNIEnv *jni, const char *clsname,
- const char *msg, va_list ap)
+ const char *msg, va_list *ap)
{
jclass cls;
int rc;
if (!msg)
rc = (*jni)->ThrowNew(jni, cls, 0);
else {
- dstr_vputf(&d, msg, &ap);
+ dstr_vputf(&d, msg, ap);
rc = (*jni)->ThrowNew(jni, cls, d.buf);
assert(!rc);
dstr_destroy(&d);
va_list ap;
va_start(ap, msg);
- vexcept(jni, clsname, msg, ap);
+ vexcept(jni, clsname, msg, &ap);
va_end(ap);
}
return ((const char *)(*jni)->GetByteArrayElements(jni, v, 0));
}
+static void put_cstring(JNIEnv *jni, jbyteArray v, const char *p)
+ { if (p) (*jni)->ReleaseByteArrayElements(jni, v, (jbyte *)p, JNI_ABORT); }
+
static void vexcept_syserror(JNIEnv *jni, const char *clsname,
- int err, const char *msg, va_list ap)
+ int err, const char *msg, va_list *ap)
{
jclass cls;
int rc;
cls = (*jni)->FindClass(jni, clsname); assert(cls);
init = (*jni)->GetMethodID(jni, cls, "<init>", "(I[B)V"); assert(init);
- dstr_vputf(&d, msg, &ap);
+ dstr_vputf(&d, msg, ap);
msgstr = wrap_cstring(jni, d.buf); assert(msgstr);
dstr_destroy(&d);
e = (*jni)->NewObject(jni, cls, init, err, msgstr); assert(e);
va_list ap;
va_start(ap, msg);
- vexcept_syserror(jni, clsname, err, msg, ap);
+ vexcept_syserror(jni, clsname, err, msg, &ap);
va_end(ap);
}
+static int set_nonblocking(JNIEnv *jni, int fd, int nb)
+{
+ int f0 = fcntl(fd, F_GETFL), f1;
+ if (f0 < 0) goto err;
+ if (nb) f1 = f0 | O_NONBLOCK;
+ else f1 = f0&~O_NONBLOCK;
+ if (fcntl(fd, F_SETFL, f1)) goto err;
+ return (f0 & O_NONBLOCK);
+err:
+ except_syserror(jni, SYSERR, errno,
+ "failed to set descriptor nonblocking");
+ return (-1);
+}
+
+static int set_closeonexec(JNIEnv *jni, int fd)
+{
+ int f = fcntl(fd, F_GETFD);
+ if (f < 0 || fcntl(fd, F_SETFD, f | FD_CLOEXEC)) {
+ except_syserror(jni, SYSERR, errno,
+ "failed to set descriptor close-on-exec");
+ return (-1);
+ }
+ return (0);
+}
+
/*----- Wrapping native types ---------------------------------------------*/
/* There's no way defined in the JNI to stash a C pointer in a Java object.
ECANCELED ENOKEY EKEYEXPIRED EKEYREVOKED EKEYREJECTED
EOWNERDEAD ENOTRECOVERABLE ERFKILL EHWPOISON)))
(save-excursion
- (goto-char (point-min))
- (search-forward (concat "***" "BEGIN errtab" "***"))
- (beginning-of-line 2)
- (delete-region (point)
- (progn
- (search-forward "***END***")
- (beginning-of-line)
- (point)))
- (dolist (err errors)
- (insert (format "#ifdef %s\n { \"%s\", %s },\n#endif\n"
- err err err)))))
+ (goto-char (point-min))
+ (search-forward (concat "***" "BEGIN errtab" "***"))
+ (beginning-of-line 2)
+ (delete-region (point)
+ (progn
+ (search-forward "***END***")
+ (beginning-of-line)
+ (point)))
+ (dolist (err errors)
+ (insert (format "#ifdef %s\n { \"%s\", %s },\n#endif\n"
+ err err err)))))
*/
/***BEGIN errtab***/
#ifdef EPERM
jobject e;
eltcls =
- (*jni)->FindClass(jni, ERRENTRY);
+ (*jni)->FindClass(jni, ERRENTCLS);
assert(eltcls);
v = (*jni)->NewObjectArray(jni, N(errtab), eltcls, 0); if (!v) return (0);
init = (*jni)->GetMethodID(jni, eltcls, "<init>",
- "(Ljava/lang/String;I)V");
+ "(L"STRCLS";I)V");
assert(init);
for (i = 0; i < N(errtab); i++) {
JNIEXPORT jobject JNIFUNC(strerror)(JNIEnv *jni, jobject cls, jint err)
{ return (wrap_cstring(jni, strerror(err))); }
+/*----- Messing with file descriptors -------------------------------------*/
+
+static void fdguts(JNIEnv *jni, jclass *cls, jfieldID *fid)
+{
+ *cls = (*jni)->FindClass(jni, FDCLS); assert(cls);
+ *fid = (*jni)->GetFieldID(jni, *cls, "fd", "I"); // OpenJDK
+ if (!*fid) *fid = (*jni)->GetFieldID(jni, *cls, "descriptor", "I"); // Android
+ assert(*fid);
+}
+
+static int fdint(JNIEnv *jni, jobject jfd)
+{
+ jclass cls;
+ jfieldID fid;
+
+ fdguts(jni, &cls, &fid);
+ return ((*jni)->GetIntField(jni, jfd, fid));
+}
+
+static jobject newfd(JNIEnv *jni, int fd)
+{
+ jobject jfd;
+ jclass cls;
+ jmethodID init;
+ jfieldID fid;
+
+ fdguts(jni, &cls, &fid);
+ init = (*jni)->GetMethodID(jni, cls, "<init>", "()V"); assert(init);
+ jfd = (*jni)->NewObject(jni, cls, init);
+ (*jni)->SetIntField(jni, jfd, fid, fd);
+ return (jfd);
+}
+
+JNIEXPORT jint JNIFUNC(fdint)(JNIEnv *jni, jobject cls, jobject jfd)
+ { return (fdint(jni, jfd)); }
+
+JNIEXPORT jobject JNIFUNC(newfd)(JNIEnv *jni, jobject cls, jint fd)
+ { return (newfd(jni, fd)); }
+
+JNIEXPORT jboolean JNIFUNC(isatty)(JNIEnv *jni, jobject cls, jobject jfd)
+ { return (isatty(fdint(jni, jfd))); }
+
/*----- Low-level file operations -----------------------------------------*/
/* Java has these already, as methods on `java.io.File' objects. Alas, these
* methods are useless at reporting errors: they tend to return a `boolean'
- * success/ fail indicator, and throw away any more detailed information.
+ * success/fail indicator, and throw away any more detailed information.
* There's better functionality in `java.nio.file.Files', but that only turns
* up in Android API 26 (in 7.0 Nougat). There's `android.system.Os', which
* has a bunch of POSIX-shaped functions -- but they're only in Android API
put_cstring(jni, path, pathstr);
}
+JNIEXPORT void JNIFUNC(chmod)(JNIEnv *jni, jobject cls,
+ jobject path, jint mode)
+{
+ const char *pathstr = 0;
+
+ pathstr = get_cstring(jni, path); if (!pathstr) goto end;
+ if (chmod(pathstr, mode)) {
+ except_syserror(jni, SYSERR, errno,
+ "failed st permissions on `%s'", pathstr);
+ goto end;
+ }
+end:
+ put_cstring(jni, path, pathstr);
+}
+
JNIEXPORT void JNIFUNC(mkfile)(JNIEnv *jni, jobject cls,
jobject path, jint mode)
{
put_cstring(jni, to, tostr);
}
-#define LKF_EXCL 1u
-#define LKF_WAIT 2u
+#define LKF_EXCL 0x1000u
+#define LKF_WAIT 0x2000u
struct lockf {
struct native_base _base;
int fd;
pathstr = get_cstring(jni, path); if (!pathstr) goto end;
again:
- fd = open(pathstr, O_RDWR | O_CREAT); if (fd < 0) goto err;
+ fd = open(pathstr, O_RDWR | O_CREAT, flags&07777); if (fd < 0) goto err;
if (fstat(fd, &st0)) goto err;
f = fcntl(fd, F_GETFD); if (f < 0) goto err;
if (fcntl(fd, F_SETFD, f | FD_CLOEXEC)) goto err;
jclass cls;
jmethodID init;
jint modehack;
-
+
modehack = st->st_mode&07777;
if (S_ISFIFO(st->st_mode)) modehack |= 0010000;
else if (S_ISCHR(st->st_mode)) modehack |= 0020000;
else if (S_ISLNK(st->st_mode)) modehack |= 0120000;
else if (S_ISSOCK(st->st_mode)) modehack |= 0140000;
- cls = (*jni)->FindClass(jni, STAT); assert(cls);
+ cls = (*jni)->FindClass(jni, STATCLS); assert(cls);
init = (*jni)->GetMethodID(jni, cls, "<init>", "(IIJIIIIIIJIJJJJ)V");
assert(init);
return ((*jni)->NewObject(jni, cls, init,
put_cstring(jni, path, pathstr);
}
-/*----- A server connection, using a Unix-domain socket -------------------*/
+/*----- Triggers ----------------------------------------------------------*/
-struct conn {
+/* A trigger is a gadget for waking up a thread which is blocking on I/O,
+ * and it's used to implement interruptability.
+ *
+ * Really, a trigger is a pipe. A `blocking' I/O operation secretly uses
+ * select(2) to block on the descriptor of interest /and/ the read side of
+ * the trigger pipe. To wake up a thread that's blocked, we just write a
+ * byte (nobody cares /which/ byte) to the write end.
+ */
+
+struct trigger {
struct native_base _base;
- int fd;
- unsigned f;
-#define CF_CLOSERD 1u
-#define CF_CLOSEWR 2u
-#define CF_CLOSEMASK (CF_CLOSERD | CF_CLOSEWR)
+ int rfd, wfd;
};
-static const struct native_type conn_type =
- { "conn", sizeof(struct conn), 0xed030167 };
+static const struct native_type trigger_type =
+ { "trigger", sizeof(struct trigger), 0x65ffd8b4 };
-JNIEXPORT wrapper JNICALL JNIFUNC(connect)(JNIEnv *jni, jobject cls,
- jobject path)
+JNIEXPORT wrapper JNICALL JNIFUNC(make_1trigger)(JNIEnv *jni, jobject cls)
{
- struct conn conn;
- struct sockaddr_un sun;
- const char *pathstr = 0;
- jobject ret = 0;
+ struct trigger trig;
+ int fd[2];
+ int i;
+ wrapper ret = 0;
+
+ fd[0] = fd[1] = -1;
+ if (pipe(fd)) {
+ except_syserror(jni, SYSERR, errno, "failed to create pipe");
+ goto end;
+ }
+ for (i = 0; i < 2; i++) {
+ if (set_nonblocking(jni, fd[i], 1) < 0 || set_closeonexec(jni, fd[i]))
+ goto end;
+ }
+
+ INIT_NATIVE(trigger, &trig);
+ trig.rfd = fd[0]; fd[0] = -1;
+ trig.wfd = fd[1]; fd[1] = -1;
+ ret = wrap(jni, &trigger_type, &trig);
+
+end:
+ for (i = 0; i < 2; i++)
+ if (fd[i] != -1) close(fd[i]);
+ return (ret);
+}
+
+JNIEXPORT void JNICALL JNIFUNC(destroy_1trigger)(JNIEnv *jni, jobject cls,
+ wrapper wtrig)
+{
+ struct trigger trig;
+
+ if (unwrap(jni, &trig, &trigger_type, wtrig)) return;
+ if (trig.rfd != -1) { close(trig.rfd); trig.rfd = -1; }
+ if (trig.wfd != -1) { close(trig.wfd); trig.wfd = -1; }
+ update_wrapper(jni, &trigger_type, wtrig, &trig);
+}
+
+JNIEXPORT void JNICALL JNIFUNC(reset_1trigger)(JNIEnv *jni, jobject cls,
+ wrapper wtrig)
+{
+ struct trigger trig;
+ char buf[64];
+ ssize_t n;
+
+ if (unwrap(jni, &trig, &trigger_type, wtrig)) return;
+ for (;;) {
+ n = read(trig.rfd, buf, sizeof(buf));
+ if (n > 0) continue;
+ assert(n < 0);
+ if (errno == EAGAIN || errno == EWOULDBLOCK) break;
+ else {
+ except_syserror(jni, SYSERR, errno, "failed to reset trigger");
+ break;
+ }
+ }
+}
+
+JNIEXPORT void JNICALL JNIFUNC(trigger)(JNIEnv *jni, jobject cls,
+ wrapper wtrig)
+{
+ struct trigger trig;
+ ssize_t n;
+ char c = 0;
+
+ if (unwrap(jni, &trig, &trigger_type, wtrig)) return;
+ n = write(trig.wfd, &c, 1);
+ if (n < 0 && errno != EAGAIN && errno != EWOULDBLOCK)
+ except_syserror(jni, SYSERR, errno, "failed to pull trigger");
+}
+
+/*----- A tunnel supplied by Java -----------------------------------------*/
+
+struct tunnel {
+ const tunnel_ops *ops;
+ sel_file f;
+ struct peer *p;
+};
+
+static const struct tunnel_ops tun_java;
+
+static int t_init(void) { return (0); }
+
+static void t_read(int fd, unsigned mode, void *v)
+{
+ tunnel *t = v;
+ ssize_t n;
+ buf b;
+
+ n = read(fd, buf_i, sizeof(buf_i));
+ if (n < 0) {
+ a_warn("TUN", "%s", p_ifname(t->p), "java",
+ "read-error", "?ERRNO", A_END);
+ return;
+ }
+ IF_TRACING(T_TUNNEL, {
+ trace(T_TUNNEL, "tun-java: packet arrived");
+ trace_block(T_PACKET, "tunnel: packet contents", buf_i, n);
+ })
+ buf_init(&b, buf_i, n);
+ p_tun(t->p, &b);
+}
+
+static tunnel *t_create(peer *p, int fd, char **ifn)
+{
+ JNIEnv *jni = jni_tripe;
+ tunnel *t = 0;
+ const char *name = p_name(p);
+ jbyteArray jname;
+ size_t n = strlen(p_name(p));
+ jclass cls, metacls;
+ jstring jclsname, jexcmsg;
+ const char *clsname, *excmsg;
+ jmethodID mid;
+ jthrowable exc;
+
+ assert(jni);
+
+ jname = wrap_cstring(jni, name);
+ cls = (*jni)->FindClass(jni, SYSCLS); assert(cls);
+ mid = (*jni)->GetStaticMethodID(jni, cls, "getTunnelFd", "([B)I");
+ assert(mid);
+ fd = (*jni)->CallStaticIntMethod(jni, cls, mid, jname);
+
+ exc = (*jni)->ExceptionOccurred(jni);
+ if (exc) {
+ cls = (*jni)->GetObjectClass(jni, exc);
+ metacls = (*jni)->GetObjectClass(jni, cls);
+ mid = (*jni)->GetMethodID(jni, metacls,
+ "getName", "()L"STRCLS";");
+ assert(mid);
+ jclsname = (*jni)->CallObjectMethod(jni, cls, mid);
+ clsname = (*jni)->GetStringUTFChars(jni, jclsname, 0);
+ mid = (*jni)->GetMethodID(jni, cls,
+ "getMessage", "()L"STRCLS";");
+ jexcmsg = (*jni)->CallObjectMethod(jni, exc, mid);
+ excmsg = (*jni)->GetStringUTFChars(jni, jexcmsg, 0);
+ a_warn("TUN", "-", "java", "get-tunnel-fd-failed",
+ "%s", clsname, "%s", excmsg, A_END);
+ (*jni)->ReleaseStringUTFChars(jni, jclsname, clsname);
+ (*jni)->ReleaseStringUTFChars(jni, jexcmsg, excmsg);
+ (*jni)->ExceptionClear(jni);
+ goto end;
+ }
+
+ t = CREATE(tunnel);
+ t->ops = &tun_java;
+ t->p = p;
+ sel_initfile(&sel, &t->f, fd, SEL_READ, t_read, t);
+
+ if (!*ifn) {
+ *ifn = xmalloc(n + 5);
+ sprintf(*ifn, "vpn-%s", name);
+ }
+
+end:
+ return (t);
+}
+
+static void t_inject(tunnel *t, buf *b)
+{
+ IF_TRACING(T_TUNNEL, {
+ trace(T_TUNNEL, "tun-java: inject decrypted packet");
+ trace_block(T_PACKET, "tunnel: packet contents", BBASE(b), BLEN(b));
+ })
+ DISCARD(write(t->f.fd, BBASE(b), BLEN(b)));
+}
+
+static void t_destroy(tunnel *t)
+ { sel_rmfile(&t->f); close(t->f.fd); DESTROY(t); }
+
+static const struct tunnel_ops tun_java = {
+ "java", 0,
+ /* init */ t_init,
+ /* create */ t_create,
+ /* setifname */ 0,
+ /* inject */ t_inject,
+ /* destroy */ t_destroy
+};
+
+
+JNIEXPORT jint JNICALL JNIFUNC(open_1tun)(JNIEnv *jni, jobject cls)
+{
+ int ret = -1;
int fd = -1;
+ struct ifreq iff;
- pathstr = get_cstring(jni, path); if (!pathstr) goto end;
- if (strlen(pathstr) >= sizeof(sun.sun_path)) {
- except(jni, ARGERR,
- "Unix-domain socket path `%s' too long", pathstr);
+ if ((fd = open("/dev/net/tun", O_RDWR)) < 0) {
+ except_syserror(jni, SYSERR, errno, "failed to open tunnel device");
goto end;
}
- INIT_NATIVE(conn, &conn);
- fd = socket(SOCK_STREAM, PF_UNIX, 0); if (fd < 0) goto err;
+ if (set_nonblocking(jni, fd, 1) || set_closeonexec(jni, fd)) goto end;
- sun.sun_family = AF_UNIX;
- strcpy(sun.sun_path, (char *)pathstr);
- if (connect(fd, (struct sockaddr *)&sun, sizeof(sun))) goto err;
+ memset(&iff, 0, sizeof(iff));
+ iff.ifr_name[0] = 0;
+ iff.ifr_flags = IFF_TUN | IFF_NO_PI;
+ if (ioctl(fd, TUNSETIFF, &iff) < 0) {
+ except_syserror(jni, SYSERR, errno, "failed to configure tunnel device");
+ goto end;
+ }
- conn.fd = fd; fd = -1;
- conn.f = 0;
- ret = wrap(jni, &conn_type, &conn);
- goto end;
+ ret = fd; fd = -1;
-err:
- except_syserror(jni, SYSERR, errno,
- "failed to connect to Unix-domain socket `%s'", pathstr);
end:
- if (fd == -1) close(fd);
- put_cstring(jni, path, pathstr);
+ if (fd != -1) close(fd);
return (ret);
}
+/*----- 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);
+}
+
static int check_buffer_bounds(JNIEnv *jni, const char *what,
jbyteArray buf, jint start, jint len)
{
}
JNIEXPORT void JNICALL JNIFUNC(send)(JNIEnv *jni, jobject cls,
- wrapper wconn, jbyteArray buf,
- jint start, jint len)
+ jbyteArray buf,
+ jint start, jint len,
+ wrapper wtrig)
{
- struct conn conn;
+ struct trigger trig;
+ int rc, maxfd;
ssize_t n;
+ fd_set rfds, wfds;
jbyte *p = 0;
- if (unwrap(jni, &conn, &conn_type, wconn)) goto end;
+ if (ensure_state(jni, RUNNING)) goto end;
+
+ if (unwrap(jni, &trig, &trigger_type, wtrig)) goto end;
if (check_buffer_bounds(jni, "send", buf, start, len)) goto end;
p = (*jni)->GetByteArrayElements(jni, buf, 0);
if (!p) goto end;
+ maxfd = trig.rfd;
+ if (maxfd < clientsk) maxfd = clientsk;
while (len) {
- n = send(conn.fd, p + start, len, 0);
- if (n < 0) {
- except_syserror(jni, SYSERR,
- errno, "failed to send on connection");
- goto end;
+ FD_ZERO(&rfds); FD_SET(trig.rfd, &rfds);
+ FD_ZERO(&wfds); FD_SET(clientsk, &wfds);
+ rc = select(maxfd + 1, &rfds, &wfds, 0, 0); if (rc < 0) goto err;
+ if (FD_ISSET(trig.rfd, &rfds)) break;
+ if (FD_ISSET(clientsk, &wfds)) {
+ n = send(clientsk, p + start, len, 0);
+ if (n >= 0) { start += n; len -= n; }
+ else if (errno != EAGAIN && errno != EWOULDBLOCK) goto err;
}
- start += n; len -= n;
}
+ goto end;
+err:
+ except_syserror(jni, SYSERR, errno, "failed to send on connection");
end:
if (p) (*jni)->ReleaseByteArrayElements(jni, buf, p, JNI_ABORT);
return;
}
JNIEXPORT jint JNICALL JNIFUNC(recv)(JNIEnv *jni, jobject cls,
- wrapper wconn, jbyteArray buf,
- jint start, jint len)
+ jbyteArray buf,
+ jint start, jint len,
+ wrapper wtrig)
{
- struct conn conn;
+ struct trigger trig;
+ int maxfd;
+ fd_set rfds;
jbyte *p = 0;
jint rc = -1;
- if (unwrap(jni, &conn, &conn_type, wconn)) goto end;
+ lock_tripe(jni);
+ if (clientsk == -1) {
+ except(jni, STERR, "client connection not established");
+ unlock_tripe(jni);
+ goto end;
+ }
+ unlock_tripe(jni);
+
+ if (unwrap(jni, &trig, &trigger_type, wtrig)) goto end;
if (check_buffer_bounds(jni, "send", buf, start, len)) goto end;
p = (*jni)->GetByteArrayElements(jni, buf, 0);
if (!p) goto end;
- rc = recv(conn.fd, p + start, len, 0);
- if (rc < 0) {
- except_syserror(jni, SYSERR,
- errno, "failed to read from connection");
- goto end;
+ maxfd = trig.rfd;
+ if (maxfd < clientsk) maxfd = clientsk;
+ for (;;) {
+ FD_ZERO(&rfds); FD_SET(trig.rfd, &rfds); FD_SET(clientsk, &rfds);
+ rc = select(maxfd + 1, &rfds, 0, 0, 0); if (rc < 0) goto err;
+ if (FD_ISSET(trig.rfd, &rfds)) {
+ break;
+ }
+ if (FD_ISSET(clientsk, &rfds)) {
+ rc = recv(clientsk, p + start, len, 0);
+ if (rc >= 0) break;
+ else if (errno != EAGAIN && errno != EWOULDBLOCK) goto err;
+ }
}
if (!rc) rc = -1;
+ goto end;
+err:
+ except_syserror(jni, SYSERR, errno, "failed to read from connection");
end:
if (p) (*jni)->ReleaseByteArrayElements(jni, buf, p, 0);
return (rc);
}
-JNIEXPORT void JNICALL JNIFUNC(close)(JNIEnv *jni, jobject cls,
- wrapper wconn, jint how)
-{
- struct conn conn;
- int rc;
-
- if (unwrap(jni, &conn, &conn_type, wconn)) goto end;
- if (conn.fd == -1) goto end;
-
- how &= CF_CLOSEMASK&~conn.f;
- conn.f |= how;
- if ((conn.f&CF_CLOSEMASK) == CF_CLOSEMASK) {
- close(conn.fd);
- conn.fd = -1;
- } else {
- if (how&CF_CLOSERD) shutdown(conn.fd, SHUT_RD);
- if (how&CF_CLOSEWR) shutdown(conn.fd, SHUT_WR);
- }
- rc = update_wrapper(jni, &conn_type, wconn, &conn); assert(!rc);
-
-end:
- return;
-}
-
/*----- That's all, folks -------------------------------------------------*/