3 * Native-code portions of the project
5 * (c) 2018 Straylight/Edgeware
8 /*----- Licensing notice --------------------------------------------------*
10 * This file is part of the Trivial IP Encryption (TrIPE) Android app.
12 * TrIPE is free software: you can redistribute it and/or modify it under
13 * the terms of the GNU General Public License as published by the Free
14 * Software Foundation; either version 3 of the License, or (at your
15 * option) any later version.
17 * TrIPE is distributed in the hope that it will be useful, but WITHOUT
18 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22 * You should have received a copy of the GNU General Public License
23 * along with TrIPE. If not, see <https://www.gnu.org/licenses/>.
26 /*----- Header files ------------------------------------------------------*/
28 #define _FILE_OFFSET_BITS 64
43 #include <sys/ioctl.h>
44 #include <sys/select.h>
45 #include <sys/socket.h>
47 #include <sys/sysmacros.h>
48 #include <sys/types.h>
53 //#include <linux/if.h>
54 #include <linux/if_tun.h>
56 #include <mLib/align.h>
57 #include <mLib/bits.h>
58 #include <mLib/dstr.h>
59 #include <mLib/macros.h>
61 #include <catacomb/ghash.h>
68 /*----- Magic class names and similar -------------------------------------*/
70 /* The name decoration is horrific. Hide it. */
71 #define JNIFUNC(f) Java_uk_org_distorted_tripe_sys_package_00024_##f
73 /* The little class for bundling up error codes. */
74 #define ERRENTCLS "uk/org/distorted/tripe/sys/package$ErrorEntry"
76 /* The `sys' package class. */
77 #define SYSCLS "uk/org/distorted/tripe/sys/package"
79 /* The server lock class. */
80 #define LOCKCLS "uk/org/distorted/tripe/sys/package$ServerLock"
82 /* The `stat' class. */
83 #define STATCLS "uk/org/distorted/tripe/sys/package$FileInfo"
85 /* Standard Java classes. */
86 #define FDCLS "java/io/FileDescriptor"
87 #define STRCLS "java/lang/String"
88 #define RANDCLS "java/security/SecureRandom"
90 /* Exception class names. */
91 #define NULLERR "java/lang/NullPointerException"
92 #define TYPEERR "uk/org/distorted/tripe/sys/package$NativeObjectTypeException"
93 #define SYSERR "uk/org/distorted/tripe/sys/package$SystemError"
94 #define NAMEERR "uk/org/distorted/tripe/sys/package$NameResolutionException"
95 #define INITERR "uk/org/distorted/tripe/sys/package$InitializationException"
96 #define ARGERR "java/lang/IllegalArgumentException"
97 #define STERR "java/lang/IllegalStateException"
98 #define BOUNDSERR "java/lang/IndexOutOfBoundsException"
100 /*----- Essential state ---------------------------------------------------*/
102 static JNIEnv *jni_tripe = 0;
104 /*----- Miscellaneous utilities -------------------------------------------*/
106 static void vexcept(JNIEnv *jni, const char *clsname,
107 const char *msg, va_list *ap)
113 cls = (*jni)->FindClass(jni, clsname); assert(cls);
115 rc = (*jni)->ThrowNew(jni, cls, 0);
117 dstr_vputf(&d, msg, ap);
118 rc = (*jni)->ThrowNew(jni, cls, d.buf);
125 static void except(JNIEnv *jni, const char *clsname, const char *msg, ...)
130 vexcept(jni, clsname, msg, &ap);
135 static void dump_bytes(const void *p, size_t n, size_t o)
137 const unsigned char *q = p;
142 fprintf(stderr, ";; %08zx\n", o);
143 for (i = 0; i < 8; i++)
144 if (i < n) fprintf(stderr, "%02x ", q[i]);
145 else fprintf(stderr, "** ");
146 fprintf(stderr, ": ");
147 for (i = 0; i < 8; i++)
148 fputc(i >= n ? '*' : isprint(q[i]) ? q[i] : '.', stderr);
155 static void dump_byte_array(JNIEnv *jni, const char *what, jbyteArray v)
160 fprintf(stderr, ";; %s\n", what);
161 if (!v) { fprintf(stderr, ";; <null>\n"); return; }
162 n = (*jni)->GetArrayLength(jni, v);
163 p = (*jni)->GetByteArrayElements(jni, v, 0);
165 (*jni)->ReleaseByteArrayElements(jni, v, p, JNI_ABORT);
169 static jbyteArray wrap_cstring(JNIEnv *jni, const char *p)
177 v = (*jni)->NewByteArray(jni, n); if (!v) return (0);
178 q = (*jni)->GetByteArrayElements(jni, v, 0); if (!q) return (0);
180 (*jni)->ReleaseByteArrayElements(jni, v, q, 0);
184 static const char *get_cstring(JNIEnv *jni, jbyteArray v)
186 if (!v) { except(jni, NULLERR, 0); return (0); }
187 return ((const char *)(*jni)->GetByteArrayElements(jni, v, 0));
190 static void put_cstring(JNIEnv *jni, jbyteArray v, const char *p)
191 { if (p) (*jni)->ReleaseByteArrayElements(jni, v, (jbyte *)p, JNI_ABORT); }
193 static void vexcept_syserror(JNIEnv *jni, const char *clsname,
194 int err, const char *msg, va_list *ap)
203 cls = (*jni)->FindClass(jni, clsname); assert(cls);
204 init = (*jni)->GetMethodID(jni, cls, "<init>", "(I[B)V"); assert(init);
205 dstr_vputf(&d, msg, ap);
206 msgstr = wrap_cstring(jni, d.buf); assert(msgstr);
208 e = (*jni)->NewObject(jni, cls, init, err, msgstr); assert(e);
209 rc = (*jni)->Throw(jni, e); assert(!rc);
212 static void except_syserror(JNIEnv *jni, const char *clsname,
213 int err, const char *msg, ...)
218 vexcept_syserror(jni, clsname, err, msg, &ap);
222 static int set_nonblocking(JNIEnv *jni, int fd, int nb)
224 int f0 = fcntl(fd, F_GETFL), f1;
225 if (f0 < 0) goto err;
226 if (nb) f1 = f0 | O_NONBLOCK;
227 else f1 = f0&~O_NONBLOCK;
228 if (fcntl(fd, F_SETFL, f1)) goto err;
229 return (f0 & O_NONBLOCK);
231 except_syserror(jni, SYSERR, errno,
232 "failed to set descriptor nonblocking");
236 static int set_closeonexec(JNIEnv *jni, int fd)
238 int f = fcntl(fd, F_GETFD);
239 if (f < 0 || fcntl(fd, F_SETFD, f | FD_CLOEXEC)) {
240 except_syserror(jni, SYSERR, errno,
241 "failed to set descriptor close-on-exec");
247 /*----- Wrapping native types ---------------------------------------------*/
249 /* There's no way defined in the JNI to stash a C pointer in a Java object.
250 * It seems that the usual approach is to cast to `jlong', but this is
251 * clearly unsatisfactory. Instead, we store structures as Java byte arrays,
252 * with a 32-bit tag on the front.
261 typedef jbyteArray wrapper;
267 static int unwrap(JNIEnv *jni, void *p,
268 const struct native_type *ty, wrapper w)
272 struct native_base *b = p;
275 if (!w) { except(jni, NULLERR, 0); return (-1); }
276 cls = (*jni)->FindClass(jni, "[B"); assert(cls);
277 if (!(*jni)->IsInstanceOf(jni, w, cls)) {
279 "corrupted native object wrapper: expected a byte array");
282 n = (*jni)->GetArrayLength(jni, w);
285 "corrupted native object wrapper: wrong size for `%s'",
289 q = (*jni)->GetByteArrayElements(jni, w, 0); if (!q) return (-1);
290 memcpy(b, q, ty->sz);
291 (*jni)->ReleaseByteArrayElements(jni, w, q, JNI_ABORT);
292 if (b->tag != ty->tag) {
294 "corrupted native object wrapper: expected tag for `%s'",
301 static int update_wrapper(JNIEnv *jni, const struct native_type *ty,
302 wrapper w, const void *p)
306 q = (*jni)->GetByteArrayElements(jni, w, 0); if (!q) return (-1);
307 memcpy(q, p, ty->sz);
308 (*jni)->ReleaseByteArrayElements(jni, w, q, 0);
312 static wrapper wrap(JNIEnv *jni, const struct native_type *ty, const void *p)
316 w = (*jni)->NewByteArray(jni, ty->sz); if (!w) return (0);
317 if (update_wrapper(jni, ty, w, p)) return (0);
321 #define INIT_NATIVE(type, p) do (p)->_base.tag = type##_type.tag; while (0)
323 /*----- Crypto information ------------------------------------------------*/
325 JNIEXPORT jint JNICALL JNIFUNC(hashsz)(JNIEnv *jni, jobject cls,
332 hname = (*jni)->GetStringUTFChars(jni, hnamestr, 0);
333 if (!hname) goto end;
334 hc = ghash_byname(hname); if (!hc) goto end;
338 if (hname) (*jni)->ReleaseStringUTFChars(jni, hnamestr, hname);
342 /*----- System errors -----------------------------------------------------*/
344 static const struct errtab { const char *tag; int err; } errtab[] = {
346 ;;; The errno name table is very boring to type. To make life less
347 ;;; awful, put the errno names in this list and evaluate the code to
348 ;;; get Emacs to regenerate it.
350 (let ((errors '(EPERM ENOENT ESRCH EINTR EIO ENXIO E2BIG ENOEXEC EBADF
351 ECHILD EAGAIN ENOMEM EACCES EFAULT ENOTBLK EBUSY EEXIST
352 EXDEV ENODEV ENOTDIR EISDIR EINVAL ENFILE EMFILE ENOTTY
353 ETXTBSY EFBIG ENOSPC ESPIPE EROFS EMLINK EPIPE EDOM
356 EDEADLK ENAMETOOLONG ENOLCK ENOSYS ENOTEMPTY ELOOP
357 EWOULDBLOCK ENOMSG EIDRM ECHRNG EL2NSYNC EL3HLT EL3RST
358 ELNRNG EUNATCH ENOCSI EL2HLT EBADE EBADR EXFULL ENOANO
359 EBADRQC EBADSLT EDEADLOCK EBFONT ENOSTR ENODATA ETIME
360 ENOSR ENONET ENOPKG EREMOTE ENOLINK EADV ESRMNT ECOMM
361 EPROTO EMULTIHOP EDOTDOT EBADMSG EOVERFLOW ENOTUNIQ
362 EBADFD EREMCHG ELIBACC ELIBBAD ELIBSCN ELIBMAX ELIBEXEC
363 EILSEQ ERESTART ESTRPIPE EUSERS ENOTSOCK EDESTADDRREQ
364 EMSGSIZE EPROTOTYPE ENOPROTOOPT EPROTONOSUPPORT
365 ESOCKTNOSUPPORT EOPNOTSUPP EPFNOSUPPORT EAFNOSUPPORT
366 EADDRINUSE EADDRNOTAVAIL ENETDOWN ENETUNREACH ENETRESET
367 ECONNABORTED ECONNRESET ENOBUFS EISCONN ENOTCONN
368 ESHUTDOWN ETOOMANYREFS ETIMEDOUT ECONNREFUSED EHOSTDOWN
369 EHOSTUNREACH EALREADY EINPROGRESS ESTALE EUCLEAN ENOTNAM
370 ENAVAIL EISNAM EREMOTEIO EDQUOT ENOMEDIUM EMEDIUMTYPE
371 ECANCELED ENOKEY EKEYEXPIRED EKEYREVOKED EKEYREJECTED
372 EOWNERDEAD ENOTRECOVERABLE ERFKILL EHWPOISON)))
374 (goto-char (point-min))
375 (search-forward (concat "***" "BEGIN errtab" "***"))
376 (beginning-of-line 2)
377 (delete-region (point)
379 (search-forward "***END***")
383 (insert (format "#ifdef %s\n { \"%s\", %s },\n#endif\n"
391 { "ENOENT", ENOENT },
409 { "ENOEXEC", ENOEXEC },
415 { "ECHILD", ECHILD },
418 { "EAGAIN", EAGAIN },
421 { "ENOMEM", ENOMEM },
424 { "EACCES", EACCES },
427 { "EFAULT", EFAULT },
430 { "ENOTBLK", ENOTBLK },
436 { "EEXIST", EEXIST },
442 { "ENODEV", ENODEV },
445 { "ENOTDIR", ENOTDIR },
448 { "EISDIR", EISDIR },
451 { "EINVAL", EINVAL },
454 { "ENFILE", ENFILE },
457 { "EMFILE", EMFILE },
460 { "ENOTTY", ENOTTY },
463 { "ETXTBSY", ETXTBSY },
469 { "ENOSPC", ENOSPC },
472 { "ESPIPE", ESPIPE },
478 { "EMLINK", EMLINK },
487 { "ERANGE", ERANGE },
490 { "EDEADLK", EDEADLK },
493 { "ENAMETOOLONG", ENAMETOOLONG },
496 { "ENOLCK", ENOLCK },
499 { "ENOSYS", ENOSYS },
502 { "ENOTEMPTY", ENOTEMPTY },
508 { "EWOULDBLOCK", EWOULDBLOCK },
511 { "ENOMSG", ENOMSG },
517 { "ECHRNG", ECHRNG },
520 { "EL2NSYNC", EL2NSYNC },
523 { "EL3HLT", EL3HLT },
526 { "EL3RST", EL3RST },
529 { "ELNRNG", ELNRNG },
532 { "EUNATCH", EUNATCH },
535 { "ENOCSI", ENOCSI },
538 { "EL2HLT", EL2HLT },
547 { "EXFULL", EXFULL },
550 { "ENOANO", ENOANO },
553 { "EBADRQC", EBADRQC },
556 { "EBADSLT", EBADSLT },
559 { "EDEADLOCK", EDEADLOCK },
562 { "EBFONT", EBFONT },
565 { "ENOSTR", ENOSTR },
568 { "ENODATA", ENODATA },
577 { "ENONET", ENONET },
580 { "ENOPKG", ENOPKG },
583 { "EREMOTE", EREMOTE },
586 { "ENOLINK", ENOLINK },
592 { "ESRMNT", ESRMNT },
598 { "EPROTO", EPROTO },
601 { "EMULTIHOP", EMULTIHOP },
604 { "EDOTDOT", EDOTDOT },
607 { "EBADMSG", EBADMSG },
610 { "EOVERFLOW", EOVERFLOW },
613 { "ENOTUNIQ", ENOTUNIQ },
616 { "EBADFD", EBADFD },
619 { "EREMCHG", EREMCHG },
622 { "ELIBACC", ELIBACC },
625 { "ELIBBAD", ELIBBAD },
628 { "ELIBSCN", ELIBSCN },
631 { "ELIBMAX", ELIBMAX },
634 { "ELIBEXEC", ELIBEXEC },
637 { "EILSEQ", EILSEQ },
640 { "ERESTART", ERESTART },
643 { "ESTRPIPE", ESTRPIPE },
646 { "EUSERS", EUSERS },
649 { "ENOTSOCK", ENOTSOCK },
652 { "EDESTADDRREQ", EDESTADDRREQ },
655 { "EMSGSIZE", EMSGSIZE },
658 { "EPROTOTYPE", EPROTOTYPE },
661 { "ENOPROTOOPT", ENOPROTOOPT },
663 #ifdef EPROTONOSUPPORT
664 { "EPROTONOSUPPORT", EPROTONOSUPPORT },
666 #ifdef ESOCKTNOSUPPORT
667 { "ESOCKTNOSUPPORT", ESOCKTNOSUPPORT },
670 { "EOPNOTSUPP", EOPNOTSUPP },
673 { "EPFNOSUPPORT", EPFNOSUPPORT },
676 { "EAFNOSUPPORT", EAFNOSUPPORT },
679 { "EADDRINUSE", EADDRINUSE },
682 { "EADDRNOTAVAIL", EADDRNOTAVAIL },
685 { "ENETDOWN", ENETDOWN },
688 { "ENETUNREACH", ENETUNREACH },
691 { "ENETRESET", ENETRESET },
694 { "ECONNABORTED", ECONNABORTED },
697 { "ECONNRESET", ECONNRESET },
700 { "ENOBUFS", ENOBUFS },
703 { "EISCONN", EISCONN },
706 { "ENOTCONN", ENOTCONN },
709 { "ESHUTDOWN", ESHUTDOWN },
712 { "ETOOMANYREFS", ETOOMANYREFS },
715 { "ETIMEDOUT", ETIMEDOUT },
718 { "ECONNREFUSED", ECONNREFUSED },
721 { "EHOSTDOWN", EHOSTDOWN },
724 { "EHOSTUNREACH", EHOSTUNREACH },
727 { "EALREADY", EALREADY },
730 { "EINPROGRESS", EINPROGRESS },
733 { "ESTALE", ESTALE },
736 { "EUCLEAN", EUCLEAN },
739 { "ENOTNAM", ENOTNAM },
742 { "ENAVAIL", ENAVAIL },
745 { "EISNAM", EISNAM },
748 { "EREMOTEIO", EREMOTEIO },
751 { "EDQUOT", EDQUOT },
754 { "ENOMEDIUM", ENOMEDIUM },
757 { "EMEDIUMTYPE", EMEDIUMTYPE },
760 { "ECANCELED", ECANCELED },
763 { "ENOKEY", ENOKEY },
766 { "EKEYEXPIRED", EKEYEXPIRED },
769 { "EKEYREVOKED", EKEYREVOKED },
772 { "EKEYREJECTED", EKEYREJECTED },
775 { "EOWNERDEAD", EOWNERDEAD },
777 #ifdef ENOTRECOVERABLE
778 { "ENOTRECOVERABLE", ENOTRECOVERABLE },
781 { "ERFKILL", ERFKILL },
784 { "EHWPOISON", EHWPOISON },
789 JNIEXPORT jobject JNIFUNC(errtab)(JNIEnv *jni, jobject cls)
798 (*jni)->FindClass(jni, ERRENTCLS);
800 v = (*jni)->NewObjectArray(jni, N(errtab), eltcls, 0); if (!v) return (0);
801 init = (*jni)->GetMethodID(jni, eltcls, "<init>",
805 for (i = 0; i < N(errtab); i++) {
806 e = (*jni)->NewObject(jni, eltcls, init,
807 (*jni)->NewStringUTF(jni, errtab[i].tag),
809 (*jni)->SetObjectArrayElement(jni, v, i, e);
814 JNIEXPORT jobject JNIFUNC(strerror)(JNIEnv *jni, jobject cls, jint err)
815 { return (wrap_cstring(jni, strerror(err))); }
817 /*----- Messing with file descriptors -------------------------------------*/
819 static void fdguts(JNIEnv *jni, jclass *cls, jfieldID *fid)
821 *cls = (*jni)->FindClass(jni, FDCLS); assert(cls);
822 *fid = (*jni)->GetFieldID(jni, *cls, "fd", "I"); // OpenJDK
823 if (!*fid) *fid = (*jni)->GetFieldID(jni, *cls, "descriptor", "I"); // Android
827 static int fdint(JNIEnv *jni, jobject jfd)
832 fdguts(jni, &cls, &fid);
833 return ((*jni)->GetIntField(jni, jfd, fid));
836 static jobject newfd(JNIEnv *jni, int fd)
843 fdguts(jni, &cls, &fid);
844 init = (*jni)->GetMethodID(jni, cls, "<init>", "()V"); assert(init);
845 jfd = (*jni)->NewObject(jni, cls, init);
846 (*jni)->SetIntField(jni, jfd, fid, fd);
850 JNIEXPORT jint JNIFUNC(fdint)(JNIEnv *jni, jobject cls, jobject jfd)
851 { return (fdint(jni, jfd)); }
853 JNIEXPORT jobject JNIFUNC(newfd)(JNIEnv *jni, jobject cls, jint fd)
854 { return (newfd(jni, fd)); }
856 JNIEXPORT jboolean JNIFUNC(isatty)(JNIEnv *jni, jobject cls, jobject jfd)
857 { return (isatty(fdint(jni, jfd))); }
859 /*----- Low-level file operations -----------------------------------------*/
861 /* Java has these already, as methods on `java.io.File' objects. Alas, these
862 * methods are useless at reporting errors: they tend to return a `boolean'
863 * success/fail indicator, and throw away any more detailed information.
864 * There's better functionality in `java.nio.file.Files', but that only turns
865 * up in Android API 26 (in 7.0 Nougat). There's `android.system.Os', which
866 * has a bunch of POSIX-shaped functions -- but they're only in Android API
867 * 21 (in 5.0 Lollipop), and there's nothing in the support library to help.
869 * So the other option is to implement them ourselves.
872 JNIEXPORT void JNIFUNC(unlink)(JNIEnv *jni, jobject cls, jobject path)
874 const char *pathstr = 0;
876 pathstr = get_cstring(jni, path); if (!pathstr) goto end;
877 if (unlink(pathstr)) {
878 except_syserror(jni, SYSERR, errno,
879 "failed to delete file `%s'", pathstr);
883 put_cstring(jni, path, pathstr);
886 JNIEXPORT void JNIFUNC(rmdir)(JNIEnv *jni, jobject cls, jobject path)
888 const char *pathstr = 0;
890 pathstr = get_cstring(jni, path); if (!pathstr) goto end;
891 if (rmdir(pathstr)) {
892 except_syserror(jni, SYSERR, errno,
893 "failed to delete directory `%s'", pathstr);
897 put_cstring(jni, path, pathstr);
900 JNIEXPORT void JNIFUNC(mkdir)(JNIEnv *jni, jobject cls,
901 jobject path, jint mode)
903 const char *pathstr = 0;
905 pathstr = get_cstring(jni, path); if (!pathstr) goto end;
906 if (mkdir(pathstr, mode)) {
907 except_syserror(jni, SYSERR, errno,
908 "failed to create directory `%s'", pathstr);
912 put_cstring(jni, path, pathstr);
915 JNIEXPORT void JNIFUNC(chmod)(JNIEnv *jni, jobject cls,
916 jobject path, jint mode)
918 const char *pathstr = 0;
920 pathstr = get_cstring(jni, path); if (!pathstr) goto end;
921 if (chmod(pathstr, mode)) {
922 except_syserror(jni, SYSERR, errno,
923 "failed st permissions on `%s'", pathstr);
927 put_cstring(jni, path, pathstr);
930 JNIEXPORT void JNIFUNC(mkfile)(JNIEnv *jni, jobject cls,
931 jobject path, jint mode)
933 const char *pathstr = 0;
936 pathstr = get_cstring(jni, path); if (!pathstr) goto end;
937 fd = open(pathstr, O_WRONLY | O_CREAT | O_EXCL, mode);
939 except_syserror(jni, SYSERR, errno,
940 "failed to create fresh file `%s'", pathstr);
944 if (fd != -1) close(fd);
945 put_cstring(jni, path, pathstr);
948 JNIEXPORT void JNIFUNC(rename)(JNIEnv *jni, jobject cls,
949 jobject from, jobject to)
951 const char *fromstr = 0, *tostr = 0;
953 fromstr = get_cstring(jni, from); if (!fromstr) goto end;
954 tostr = get_cstring(jni, to); if (!tostr) goto end;
955 if (rename(fromstr, tostr)) {
956 except_syserror(jni, SYSERR, errno,
957 "failed to rename `%s' as `%s'", fromstr, tostr);
961 put_cstring(jni, from, fromstr);
962 put_cstring(jni, to, tostr);
965 #define LKF_EXCL 0x1000u
966 #define LKF_WAIT 0x2000u
968 struct native_base _base;
971 static struct native_type lockf_type =
972 { "lock", sizeof(struct lockf), 0xb2648926};
973 JNIEXPORT wrapper JNIFUNC(lock)(JNIEnv *jni, jobject cls,
974 jobject path, jint flags)
976 const char *pathstr = 0;
980 struct stat st0, st1;
984 pathstr = get_cstring(jni, path); if (!pathstr) goto end;
987 fd = open(pathstr, O_RDWR | O_CREAT, flags&07777); if (fd < 0) goto err;
988 if (fstat(fd, &st0)) goto err;
989 f = fcntl(fd, F_GETFD); if (f < 0) goto err;
990 if (fcntl(fd, F_SETFD, f | FD_CLOEXEC)) goto err;
991 l.l_type = (flags&LKF_EXCL) ? F_WRLCK : F_RDLCK;
992 l.l_whence = SEEK_SET;
995 if (fcntl(fd, (flags&LKF_WAIT) ? F_SETLKW : F_SETLK, &l)) goto err;
996 if (stat(pathstr, &st1))
997 { if (errno == ENOENT) goto again; else goto err; }
998 if (st0.st_dev != st1.st_dev || st0.st_ino != st1.st_ino)
999 { close(fd); fd = -1; goto again; }
1001 INIT_NATIVE(lockf, &lk); lk.fd = fd; fd = -1;
1002 r = wrap(jni, &lockf_type, &lk);
1006 except_syserror(jni, SYSERR, errno, "failed to lock file `%s'", pathstr);
1008 if (fd != -1) close(fd);
1009 put_cstring(jni, path, pathstr);
1013 JNIEXPORT void JNIFUNC(unlock)(JNIEnv *jni, jobject cls, wrapper wlk)
1019 if (unwrap(jni, &lk, &lockf_type, wlk)) goto end;
1020 if (lk.fd == -1) goto end;
1022 l.l_whence = SEEK_SET;
1025 if (fcntl(lk.fd, F_SETLK, &l)) goto end;
1026 close(lk.fd); lk.fd = -1;
1027 rc = update_wrapper(jni, &lockf_type, wlk, &lk); assert(!rc);
1031 static jlong xlttimespec(const struct timespec *ts)
1032 { return (1000*(jlong)ts->tv_sec + ts->tv_nsec/1000000); }
1034 static jobject xltstat(JNIEnv *jni, const struct stat *st)
1040 modehack = st->st_mode&07777;
1041 if (S_ISFIFO(st->st_mode)) modehack |= 0010000;
1042 else if (S_ISCHR(st->st_mode)) modehack |= 0020000;
1043 else if (S_ISDIR(st->st_mode)) modehack |= 0040000;
1044 else if (S_ISBLK(st->st_mode)) modehack |= 0060000;
1045 else if (S_ISREG(st->st_mode)) modehack |= 0100000;
1046 else if (S_ISLNK(st->st_mode)) modehack |= 0120000;
1047 else if (S_ISSOCK(st->st_mode)) modehack |= 0140000;
1049 cls = (*jni)->FindClass(jni, STATCLS); assert(cls);
1050 init = (*jni)->GetMethodID(jni, cls, "<init>", "(IIJIIIIIIJIJJJJ)V");
1052 return ((*jni)->NewObject(jni, cls, init,
1053 (jint)major(st->st_dev), (jint)minor(st->st_dev),
1057 (jint)st->st_uid, (jint)st->st_gid,
1058 (jint)major(st->st_rdev), (jint)minor(st->st_rdev),
1060 (jint)st->st_blksize, (jlong)st->st_blocks,
1061 xlttimespec(&st->st_atim),
1062 xlttimespec(&st->st_mtim),
1063 xlttimespec(&st->st_ctim)));
1066 JNIEXPORT jobject JNIFUNC(stat)(JNIEnv *jni, jobject cls, jobject path)
1069 const char *pathstr = 0;
1072 pathstr = get_cstring(jni, path); if (!pathstr) goto end;
1073 if (stat(pathstr, &st)) {
1074 except_syserror(jni, SYSERR, errno,
1075 "failed to read information about `%s'", pathstr);
1078 r = xltstat(jni, &st);
1080 put_cstring(jni, path, pathstr);
1084 JNIEXPORT jobject JNIFUNC(lstat)(JNIEnv *jni, jobject cls, jobject path)
1087 const char *pathstr = 0;
1090 pathstr = get_cstring(jni, path); if (!pathstr) goto end;
1091 if (lstat(pathstr, &st)) {
1092 except_syserror(jni, SYSERR, errno,
1093 "failed to read information about `%s'", pathstr);
1096 r = xltstat(jni, &st);
1098 put_cstring(jni, path, pathstr);
1103 struct native_base _base;
1106 static const struct native_type dir_type =
1107 { "dir", sizeof(struct dir), 0x0f5ca477 };
1109 JNIEXPORT jobject JNIFUNC(opendir)(JNIEnv *jni, jobject cls, jobject path)
1111 const char *pathstr = 0;
1115 pathstr = get_cstring(jni, path); if (!pathstr) goto end;
1116 INIT_NATIVE(dir, &dir);
1117 dir.d = opendir(pathstr);
1119 except_syserror(jni, SYSERR, errno,
1120 "failed to open directory `%s'", pathstr);
1123 r = wrap(jni, &dir_type, &dir);
1125 put_cstring(jni, path, pathstr);
1129 JNIEXPORT jbyteArray JNIFUNC(readdir)(JNIEnv *jni, jobject cls,
1130 jobject path, jobject wdir)
1132 const char *pathstr = 0;
1137 if (unwrap(jni, &dir, &dir_type, wdir)) goto end;
1138 if (!dir.d) { except(jni, ARGERR, "directory has been closed"); goto end; }
1139 errno = 0; d = readdir(dir.d);
1141 pathstr = get_cstring(jni, path); if (!pathstr) goto end;
1142 except_syserror(jni, SYSERR, errno,
1143 "failed to read directory `%s'", pathstr);
1146 if (d) r = wrap_cstring(jni, d->d_name);
1148 put_cstring(jni, path, pathstr);
1152 JNIEXPORT void JNIFUNC(closedir)(JNIEnv *jni, jobject cls,
1153 jobject path, jobject wdir)
1155 const char *pathstr = 0;
1158 if (unwrap(jni, &dir, &dir_type, wdir)) goto end;
1159 if (!dir.d) goto end;
1160 if (closedir(dir.d)) {
1161 pathstr = get_cstring(jni, path); if (!pathstr) goto end;
1162 except_syserror(jni, SYSERR, errno,
1163 "failed to close directory `%s'", pathstr);
1167 if (update_wrapper(jni, &dir_type, wdir, &dir)) goto end;
1169 put_cstring(jni, path, pathstr);
1172 /*----- Triggers ----------------------------------------------------------*/
1174 /* A trigger is a gadget for waking up a thread which is blocking on I/O,
1175 * and it's used to implement interruptability.
1177 * Really, a trigger is a pipe. A `blocking' I/O operation secretly uses
1178 * select(2) to block on the descriptor of interest /and/ the read side of
1179 * the trigger pipe. To wake up a thread that's blocked, we just write a
1180 * byte (nobody cares /which/ byte) to the write end.
1184 struct native_base _base;
1187 static const struct native_type trigger_type =
1188 { "trigger", sizeof(struct trigger), 0x65ffd8b4 };
1190 JNIEXPORT wrapper JNICALL JNIFUNC(make_1trigger)(JNIEnv *jni, jobject cls)
1192 struct trigger trig;
1199 except_syserror(jni, SYSERR, errno, "failed to create pipe");
1202 for (i = 0; i < 2; i++) {
1203 if (set_nonblocking(jni, fd[i], 1) < 0 || set_closeonexec(jni, fd[i]))
1207 INIT_NATIVE(trigger, &trig);
1208 trig.rfd = fd[0]; fd[0] = -1;
1209 trig.wfd = fd[1]; fd[1] = -1;
1210 ret = wrap(jni, &trigger_type, &trig);
1213 for (i = 0; i < 2; i++)
1214 if (fd[i] != -1) close(fd[i]);
1218 JNIEXPORT void JNICALL JNIFUNC(destroy_1trigger)(JNIEnv *jni, jobject cls,
1221 struct trigger trig;
1223 if (unwrap(jni, &trig, &trigger_type, wtrig)) return;
1224 if (trig.rfd != -1) { close(trig.rfd); trig.rfd = -1; }
1225 if (trig.wfd != -1) { close(trig.wfd); trig.wfd = -1; }
1226 update_wrapper(jni, &trigger_type, wtrig, &trig);
1229 JNIEXPORT void JNICALL JNIFUNC(reset_1trigger)(JNIEnv *jni, jobject cls,
1232 struct trigger trig;
1236 if (unwrap(jni, &trig, &trigger_type, wtrig)) return;
1238 n = read(trig.rfd, buf, sizeof(buf));
1239 if (n > 0) continue;
1241 if (errno == EAGAIN || errno == EWOULDBLOCK) break;
1243 except_syserror(jni, SYSERR, errno, "failed to reset trigger");
1249 JNIEXPORT void JNICALL JNIFUNC(trigger)(JNIEnv *jni, jobject cls,
1252 struct trigger trig;
1256 if (unwrap(jni, &trig, &trigger_type, wtrig)) return;
1257 n = write(trig.wfd, &c, 1);
1258 if (n < 0 && errno != EAGAIN && errno != EWOULDBLOCK)
1259 except_syserror(jni, SYSERR, errno, "failed to pull trigger");
1262 /*----- A tunnel supplied by Java -----------------------------------------*/
1265 const tunnel_ops *ops;
1270 static const struct tunnel_ops tun_java;
1272 static int t_init(void) { return (0); }
1274 static void t_read(int fd, unsigned mode, void *v)
1280 n = read(fd, buf_i, sizeof(buf_i));
1282 a_warn("TUN", "%s", p_ifname(t->p), "java",
1283 "read-error", "?ERRNO", A_END);
1286 IF_TRACING(T_TUNNEL, {
1287 trace(T_TUNNEL, "tun-java: packet arrived");
1288 trace_block(T_PACKET, "tunnel: packet contents", buf_i, n);
1290 buf_init(&b, buf_i, n);
1294 static tunnel *t_create(peer *p, int fd, char **ifn)
1296 JNIEnv *jni = jni_tripe;
1298 const char *name = p_name(p);
1300 size_t n = strlen(p_name(p));
1301 jclass cls, metacls;
1302 jstring jclsname, jexcmsg;
1303 const char *clsname, *excmsg;
1309 jname = wrap_cstring(jni, name);
1310 cls = (*jni)->FindClass(jni, SYSCLS); assert(cls);
1311 mid = (*jni)->GetStaticMethodID(jni, cls, "getTunnelFd", "([B)I");
1313 fd = (*jni)->CallStaticIntMethod(jni, cls, mid, jname);
1315 exc = (*jni)->ExceptionOccurred(jni);
1317 cls = (*jni)->GetObjectClass(jni, exc);
1318 metacls = (*jni)->GetObjectClass(jni, cls);
1319 mid = (*jni)->GetMethodID(jni, metacls,
1320 "getName", "()L"STRCLS";");
1322 jclsname = (*jni)->CallObjectMethod(jni, cls, mid);
1323 clsname = (*jni)->GetStringUTFChars(jni, jclsname, 0);
1324 mid = (*jni)->GetMethodID(jni, cls,
1325 "getMessage", "()L"STRCLS";");
1326 jexcmsg = (*jni)->CallObjectMethod(jni, exc, mid);
1327 excmsg = (*jni)->GetStringUTFChars(jni, jexcmsg, 0);
1328 a_warn("TUN", "-", "java", "get-tunnel-fd-failed",
1329 "%s", clsname, "%s", excmsg, A_END);
1330 (*jni)->ReleaseStringUTFChars(jni, jclsname, clsname);
1331 (*jni)->ReleaseStringUTFChars(jni, jexcmsg, excmsg);
1332 (*jni)->ExceptionClear(jni);
1339 sel_initfile(&sel, &t->f, fd, SEL_READ, t_read, t);
1342 *ifn = xmalloc(n + 5);
1343 sprintf(*ifn, "vpn-%s", name);
1350 static void t_inject(tunnel *t, buf *b)
1352 IF_TRACING(T_TUNNEL, {
1353 trace(T_TUNNEL, "tun-java: inject decrypted packet");
1354 trace_block(T_PACKET, "tunnel: packet contents", BBASE(b), BLEN(b));
1356 DISCARD(write(t->f.fd, BBASE(b), BLEN(b)));
1359 static void t_destroy(tunnel *t)
1360 { sel_rmfile(&t->f); close(t->f.fd); DESTROY(t); }
1362 static const struct tunnel_ops tun_java = {
1365 /* create */ t_create,
1367 /* inject */ t_inject,
1368 /* destroy */ t_destroy
1372 JNIEXPORT jint JNICALL JNIFUNC(open_1tun)(JNIEnv *jni, jobject cls)
1378 if ((fd = open("/dev/net/tun", O_RDWR)) < 0) {
1379 except_syserror(jni, SYSERR, errno, "failed to open tunnel device");
1383 if (set_nonblocking(jni, fd, 1) || set_closeonexec(jni, fd)) goto end;
1385 memset(&iff, 0, sizeof(iff));
1386 iff.ifr_name[0] = 0;
1387 iff.ifr_flags = IFF_TUN | IFF_NO_PI;
1388 if (ioctl(fd, TUNSETIFF, &iff) < 0) {
1389 except_syserror(jni, SYSERR, errno, "failed to configure tunnel device");
1396 if (fd != -1) close(fd);
1400 /*----- A custom noise source ---------------------------------------------*/
1402 static void javanoise(rand_pool *r)
1404 JNIEnv *jni = jni_tripe;
1414 cls = (*jni)->FindClass(jni, RANDCLS); assert(cls);
1415 mid = (*jni)->GetStaticMethodID(jni, cls, "getSeed", "(I)[B"); assert(mid);
1416 v = (*jni)->CallStaticObjectMethod(jni, cls, mid, 32);
1418 n = (*jni)->GetArrayLength(jni, v);
1419 p = (*jni)->GetByteArrayElements(jni, v, 0);
1420 rand_add(r, p, n, n);
1421 (*jni)->ReleaseByteArrayElements(jni, v, p, JNI_ABORT);
1423 if ((*jni)->ExceptionOccurred(jni)) {
1424 (*jni)->ExceptionDescribe(jni);
1425 (*jni)->ExceptionClear(jni);
1429 static const rand_source javasource = { javanoise, noise_timer };
1431 /*----- Embedding the TrIPE server ----------------------------------------*/
1433 static void lock_tripe(JNIEnv *jni)
1435 jclass cls = (*jni)->FindClass(jni, LOCKCLS); assert(cls);
1436 (*jni)->MonitorEnter(jni, cls);
1439 static void unlock_tripe(JNIEnv *jni)
1441 jclass cls = (*jni)->FindClass(jni, LOCKCLS); assert(cls);
1442 (*jni)->MonitorExit(jni, cls);
1454 #define DEFTAG(st) st,
1460 static const char *statetab[] = {
1461 #define DEFNAME(st) #st,
1466 static unsigned state = INIT;
1467 static int clientsk = -1;
1469 static const char *statename(unsigned st)
1471 if (st >= MAXSTATE) return ("<invalid>");
1472 else return (statetab[st]);
1475 static int ensure_state(JNIEnv *jni, unsigned want)
1484 except(jni, STERR, "server is in state %s (%u), not %s (%u)",
1485 statename(cur), cur, statename(want), want);
1491 JNIEXPORT void JNICALL JNIFUNC(base_1init)(JNIEnv *jni, jobject cls)
1496 for (i = 0; i < N(fd); i++) fd[i] = -1;
1500 if (ensure_state(jni, INIT)) goto end;
1502 if (socketpair(PF_UNIX, SOCK_STREAM, 0, fd)) {
1503 except_syserror(jni, SYSERR, errno, "failed to create socket pair");
1507 clientsk = fd[0]; fd[0] = -1;
1509 rand_noisesrc(RAND_GLOBAL, &javasource);
1510 rand_seed(RAND_GLOBAL, MAXHASHSZ);
1512 a_create(fd[1], fd[1], AF_NOTE | AF_WARN | AF_TRACE); fd[1] = -1;
1514 p_addtun(&tun_java); p_setdflttun(&tun_java);
1521 for (i = 0; i < N(fd); i++) if (fd[i] != -1) close(fd[i]);
1526 JNIEXPORT void JNICALL JNIFUNC(setup_1resolver)(JNIEnv *jni, jobject cls)
1529 if (ensure_state(jni, RESOLVE)) goto end;
1532 { except(jni, INITERR, "failed to initialize resolver"); return; }
1540 JNIEXPORT void JNICALL JNIFUNC(load_1keys)(JNIEnv *jni, jobject cls,
1541 jobject privstr, jobject pubstr,
1544 const char *priv = 0, *pub = 0, *tag = 0;
1547 if (ensure_state(jni, KEYS)) return;
1549 priv = get_cstring(jni, privstr); if (!priv) goto end;
1550 pub = get_cstring(jni, pubstr); if (!pub) goto end;
1551 tag = get_cstring(jni, tagstr); if (!tag) goto end;
1553 if (km_init(priv, pub, tag))
1554 { except(jni, INITERR, "failed to load initial keys"); goto end; }
1559 put_cstring(jni, privstr, priv);
1560 put_cstring(jni, pubstr, pub);
1561 put_cstring(jni, tagstr, tag);
1565 JNIEXPORT void JNICALL JNIFUNC(unload_1keys)(JNIEnv *jni, jobject cls)
1568 if (ensure_state(jni, KEYS + 1)) goto end;
1578 JNIEXPORT void JNICALL JNIFUNC(bind)(JNIEnv *jni, jobject cls,
1579 jbyteArray hoststr, jbyteArray svcstr)
1581 const char *host = 0, *svc = 0;
1582 struct addrinfo hint, *ai = 0;
1586 if (ensure_state(jni, BIND)) goto end;
1588 if (hoststr) { host = get_cstring(jni, hoststr); if (!host) goto end; }
1589 svc = get_cstring(jni, svcstr); if (!svc) goto end;
1591 hint.ai_socktype = SOCK_DGRAM;
1592 hint.ai_family = AF_UNSPEC;
1593 hint.ai_protocol = IPPROTO_UDP;
1594 hint.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
1595 err = getaddrinfo(host, svc, &hint, &ai);
1597 except(jni, NAMEERR, "failed to resolve %c%s%c, port `%s': %s",
1598 host ? '`' : '<', host ? host : "nil", host ? '\'' : '>',
1599 svc, gai_strerror(err));
1604 { except(jni, INITERR, "failed to bind master socket"); goto end; }
1609 if (ai) freeaddrinfo(ai);
1610 put_cstring(jni, hoststr, host);
1611 put_cstring(jni, svcstr, svc);
1615 JNIEXPORT void JNICALL JNIFUNC(unbind)(JNIEnv *jni, jobject cls)
1618 if (ensure_state(jni, BIND + 1)) goto end;
1628 JNIEXPORT void JNICALL JNIFUNC(mark)(JNIEnv *jni, jobject cls, jint seq)
1631 a_notify("MARK", "%d", seq, A_END);
1635 JNIEXPORT void JNICALL JNIFUNC(run)(JNIEnv *jni, jobject cls)
1638 if (ensure_state(jni, READY)) goto end;
1654 static int check_buffer_bounds(JNIEnv *jni, const char *what,
1655 jbyteArray buf, jint start, jint len)
1660 cls = (*jni)->FindClass(jni, "[B"); assert(cls);
1661 if (!(*jni)->IsInstanceOf(jni, buf, cls)) {
1663 "expected a byte array");
1666 bufsz = (*jni)->GetArrayLength(jni, buf);
1667 if (start > bufsz) {
1668 except(jni, BOUNDSERR,
1669 "bad %s buffer bounds: start %d > buffer size %d", start, bufsz);
1672 if (len > bufsz - start) {
1673 except(jni, BOUNDSERR,
1674 "bad %s buffer bounds: length %d > remaining buffer size %d",
1675 len, bufsz - start);
1681 JNIEXPORT void JNICALL JNIFUNC(send)(JNIEnv *jni, jobject cls,
1683 jint start, jint len,
1686 struct trigger trig;
1692 if (ensure_state(jni, RUNNING)) goto end;
1694 if (unwrap(jni, &trig, &trigger_type, wtrig)) goto end;
1695 if (check_buffer_bounds(jni, "send", buf, start, len)) goto end;
1697 p = (*jni)->GetByteArrayElements(jni, buf, 0);
1701 if (maxfd < clientsk) maxfd = clientsk;
1703 FD_ZERO(&rfds); FD_SET(trig.rfd, &rfds);
1704 FD_ZERO(&wfds); FD_SET(clientsk, &wfds);
1705 rc = select(maxfd + 1, &rfds, &wfds, 0, 0); if (rc < 0) goto err;
1706 if (FD_ISSET(trig.rfd, &rfds)) break;
1707 if (FD_ISSET(clientsk, &wfds)) {
1708 n = send(clientsk, p + start, len, 0);
1709 if (n >= 0) { start += n; len -= n; }
1710 else if (errno != EAGAIN && errno != EWOULDBLOCK) goto err;
1716 except_syserror(jni, SYSERR, errno, "failed to send on connection");
1718 if (p) (*jni)->ReleaseByteArrayElements(jni, buf, p, JNI_ABORT);
1722 JNIEXPORT jint JNICALL JNIFUNC(recv)(JNIEnv *jni, jobject cls,
1724 jint start, jint len,
1727 struct trigger trig;
1734 if (clientsk == -1) {
1735 except(jni, STERR, "client connection not established");
1741 if (unwrap(jni, &trig, &trigger_type, wtrig)) goto end;
1742 if (check_buffer_bounds(jni, "send", buf, start, len)) goto end;
1744 p = (*jni)->GetByteArrayElements(jni, buf, 0);
1748 if (maxfd < clientsk) maxfd = clientsk;
1750 FD_ZERO(&rfds); FD_SET(trig.rfd, &rfds); FD_SET(clientsk, &rfds);
1751 rc = select(maxfd + 1, &rfds, 0, 0, 0); if (rc < 0) goto err;
1752 if (FD_ISSET(trig.rfd, &rfds)) {
1755 if (FD_ISSET(clientsk, &rfds)) {
1756 rc = recv(clientsk, p + start, len, 0);
1758 else if (errno != EAGAIN && errno != EWOULDBLOCK) goto err;
1765 except_syserror(jni, SYSERR, errno, "failed to read from connection");
1767 if (p) (*jni)->ReleaseByteArrayElements(jni, buf, p, 0);
1771 /*----- That's all, folks -------------------------------------------------*/