Mostly autogenerating string resources.
[tripe-android] / jni.c
CommitLineData
8eabb4ff
MW
1/* -*-c-*-
2 *
3 * Native-code portions of the project
4 *
5 * (c) 2018 Straylight/Edgeware
6 */
7
8/*----- Licensing notice --------------------------------------------------*
9 *
10 * This file is part of the Trivial IP Encryption (TrIPE) Android app.
11 *
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.
16 *
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
20 * for more details.
21 *
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/>.
24 */
25
26/*----- Header files ------------------------------------------------------*/
27
28#define _FILE_OFFSET_BITS 64
29
3a2f1a4b 30#include <assert.h>
8eabb4ff 31#include <ctype.h>
3a2f1a4b
MW
32#include <errno.h>
33#include <inttypes.h>
8eabb4ff 34#include <stdarg.h>
3a2f1a4b
MW
35#include <stdio.h>
36#include <stdlib.h>
37#include <string.h>
38
3bb2303d
MW
39#include <dirent.h>
40#include <fcntl.h>
41#include <netdb.h>
42#include <unistd.h>
43#include <sys/ioctl.h>
04a5abae 44#include <sys/select.h>
3a2f1a4b 45#include <sys/socket.h>
8eabb4ff
MW
46#include <sys/stat.h>
47#include <sys/sysmacros.h>
3bb2303d 48#include <sys/types.h>
3a2f1a4b 49#include <sys/un.h>
3bb2303d
MW
50
51#include <jni.h>
52
53//#include <linux/if.h>
54#include <linux/if_tun.h>
8eabb4ff
MW
55
56#include <mLib/align.h>
57#include <mLib/bits.h>
58#include <mLib/dstr.h>
59#include <mLib/macros.h>
60
61#include <catacomb/ghash.h>
3a2f1a4b 62
3bb2303d
MW
63#define TUN_INTERNALS
64#include <tripe.h>
65
3a2f1a4b
MW
66#undef sun
67
8eabb4ff
MW
68/*----- Magic class names and similar -------------------------------------*/
69
70/* The name decoration is horrific. Hide it. */
25c35469 71#define JNIFUNC(f) Java_uk_org_distorted_tripe_sys_package_00024_##f
8eabb4ff
MW
72
73/* The little class for bundling up error codes. */
3bb2303d
MW
74#define ERRENTCLS "uk/org/distorted/tripe/sys/package$ErrorEntry"
75
76/* The `sys' package class. */
77#define SYSCLS "uk/org/distorted/tripe/sys/package"
78
79/* The server lock class. */
80#define LOCKCLS "uk/org/distorted/tripe/sys/package$ServerLock"
8eabb4ff
MW
81
82/* The `stat' class. */
3bb2303d
MW
83#define STATCLS "uk/org/distorted/tripe/sys/package$FileInfo"
84
85/* Standard Java classes. */
86#define FDCLS "java/io/FileDescriptor"
87#define STRCLS "java/lang/String"
88#define RANDCLS "java/security/SecureRandom"
8eabb4ff
MW
89
90/* Exception class names. */
91#define NULLERR "java/lang/NullPointerException"
25c35469 92#define TYPEERR "uk/org/distorted/tripe/sys/package$NativeObjectTypeException"
8eabb4ff 93#define SYSERR "uk/org/distorted/tripe/sys/package$SystemError"
3bb2303d
MW
94#define NAMEERR "uk/org/distorted/tripe/sys/package$NameResolutionException"
95#define INITERR "uk/org/distorted/tripe/sys/package$InitializationException"
8eabb4ff 96#define ARGERR "java/lang/IllegalArgumentException"
3bb2303d 97#define STERR "java/lang/IllegalStateException"
8eabb4ff
MW
98#define BOUNDSERR "java/lang/IndexOutOfBoundsException"
99
3bb2303d 100/*----- Essential state ---------------------------------------------------*/
8eabb4ff 101
3bb2303d
MW
102static JNIEnv *jni_tripe = 0;
103
104/*----- Miscellaneous utilities -------------------------------------------*/
8eabb4ff
MW
105
106static void vexcept(JNIEnv *jni, const char *clsname,
25c35469 107 const char *msg, va_list *ap)
8eabb4ff
MW
108{
109 jclass cls;
110 int rc;
111 dstr d = DSTR_INIT;
112
113 cls = (*jni)->FindClass(jni, clsname); assert(cls);
114 if (!msg)
115 rc = (*jni)->ThrowNew(jni, cls, 0);
116 else {
25c35469 117 dstr_vputf(&d, msg, ap);
8eabb4ff
MW
118 rc = (*jni)->ThrowNew(jni, cls, d.buf);
119 assert(!rc);
120 dstr_destroy(&d);
121 }
122 assert(!rc);
123}
124
125static void except(JNIEnv *jni, const char *clsname, const char *msg, ...)
126{
127 va_list ap;
128
129 va_start(ap, msg);
25c35469 130 vexcept(jni, clsname, msg, &ap);
8eabb4ff
MW
131 va_end(ap);
132}
133
134#ifdef DEBUG
135static void dump_bytes(const void *p, size_t n, size_t o)
136{
137 const unsigned char *q = p;
138 size_t i;
139
140 if (!n) return;
141 for (;;) {
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);
149 fputc('\n', stderr);
150 if (n <= 8) break;
151 q += 8; n -= 8;
152 }
153}
154
155static void dump_byte_array(JNIEnv *jni, const char *what, jbyteArray v)
156{
157 jsize n;
158 jbyte *p;
159
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);
164 dump_bytes(p, n, 0);
165 (*jni)->ReleaseByteArrayElements(jni, v, p, JNI_ABORT);
166}
167#endif
168
169static jbyteArray wrap_cstring(JNIEnv *jni, const char *p)
170{
171 size_t n;
172 jbyteArray v;
173 jbyte *q;
174
175 if (!p) return (0);
176 n = strlen(p) + 1;
177 v = (*jni)->NewByteArray(jni, n); if (!v) return (0);
178 q = (*jni)->GetByteArrayElements(jni, v, 0); if (!q) return (0);
179 memcpy(q, p, n);
180 (*jni)->ReleaseByteArrayElements(jni, v, q, 0);
181 return (v);
182}
183
184static const char *get_cstring(JNIEnv *jni, jbyteArray v)
185{
186 if (!v) { except(jni, NULLERR, 0); return (0); }
187 return ((const char *)(*jni)->GetByteArrayElements(jni, v, 0));
188}
189
3bb2303d
MW
190static void put_cstring(JNIEnv *jni, jbyteArray v, const char *p)
191 { if (p) (*jni)->ReleaseByteArrayElements(jni, v, (jbyte *)p, JNI_ABORT); }
192
8eabb4ff 193static void vexcept_syserror(JNIEnv *jni, const char *clsname,
25c35469 194 int err, const char *msg, va_list *ap)
8eabb4ff
MW
195{
196 jclass cls;
197 int rc;
198 dstr d = DSTR_INIT;
199 jbyteArray msgstr;
200 jthrowable e;
201 jmethodID init;
202
203 cls = (*jni)->FindClass(jni, clsname); assert(cls);
204 init = (*jni)->GetMethodID(jni, cls, "<init>", "(I[B)V"); assert(init);
25c35469 205 dstr_vputf(&d, msg, ap);
8eabb4ff
MW
206 msgstr = wrap_cstring(jni, d.buf); assert(msgstr);
207 dstr_destroy(&d);
208 e = (*jni)->NewObject(jni, cls, init, err, msgstr); assert(e);
209 rc = (*jni)->Throw(jni, e); assert(!rc);
210}
211
212static void except_syserror(JNIEnv *jni, const char *clsname,
213 int err, const char *msg, ...)
214{
215 va_list ap;
216
217 va_start(ap, msg);
25c35469 218 vexcept_syserror(jni, clsname, err, msg, &ap);
8eabb4ff
MW
219 va_end(ap);
220}
221
04a5abae
MW
222static int set_nonblocking(JNIEnv *jni, int fd, int nb)
223{
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);
230err:
231 except_syserror(jni, SYSERR, errno,
232 "failed to set descriptor nonblocking");
233 return (-1);
234}
235
236static int set_closeonexec(JNIEnv *jni, int fd)
237{
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");
242 return (-1);
243 }
244 return (0);
245}
246
8eabb4ff
MW
247/*----- Wrapping native types ---------------------------------------------*/
248
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.
253 */
3a2f1a4b
MW
254
255struct native_type {
256 const char *name;
257 size_t sz;
8eabb4ff 258 uint32 tag;
3a2f1a4b
MW
259};
260
8eabb4ff 261typedef jbyteArray wrapper;
3a2f1a4b 262
8eabb4ff
MW
263struct native_base {
264 uint32 tag;
3a2f1a4b
MW
265};
266
8eabb4ff
MW
267static int unwrap(JNIEnv *jni, void *p,
268 const struct native_type *ty, wrapper w)
269{
270 jbyte *q;
271 jclass cls;
272 struct native_base *b = p;
273 jsize n;
274
275 if (!w) { except(jni, NULLERR, 0); return (-1); }
276 cls = (*jni)->FindClass(jni, "[B"); assert(cls);
277 if (!(*jni)->IsInstanceOf(jni, w, cls)) {
278 except(jni, TYPEERR,
279 "corrupted native object wrapper: expected a byte array");
280 return (-1);
281 }
282 n = (*jni)->GetArrayLength(jni, w);
283 if (n != ty->sz) {
284 except(jni, TYPEERR,
285 "corrupted native object wrapper: wrong size for `%s'",
286 ty->name);
287 return (-1);
288 }
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) {
293 except(jni, TYPEERR,
294 "corrupted native object wrapper: expected tag for `%s'",
295 ty->name);
296 return (-1);
297 }
298 return (0);
299}
300
301static int update_wrapper(JNIEnv *jni, const struct native_type *ty,
302 wrapper w, const void *p)
303{
304 jbyte *q;
305
306 q = (*jni)->GetByteArrayElements(jni, w, 0); if (!q) return (-1);
307 memcpy(q, p, ty->sz);
308 (*jni)->ReleaseByteArrayElements(jni, w, q, 0);
309 return (0);
310}
311
312static wrapper wrap(JNIEnv *jni, const struct native_type *ty, const void *p)
313{
314 wrapper w;
315
316 w = (*jni)->NewByteArray(jni, ty->sz); if (!w) return (0);
317 if (update_wrapper(jni, ty, w, p)) return (0);
318 return (w);
319}
320
321#define INIT_NATIVE(type, p) do (p)->_base.tag = type##_type.tag; while (0)
322
323/*----- Crypto information ------------------------------------------------*/
324
325JNIEXPORT jint JNICALL JNIFUNC(hashsz)(JNIEnv *jni, jobject cls,
326 jstring hnamestr)
327{
328 jint rc = -1;
329 const char *hname;
330 const gchash *hc;
331
332 hname = (*jni)->GetStringUTFChars(jni, hnamestr, 0);
333 if (!hname) goto end;
334 hc = ghash_byname(hname); if (!hc) goto end;
335 rc = hc->hashsz;
336
337end:
338 if (hname) (*jni)->ReleaseStringUTFChars(jni, hnamestr, hname);
339 return (rc);
340}
341
342/*----- System errors -----------------------------------------------------*/
343
344static const struct errtab { const char *tag; int err; } errtab[] = {
345 /*
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.
349
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
354 ERANGE
355
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)))
373 (save-excursion
c8292b34
MW
374 (goto-char (point-min))
375 (search-forward (concat "***" "BEGIN errtab" "***"))
376 (beginning-of-line 2)
377 (delete-region (point)
378 (progn
379 (search-forward "***END***")
380 (beginning-of-line)
381 (point)))
382 (dolist (err errors)
383 (insert (format "#ifdef %s\n { \"%s\", %s },\n#endif\n"
384 err err err)))))
8eabb4ff
MW
385 */
386 /***BEGIN errtab***/
387#ifdef EPERM
388 { "EPERM", EPERM },
389#endif
390#ifdef ENOENT
391 { "ENOENT", ENOENT },
392#endif
393#ifdef ESRCH
394 { "ESRCH", ESRCH },
395#endif
396#ifdef EINTR
397 { "EINTR", EINTR },
398#endif
399#ifdef EIO
400 { "EIO", EIO },
401#endif
402#ifdef ENXIO
403 { "ENXIO", ENXIO },
404#endif
405#ifdef E2BIG
406 { "E2BIG", E2BIG },
407#endif
408#ifdef ENOEXEC
409 { "ENOEXEC", ENOEXEC },
410#endif
411#ifdef EBADF
412 { "EBADF", EBADF },
413#endif
414#ifdef ECHILD
415 { "ECHILD", ECHILD },
416#endif
417#ifdef EAGAIN
418 { "EAGAIN", EAGAIN },
419#endif
420#ifdef ENOMEM
421 { "ENOMEM", ENOMEM },
422#endif
423#ifdef EACCES
424 { "EACCES", EACCES },
425#endif
426#ifdef EFAULT
427 { "EFAULT", EFAULT },
428#endif
429#ifdef ENOTBLK
430 { "ENOTBLK", ENOTBLK },
431#endif
432#ifdef EBUSY
433 { "EBUSY", EBUSY },
434#endif
435#ifdef EEXIST
436 { "EEXIST", EEXIST },
437#endif
438#ifdef EXDEV
439 { "EXDEV", EXDEV },
440#endif
441#ifdef ENODEV
442 { "ENODEV", ENODEV },
443#endif
444#ifdef ENOTDIR
445 { "ENOTDIR", ENOTDIR },
446#endif
447#ifdef EISDIR
448 { "EISDIR", EISDIR },
449#endif
450#ifdef EINVAL
451 { "EINVAL", EINVAL },
452#endif
453#ifdef ENFILE
454 { "ENFILE", ENFILE },
455#endif
456#ifdef EMFILE
457 { "EMFILE", EMFILE },
458#endif
459#ifdef ENOTTY
460 { "ENOTTY", ENOTTY },
461#endif
462#ifdef ETXTBSY
463 { "ETXTBSY", ETXTBSY },
464#endif
465#ifdef EFBIG
466 { "EFBIG", EFBIG },
467#endif
468#ifdef ENOSPC
469 { "ENOSPC", ENOSPC },
470#endif
471#ifdef ESPIPE
472 { "ESPIPE", ESPIPE },
473#endif
474#ifdef EROFS
475 { "EROFS", EROFS },
476#endif
477#ifdef EMLINK
478 { "EMLINK", EMLINK },
479#endif
480#ifdef EPIPE
481 { "EPIPE", EPIPE },
482#endif
483#ifdef EDOM
484 { "EDOM", EDOM },
485#endif
486#ifdef ERANGE
487 { "ERANGE", ERANGE },
488#endif
489#ifdef EDEADLK
490 { "EDEADLK", EDEADLK },
491#endif
492#ifdef ENAMETOOLONG
493 { "ENAMETOOLONG", ENAMETOOLONG },
494#endif
495#ifdef ENOLCK
496 { "ENOLCK", ENOLCK },
497#endif
498#ifdef ENOSYS
499 { "ENOSYS", ENOSYS },
500#endif
501#ifdef ENOTEMPTY
502 { "ENOTEMPTY", ENOTEMPTY },
503#endif
504#ifdef ELOOP
505 { "ELOOP", ELOOP },
506#endif
507#ifdef EWOULDBLOCK
508 { "EWOULDBLOCK", EWOULDBLOCK },
509#endif
510#ifdef ENOMSG
511 { "ENOMSG", ENOMSG },
512#endif
513#ifdef EIDRM
514 { "EIDRM", EIDRM },
515#endif
516#ifdef ECHRNG
517 { "ECHRNG", ECHRNG },
518#endif
519#ifdef EL2NSYNC
520 { "EL2NSYNC", EL2NSYNC },
521#endif
522#ifdef EL3HLT
523 { "EL3HLT", EL3HLT },
524#endif
525#ifdef EL3RST
526 { "EL3RST", EL3RST },
527#endif
528#ifdef ELNRNG
529 { "ELNRNG", ELNRNG },
530#endif
531#ifdef EUNATCH
532 { "EUNATCH", EUNATCH },
533#endif
534#ifdef ENOCSI
535 { "ENOCSI", ENOCSI },
536#endif
537#ifdef EL2HLT
538 { "EL2HLT", EL2HLT },
539#endif
540#ifdef EBADE
541 { "EBADE", EBADE },
542#endif
543#ifdef EBADR
544 { "EBADR", EBADR },
545#endif
546#ifdef EXFULL
547 { "EXFULL", EXFULL },
548#endif
549#ifdef ENOANO
550 { "ENOANO", ENOANO },
551#endif
552#ifdef EBADRQC
553 { "EBADRQC", EBADRQC },
554#endif
555#ifdef EBADSLT
556 { "EBADSLT", EBADSLT },
557#endif
558#ifdef EDEADLOCK
559 { "EDEADLOCK", EDEADLOCK },
560#endif
561#ifdef EBFONT
562 { "EBFONT", EBFONT },
563#endif
564#ifdef ENOSTR
565 { "ENOSTR", ENOSTR },
566#endif
567#ifdef ENODATA
568 { "ENODATA", ENODATA },
569#endif
570#ifdef ETIME
571 { "ETIME", ETIME },
572#endif
573#ifdef ENOSR
574 { "ENOSR", ENOSR },
575#endif
576#ifdef ENONET
577 { "ENONET", ENONET },
578#endif
579#ifdef ENOPKG
580 { "ENOPKG", ENOPKG },
581#endif
582#ifdef EREMOTE
583 { "EREMOTE", EREMOTE },
584#endif
585#ifdef ENOLINK
586 { "ENOLINK", ENOLINK },
587#endif
588#ifdef EADV
589 { "EADV", EADV },
590#endif
591#ifdef ESRMNT
592 { "ESRMNT", ESRMNT },
593#endif
594#ifdef ECOMM
595 { "ECOMM", ECOMM },
596#endif
597#ifdef EPROTO
598 { "EPROTO", EPROTO },
599#endif
600#ifdef EMULTIHOP
601 { "EMULTIHOP", EMULTIHOP },
602#endif
603#ifdef EDOTDOT
604 { "EDOTDOT", EDOTDOT },
605#endif
606#ifdef EBADMSG
607 { "EBADMSG", EBADMSG },
608#endif
609#ifdef EOVERFLOW
610 { "EOVERFLOW", EOVERFLOW },
611#endif
612#ifdef ENOTUNIQ
613 { "ENOTUNIQ", ENOTUNIQ },
614#endif
615#ifdef EBADFD
616 { "EBADFD", EBADFD },
617#endif
618#ifdef EREMCHG
619 { "EREMCHG", EREMCHG },
620#endif
621#ifdef ELIBACC
622 { "ELIBACC", ELIBACC },
623#endif
624#ifdef ELIBBAD
625 { "ELIBBAD", ELIBBAD },
626#endif
627#ifdef ELIBSCN
628 { "ELIBSCN", ELIBSCN },
629#endif
630#ifdef ELIBMAX
631 { "ELIBMAX", ELIBMAX },
632#endif
633#ifdef ELIBEXEC
634 { "ELIBEXEC", ELIBEXEC },
635#endif
636#ifdef EILSEQ
637 { "EILSEQ", EILSEQ },
638#endif
639#ifdef ERESTART
640 { "ERESTART", ERESTART },
641#endif
642#ifdef ESTRPIPE
643 { "ESTRPIPE", ESTRPIPE },
644#endif
645#ifdef EUSERS
646 { "EUSERS", EUSERS },
647#endif
648#ifdef ENOTSOCK
649 { "ENOTSOCK", ENOTSOCK },
650#endif
651#ifdef EDESTADDRREQ
652 { "EDESTADDRREQ", EDESTADDRREQ },
653#endif
654#ifdef EMSGSIZE
655 { "EMSGSIZE", EMSGSIZE },
656#endif
657#ifdef EPROTOTYPE
658 { "EPROTOTYPE", EPROTOTYPE },
659#endif
660#ifdef ENOPROTOOPT
661 { "ENOPROTOOPT", ENOPROTOOPT },
662#endif
663#ifdef EPROTONOSUPPORT
664 { "EPROTONOSUPPORT", EPROTONOSUPPORT },
665#endif
666#ifdef ESOCKTNOSUPPORT
667 { "ESOCKTNOSUPPORT", ESOCKTNOSUPPORT },
668#endif
669#ifdef EOPNOTSUPP
670 { "EOPNOTSUPP", EOPNOTSUPP },
671#endif
672#ifdef EPFNOSUPPORT
673 { "EPFNOSUPPORT", EPFNOSUPPORT },
674#endif
675#ifdef EAFNOSUPPORT
676 { "EAFNOSUPPORT", EAFNOSUPPORT },
677#endif
678#ifdef EADDRINUSE
679 { "EADDRINUSE", EADDRINUSE },
680#endif
681#ifdef EADDRNOTAVAIL
682 { "EADDRNOTAVAIL", EADDRNOTAVAIL },
683#endif
684#ifdef ENETDOWN
685 { "ENETDOWN", ENETDOWN },
686#endif
687#ifdef ENETUNREACH
688 { "ENETUNREACH", ENETUNREACH },
689#endif
690#ifdef ENETRESET
691 { "ENETRESET", ENETRESET },
692#endif
693#ifdef ECONNABORTED
694 { "ECONNABORTED", ECONNABORTED },
695#endif
696#ifdef ECONNRESET
697 { "ECONNRESET", ECONNRESET },
698#endif
699#ifdef ENOBUFS
700 { "ENOBUFS", ENOBUFS },
701#endif
702#ifdef EISCONN
703 { "EISCONN", EISCONN },
704#endif
705#ifdef ENOTCONN
706 { "ENOTCONN", ENOTCONN },
707#endif
708#ifdef ESHUTDOWN
709 { "ESHUTDOWN", ESHUTDOWN },
710#endif
711#ifdef ETOOMANYREFS
712 { "ETOOMANYREFS", ETOOMANYREFS },
713#endif
714#ifdef ETIMEDOUT
715 { "ETIMEDOUT", ETIMEDOUT },
716#endif
717#ifdef ECONNREFUSED
718 { "ECONNREFUSED", ECONNREFUSED },
719#endif
720#ifdef EHOSTDOWN
721 { "EHOSTDOWN", EHOSTDOWN },
722#endif
723#ifdef EHOSTUNREACH
724 { "EHOSTUNREACH", EHOSTUNREACH },
725#endif
726#ifdef EALREADY
727 { "EALREADY", EALREADY },
728#endif
729#ifdef EINPROGRESS
730 { "EINPROGRESS", EINPROGRESS },
731#endif
732#ifdef ESTALE
733 { "ESTALE", ESTALE },
734#endif
735#ifdef EUCLEAN
736 { "EUCLEAN", EUCLEAN },
737#endif
738#ifdef ENOTNAM
739 { "ENOTNAM", ENOTNAM },
740#endif
741#ifdef ENAVAIL
742 { "ENAVAIL", ENAVAIL },
743#endif
744#ifdef EISNAM
745 { "EISNAM", EISNAM },
746#endif
747#ifdef EREMOTEIO
748 { "EREMOTEIO", EREMOTEIO },
749#endif
750#ifdef EDQUOT
751 { "EDQUOT", EDQUOT },
752#endif
753#ifdef ENOMEDIUM
754 { "ENOMEDIUM", ENOMEDIUM },
755#endif
756#ifdef EMEDIUMTYPE
757 { "EMEDIUMTYPE", EMEDIUMTYPE },
758#endif
759#ifdef ECANCELED
760 { "ECANCELED", ECANCELED },
761#endif
762#ifdef ENOKEY
763 { "ENOKEY", ENOKEY },
764#endif
765#ifdef EKEYEXPIRED
766 { "EKEYEXPIRED", EKEYEXPIRED },
767#endif
768#ifdef EKEYREVOKED
769 { "EKEYREVOKED", EKEYREVOKED },
770#endif
771#ifdef EKEYREJECTED
772 { "EKEYREJECTED", EKEYREJECTED },
773#endif
774#ifdef EOWNERDEAD
775 { "EOWNERDEAD", EOWNERDEAD },
776#endif
777#ifdef ENOTRECOVERABLE
778 { "ENOTRECOVERABLE", ENOTRECOVERABLE },
779#endif
780#ifdef ERFKILL
781 { "ERFKILL", ERFKILL },
782#endif
783#ifdef EHWPOISON
784 { "EHWPOISON", EHWPOISON },
785#endif
786 /***END***/
3a2f1a4b
MW
787};
788
8eabb4ff 789JNIEXPORT jobject JNIFUNC(errtab)(JNIEnv *jni, jobject cls)
3a2f1a4b 790{
8eabb4ff
MW
791 size_t i;
792 jclass eltcls;
793 jarray v;
794 jmethodID init;
795 jobject e;
3a2f1a4b 796
8eabb4ff 797 eltcls =
3bb2303d 798 (*jni)->FindClass(jni, ERRENTCLS);
8eabb4ff
MW
799 assert(eltcls);
800 v = (*jni)->NewObjectArray(jni, N(errtab), eltcls, 0); if (!v) return (0);
801 init = (*jni)->GetMethodID(jni, eltcls, "<init>",
3bb2303d 802 "(L"STRCLS";I)V");
8eabb4ff
MW
803 assert(init);
804
805 for (i = 0; i < N(errtab); i++) {
806 e = (*jni)->NewObject(jni, eltcls, init,
807 (*jni)->NewStringUTF(jni, errtab[i].tag),
808 errtab[i].err);
809 (*jni)->SetObjectArrayElement(jni, v, i, e);
810 }
811 return (v);
812}
813
814JNIEXPORT jobject JNIFUNC(strerror)(JNIEnv *jni, jobject cls, jint err)
815 { return (wrap_cstring(jni, strerror(err))); }
816
c8292b34
MW
817/*----- Messing with file descriptors -------------------------------------*/
818
819static void fdguts(JNIEnv *jni, jclass *cls, jfieldID *fid)
820{
3bb2303d 821 *cls = (*jni)->FindClass(jni, FDCLS); assert(cls);
c8292b34
MW
822 *fid = (*jni)->GetFieldID(jni, *cls, "fd", "I"); // OpenJDK
823 if (!*fid) *fid = (*jni)->GetFieldID(jni, *cls, "descriptor", "I"); // Android
824 assert(*fid);
825}
826
827static int fdint(JNIEnv *jni, jobject jfd)
828{
829 jclass cls;
830 jfieldID fid;
831
832 fdguts(jni, &cls, &fid);
833 return ((*jni)->GetIntField(jni, jfd, fid));
834}
835
836static jobject newfd(JNIEnv *jni, int fd)
837{
838 jobject jfd;
839 jclass cls;
840 jmethodID init;
841 jfieldID fid;
842
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);
847 return (jfd);
848}
849
850JNIEXPORT jint JNIFUNC(fdint)(JNIEnv *jni, jobject cls, jobject jfd)
851 { return (fdint(jni, jfd)); }
852
853JNIEXPORT jobject JNIFUNC(newfd)(JNIEnv *jni, jobject cls, jint fd)
854 { return (newfd(jni, fd)); }
855
856JNIEXPORT jboolean JNIFUNC(isatty)(JNIEnv *jni, jobject cls, jobject jfd)
857 { return (isatty(fdint(jni, jfd))); }
858
8eabb4ff
MW
859/*----- Low-level file operations -----------------------------------------*/
860
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'
04a5abae 863 * success/fail indicator, and throw away any more detailed information.
8eabb4ff
MW
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.
868 *
869 * So the other option is to implement them ourselves.
870 */
871
872JNIEXPORT void JNIFUNC(unlink)(JNIEnv *jni, jobject cls, jobject path)
873{
874 const char *pathstr = 0;
875
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);
880 goto end;
881 }
882end:
883 put_cstring(jni, path, pathstr);
3a2f1a4b
MW
884}
885
8eabb4ff
MW
886JNIEXPORT void JNIFUNC(rmdir)(JNIEnv *jni, jobject cls, jobject path)
887{
888 const char *pathstr = 0;
3a2f1a4b 889
8eabb4ff
MW
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);
894 goto end;
895 }
896end:
897 put_cstring(jni, path, pathstr);
898}
899
900JNIEXPORT void JNIFUNC(mkdir)(JNIEnv *jni, jobject cls,
901 jobject path, jint mode)
3a2f1a4b 902{
8eabb4ff
MW
903 const char *pathstr = 0;
904
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);
909 goto end;
910 }
911end:
912 put_cstring(jni, path, pathstr);
3a2f1a4b
MW
913}
914
8eabb4ff
MW
915JNIEXPORT void JNIFUNC(mkfile)(JNIEnv *jni, jobject cls,
916 jobject path, jint mode)
3a2f1a4b 917{
8eabb4ff
MW
918 const char *pathstr = 0;
919 int fd = -1;
3a2f1a4b 920
8eabb4ff
MW
921 pathstr = get_cstring(jni, path); if (!pathstr) goto end;
922 fd = open(pathstr, O_WRONLY | O_CREAT | O_EXCL, mode);
923 if (fd < 0) {
924 except_syserror(jni, SYSERR, errno,
925 "failed to create fresh file `%s'", pathstr);
926 goto end;
3a2f1a4b 927 }
8eabb4ff
MW
928end:
929 if (fd != -1) close(fd);
930 put_cstring(jni, path, pathstr);
3a2f1a4b
MW
931}
932
8eabb4ff
MW
933JNIEXPORT void JNIFUNC(rename)(JNIEnv *jni, jobject cls,
934 jobject from, jobject to)
3a2f1a4b 935{
8eabb4ff
MW
936 const char *fromstr = 0, *tostr = 0;
937
938 fromstr = get_cstring(jni, from); if (!fromstr) goto end;
939 tostr = get_cstring(jni, to); if (!tostr) goto end;
940 if (rename(fromstr, tostr)) {
941 except_syserror(jni, SYSERR, errno,
942 "failed to rename `%s' as `%s'", fromstr, tostr);
943 goto end;
944 }
945end:
946 put_cstring(jni, from, fromstr);
947 put_cstring(jni, to, tostr);
3a2f1a4b
MW
948}
949
c8292b34
MW
950#define LKF_EXCL 0x1000u
951#define LKF_WAIT 0x2000u
8eabb4ff
MW
952struct lockf {
953 struct native_base _base;
954 int fd;
955};
956static struct native_type lockf_type =
957 { "lock", sizeof(struct lockf), 0xb2648926};
958JNIEXPORT wrapper JNIFUNC(lock)(JNIEnv *jni, jobject cls,
959 jobject path, jint flags)
960{
961 const char *pathstr = 0;
962 int fd = -1;
963 struct flock l;
964 struct lockf lk;
965 struct stat st0, st1;
966 int f;
967 wrapper r = 0;
968
969 pathstr = get_cstring(jni, path); if (!pathstr) goto end;
970
971again:
c8292b34 972 fd = open(pathstr, O_RDWR | O_CREAT, flags&07777); if (fd < 0) goto err;
8eabb4ff
MW
973 if (fstat(fd, &st0)) goto err;
974 f = fcntl(fd, F_GETFD); if (f < 0) goto err;
975 if (fcntl(fd, F_SETFD, f | FD_CLOEXEC)) goto err;
976 l.l_type = (flags&LKF_EXCL) ? F_WRLCK : F_RDLCK;
977 l.l_whence = SEEK_SET;
978 l.l_start = 0;
979 l.l_len = 0;
980 if (fcntl(fd, (flags&LKF_WAIT) ? F_SETLKW : F_SETLK, &l)) goto err;
981 if (stat(pathstr, &st1))
982 { if (errno == ENOENT) goto again; else goto err; }
983 if (st0.st_dev != st1.st_dev || st0.st_ino != st1.st_ino)
984 { close(fd); fd = -1; goto again; }
985
986 INIT_NATIVE(lockf, &lk); lk.fd = fd; fd = -1;
987 r = wrap(jni, &lockf_type, &lk);
988 goto end;
989
990err:
991 except_syserror(jni, SYSERR, errno, "failed to lock file `%s'", pathstr);
992end:
993 if (fd != -1) close(fd);
994 put_cstring(jni, path, pathstr);
995 return (r);
996}
997
998JNIEXPORT void JNIFUNC(unlock)(JNIEnv *jni, jobject cls, wrapper wlk)
999{
1000 struct lockf lk;
1001 struct flock l;
1002 int rc;
1003
1004 if (unwrap(jni, &lk, &lockf_type, wlk)) goto end;
1005 if (lk.fd == -1) goto end;
1006 l.l_type = F_UNLCK;
1007 l.l_whence = SEEK_SET;
1008 l.l_start = 0;
1009 l.l_len = 0;
1010 if (fcntl(lk.fd, F_SETLK, &l)) goto end;
1011 close(lk.fd); lk.fd = -1;
1012 rc = update_wrapper(jni, &lockf_type, wlk, &lk); assert(!rc);
1013end:;
1014}
1015
1016static jlong xlttimespec(const struct timespec *ts)
1017 { return (1000*(jlong)ts->tv_sec + ts->tv_nsec/1000000); }
1018
1019static jobject xltstat(JNIEnv *jni, const struct stat *st)
1020{
1021 jclass cls;
1022 jmethodID init;
1023 jint modehack;
c8292b34 1024
8eabb4ff
MW
1025 modehack = st->st_mode&07777;
1026 if (S_ISFIFO(st->st_mode)) modehack |= 0010000;
1027 else if (S_ISCHR(st->st_mode)) modehack |= 0020000;
1028 else if (S_ISDIR(st->st_mode)) modehack |= 0040000;
1029 else if (S_ISBLK(st->st_mode)) modehack |= 0060000;
1030 else if (S_ISREG(st->st_mode)) modehack |= 0100000;
1031 else if (S_ISLNK(st->st_mode)) modehack |= 0120000;
1032 else if (S_ISSOCK(st->st_mode)) modehack |= 0140000;
1033
3bb2303d 1034 cls = (*jni)->FindClass(jni, STATCLS); assert(cls);
8eabb4ff
MW
1035 init = (*jni)->GetMethodID(jni, cls, "<init>", "(IIJIIIIIIJIJJJJ)V");
1036 assert(init);
1037 return ((*jni)->NewObject(jni, cls, init,
1038 (jint)major(st->st_dev), (jint)minor(st->st_dev),
1039 (jlong)st->st_ino,
1040 modehack,
1041 (jint)st->st_nlink,
1042 (jint)st->st_uid, (jint)st->st_gid,
1043 (jint)major(st->st_rdev), (jint)minor(st->st_rdev),
1044 (jlong)st->st_size,
1045 (jint)st->st_blksize, (jlong)st->st_blocks,
1046 xlttimespec(&st->st_atim),
1047 xlttimespec(&st->st_mtim),
1048 xlttimespec(&st->st_ctim)));
1049}
1050
1051JNIEXPORT jobject JNIFUNC(stat)(JNIEnv *jni, jobject cls, jobject path)
3a2f1a4b 1052{
8eabb4ff
MW
1053 jobject r = 0;
1054 const char *pathstr = 0;
1055 struct stat st;
1056
1057 pathstr = get_cstring(jni, path); if (!pathstr) goto end;
1058 if (stat(pathstr, &st)) {
1059 except_syserror(jni, SYSERR, errno,
1060 "failed to read information about `%s'", pathstr);
1061 goto end;
1062 }
1063 r = xltstat(jni, &st);
1064end:
1065 put_cstring(jni, path, pathstr);
1066 return (r);
3a2f1a4b
MW
1067}
1068
8eabb4ff
MW
1069JNIEXPORT jobject JNIFUNC(lstat)(JNIEnv *jni, jobject cls, jobject path)
1070{
1071 jobject r = 0;
1072 const char *pathstr = 0;
1073 struct stat st;
3a2f1a4b 1074
8eabb4ff
MW
1075 pathstr = get_cstring(jni, path); if (!pathstr) goto end;
1076 if (lstat(pathstr, &st)) {
1077 except_syserror(jni, SYSERR, errno,
1078 "failed to read information about `%s'", pathstr);
1079 goto end;
1080 }
1081 r = xltstat(jni, &st);
1082end:
1083 put_cstring(jni, path, pathstr);
1084 return (r);
1085}
1086
1087struct dir {
1088 struct native_base _base;
1089 DIR *d;
3a2f1a4b 1090};
8eabb4ff
MW
1091static const struct native_type dir_type =
1092 { "dir", sizeof(struct dir), 0x0f5ca477 };
3a2f1a4b 1093
8eabb4ff 1094JNIEXPORT jobject JNIFUNC(opendir)(JNIEnv *jni, jobject cls, jobject path)
3a2f1a4b 1095{
8eabb4ff
MW
1096 const char *pathstr = 0;
1097 struct dir dir;
1098 wrapper r = 0;
3a2f1a4b 1099
8eabb4ff
MW
1100 pathstr = get_cstring(jni, path); if (!pathstr) goto end;
1101 INIT_NATIVE(dir, &dir);
1102 dir.d = opendir(pathstr);
1103 if (!dir.d) {
1104 except_syserror(jni, SYSERR, errno,
1105 "failed to open directory `%s'", pathstr);
1106 goto end;
1107 }
1108 r = wrap(jni, &dir_type, &dir);
1109end:
1110 put_cstring(jni, path, pathstr);
1111 return (r);
3a2f1a4b
MW
1112}
1113
8eabb4ff
MW
1114JNIEXPORT jbyteArray JNIFUNC(readdir)(JNIEnv *jni, jobject cls,
1115 jobject path, jobject wdir)
3a2f1a4b 1116{
8eabb4ff
MW
1117 const char *pathstr = 0;
1118 struct dir dir;
1119 struct dirent *d;
1120 jbyteArray r = 0;
3a2f1a4b 1121
8eabb4ff
MW
1122 if (unwrap(jni, &dir, &dir_type, wdir)) goto end;
1123 if (!dir.d) { except(jni, ARGERR, "directory has been closed"); goto end; }
1124 errno = 0; d = readdir(dir.d);
1125 if (errno) {
1126 pathstr = get_cstring(jni, path); if (!pathstr) goto end;
1127 except_syserror(jni, SYSERR, errno,
1128 "failed to read directory `%s'", pathstr);
1129 goto end;
1130 }
1131 if (d) r = wrap_cstring(jni, d->d_name);
1132end:
1133 put_cstring(jni, path, pathstr);
1134 return (r);
1135}
1136
1137JNIEXPORT void JNIFUNC(closedir)(JNIEnv *jni, jobject cls,
1138 jobject path, jobject wdir)
1139{
1140 const char *pathstr = 0;
1141 struct dir dir;
1142
1143 if (unwrap(jni, &dir, &dir_type, wdir)) goto end;
1144 if (!dir.d) goto end;
1145 if (closedir(dir.d)) {
1146 pathstr = get_cstring(jni, path); if (!pathstr) goto end;
1147 except_syserror(jni, SYSERR, errno,
1148 "failed to close directory `%s'", pathstr);
1149 goto end;
1150 }
1151 dir.d = 0;
1152 if (update_wrapper(jni, &dir_type, wdir, &dir)) goto end;
1153end:
1154 put_cstring(jni, path, pathstr);
3a2f1a4b
MW
1155}
1156
04a5abae
MW
1157/*----- Triggers ----------------------------------------------------------*/
1158
1159/* A trigger is a gadget for waking up a thread which is blocking on I/O,
1160 * and it's used to implement interruptability.
1161 *
1162 * Really, a trigger is a pipe. A `blocking' I/O operation secretly uses
1163 * select(2) to block on the descriptor of interest /and/ the read side of
1164 * the trigger pipe. To wake up a thread that's blocked, we just write a
1165 * byte (nobody cares /which/ byte) to the write end.
1166 */
1167
1168struct trigger {
1169 struct native_base _base;
1170 int rfd, wfd;
1171};
1172static const struct native_type trigger_type =
1173 { "trigger", sizeof(struct trigger), 0x65ffd8b4 };
1174
3bb2303d 1175JNIEXPORT wrapper JNICALL JNIFUNC(make_1trigger)(JNIEnv *jni, jobject cls)
04a5abae
MW
1176{
1177 struct trigger trig;
1178 int fd[2];
1179 int i;
1180 wrapper ret = 0;
1181
1182 fd[0] = fd[1] = -1;
1183 if (pipe(fd)) {
1184 except_syserror(jni, SYSERR, errno, "failed to create pipe");
1185 goto end;
1186 }
1187 for (i = 0; i < 2; i++) {
1188 if (set_nonblocking(jni, fd[i], 1) < 0 || set_closeonexec(jni, fd[i]))
1189 goto end;
1190 }
1191
1192 INIT_NATIVE(trigger, &trig);
1193 trig.rfd = fd[0]; fd[0] = -1;
1194 trig.wfd = fd[1]; fd[1] = -1;
1195 ret = wrap(jni, &trigger_type, &trig);
1196
1197end:
1198 for (i = 0; i < 2; i++)
1199 if (fd[i] != -1) close(fd[i]);
1200 return (ret);
1201}
1202
3bb2303d
MW
1203JNIEXPORT void JNICALL JNIFUNC(destroy_1trigger)(JNIEnv *jni, jobject cls,
1204 wrapper wtrig)
04a5abae
MW
1205{
1206 struct trigger trig;
1207
1208 if (unwrap(jni, &trig, &trigger_type, wtrig)) return;
1209 if (trig.rfd != -1) { close(trig.rfd); trig.rfd = -1; }
1210 if (trig.wfd != -1) { close(trig.wfd); trig.wfd = -1; }
1211 update_wrapper(jni, &trigger_type, wtrig, &trig);
1212}
1213
3bb2303d
MW
1214JNIEXPORT void JNICALL JNIFUNC(reset_1trigger)(JNIEnv *jni, jobject cls,
1215 wrapper wtrig)
04a5abae
MW
1216{
1217 struct trigger trig;
1218 char buf[64];
1219 ssize_t n;
1220
1221 if (unwrap(jni, &trig, &trigger_type, wtrig)) return;
1222 for (;;) {
1223 n = read(trig.rfd, buf, sizeof(buf));
1224 if (n > 0) continue;
1225 assert(n < 0);
1226 if (errno == EAGAIN || errno == EWOULDBLOCK) break;
1227 else {
1228 except_syserror(jni, SYSERR, errno, "failed to reset trigger");
1229 break;
1230 }
1231 }
1232}
1233
1234JNIEXPORT void JNICALL JNIFUNC(trigger)(JNIEnv *jni, jobject cls,
1235 wrapper wtrig)
1236{
1237 struct trigger trig;
1238 ssize_t n;
1239 char c = 0;
1240
1241 if (unwrap(jni, &trig, &trigger_type, wtrig)) return;
1242 n = write(trig.wfd, &c, 1);
1243 if (n < 0 && errno != EAGAIN && errno != EWOULDBLOCK)
1244 except_syserror(jni, SYSERR, errno, "failed to pull trigger");
1245}
1246
3bb2303d 1247/*----- A tunnel supplied by Java -----------------------------------------*/
8eabb4ff 1248
3bb2303d
MW
1249struct tunnel {
1250 const tunnel_ops *ops;
1251 sel_file f;
1252 struct peer *p;
3a2f1a4b 1253};
3a2f1a4b 1254
3bb2303d
MW
1255static const struct tunnel_ops tun_java;
1256
1257static int t_init(void) { return (0); }
1258
1259static void t_read(int fd, unsigned mode, void *v)
3a2f1a4b 1260{
3bb2303d
MW
1261 tunnel *t = v;
1262 ssize_t n;
1263 buf b;
3a2f1a4b 1264
3bb2303d
MW
1265 n = read(fd, buf_i, sizeof(buf_i));
1266 if (n < 0) {
1267 a_warn("TUN", "%s", p_ifname(t->p), "java",
1268 "read-error", "?ERRNO", A_END);
1269 return;
1270 }
1271 IF_TRACING(T_TUNNEL, {
1272 trace(T_TUNNEL, "tun-java: packet arrived");
1273 trace_block(T_PACKET, "tunnel: packet contents", buf_i, n);
1274 })
1275 buf_init(&b, buf_i, n);
1276 p_tun(t->p, &b);
1277}
1278
1279static tunnel *t_create(peer *p, int fd, char **ifn)
1280{
1281 JNIEnv *jni = jni_tripe;
1282 tunnel *t = 0;
1283 const char *name = p_name(p);
1284 jbyteArray jname;
1285 size_t n = strlen(p_name(p));
1286 jclass cls, metacls;
1287 jstring jclsname, jexcmsg;
1288 const char *clsname, *excmsg;
1289 jmethodID mid;
1290 jthrowable exc;
1291
1292 assert(jni);
1293
1294 jname = wrap_cstring(jni, name);
1295 cls = (*jni)->FindClass(jni, SYSCLS); assert(cls);
1296 mid = (*jni)->GetStaticMethodID(jni, cls, "getTunnelFd", "([B)I");
1297 assert(mid);
1298 fd = (*jni)->CallStaticIntMethod(jni, cls, mid, jname);
1299
1300 exc = (*jni)->ExceptionOccurred(jni);
1301 if (exc) {
1302 cls = (*jni)->GetObjectClass(jni, exc);
1303 metacls = (*jni)->GetObjectClass(jni, cls);
1304 mid = (*jni)->GetMethodID(jni, metacls,
1305 "getName", "()L"STRCLS";");
1306 assert(mid);
1307 jclsname = (*jni)->CallObjectMethod(jni, cls, mid);
1308 clsname = (*jni)->GetStringUTFChars(jni, jclsname, 0);
1309 mid = (*jni)->GetMethodID(jni, cls,
1310 "getMessage", "()L"STRCLS";");
1311 jexcmsg = (*jni)->CallObjectMethod(jni, exc, mid);
1312 excmsg = (*jni)->GetStringUTFChars(jni, jexcmsg, 0);
1313 a_warn("TUN", "-", "java", "get-tunnel-fd-failed",
1314 "%s", clsname, "%s", excmsg, A_END);
1315 (*jni)->ReleaseStringUTFChars(jni, jclsname, clsname);
1316 (*jni)->ReleaseStringUTFChars(jni, jexcmsg, excmsg);
1317 (*jni)->ExceptionClear(jni);
8eabb4ff
MW
1318 goto end;
1319 }
3a2f1a4b 1320
3bb2303d
MW
1321 t = CREATE(tunnel);
1322 t->ops = &tun_java;
1323 t->p = p;
1324 sel_initfile(&sel, &t->f, fd, SEL_READ, t_read, t);
3a2f1a4b 1325
3bb2303d
MW
1326 if (!*ifn) {
1327 *ifn = xmalloc(n + 5);
1328 sprintf(*ifn, "vpn-%s", name);
1329 }
04a5abae 1330
3bb2303d
MW
1331end:
1332 return (t);
1333}
1334
1335static void t_inject(tunnel *t, buf *b)
1336{
1337 IF_TRACING(T_TUNNEL, {
1338 trace(T_TUNNEL, "tun-java: inject decrypted packet");
1339 trace_block(T_PACKET, "tunnel: packet contents", BBASE(b), BLEN(b));
1340 })
1341 DISCARD(write(t->f.fd, BBASE(b), BLEN(b)));
1342}
1343
1344static void t_destroy(tunnel *t)
1345 { sel_rmfile(&t->f); close(t->f.fd); DESTROY(t); }
1346
1347static const struct tunnel_ops tun_java = {
1348 "java", 0,
1349 /* init */ t_init,
1350 /* create */ t_create,
1351 /* setifname */ 0,
1352 /* inject */ t_inject,
1353 /* destroy */ t_destroy
1354};
1355
1356
1357JNIEXPORT jint JNICALL JNIFUNC(open_1tun)(JNIEnv *jni, jobject cls)
1358{
1359 int ret = -1;
1360 int fd = -1;
1361 struct ifreq iff;
1362
1363 if ((fd = open("/dev/net/tun", O_RDWR)) < 0) {
1364 except_syserror(jni, SYSERR, errno, "failed to open tunnel device");
1365 goto end;
04a5abae 1366 }
3a2f1a4b 1367
3bb2303d
MW
1368 if (set_nonblocking(jni, fd, 1) || set_closeonexec(jni, fd)) goto end;
1369
1370 memset(&iff, 0, sizeof(iff));
1371 iff.ifr_name[0] = 0;
1372 iff.ifr_flags = IFF_TUN | IFF_NO_PI;
1373 if (ioctl(fd, TUNSETIFF, &iff) < 0) {
1374 except_syserror(jni, SYSERR, errno, "failed to configure tunnel device");
1375 goto end;
1376 }
1377
1378 ret = fd; fd = -1;
3a2f1a4b 1379
8eabb4ff 1380end:
04a5abae 1381 if (fd != -1) close(fd);
8eabb4ff 1382 return (ret);
3a2f1a4b
MW
1383}
1384
3bb2303d
MW
1385/*----- A custom noise source ---------------------------------------------*/
1386
1387static void javanoise(rand_pool *r)
1388{
1389 JNIEnv *jni = jni_tripe;
1390 jclass cls;
1391 jmethodID mid;
1392 jbyteArray v;
1393 jbyte *p;
1394 jsize n;
1395
1396 noise_devrandom(r);
1397
1398 assert(jni);
1399 cls = (*jni)->FindClass(jni, RANDCLS); assert(cls);
1400 mid = (*jni)->GetStaticMethodID(jni, cls, "getSeed", "(I)[B"); assert(mid);
1401 v = (*jni)->CallStaticObjectMethod(jni, cls, mid, 32);
1402 if (v) {
1403 n = (*jni)->GetArrayLength(jni, v);
1404 p = (*jni)->GetByteArrayElements(jni, v, 0);
1405 rand_add(r, p, n, n);
1406 (*jni)->ReleaseByteArrayElements(jni, v, p, JNI_ABORT);
1407 }
1408 if ((*jni)->ExceptionOccurred(jni)) {
1409 (*jni)->ExceptionDescribe(jni);
1410 (*jni)->ExceptionClear(jni);
1411 }
1412}
1413
1414static const rand_source javasource = { javanoise, noise_timer };
1415
1416/*----- Embedding the TrIPE server ----------------------------------------*/
1417
1418static void lock_tripe(JNIEnv *jni)
1419{
1420 jclass cls = (*jni)->FindClass(jni, LOCKCLS); assert(cls);
1421 (*jni)->MonitorEnter(jni, cls);
1422}
1423
1424static void unlock_tripe(JNIEnv *jni)
1425{
1426 jclass cls = (*jni)->FindClass(jni, LOCKCLS); assert(cls);
1427 (*jni)->MonitorExit(jni, cls);
1428}
1429
1430#define STATES(_) \
1431 _(INIT) \
1432 _(RESOLVE) \
1433 _(KEYS) \
1434 _(BIND) \
1435 _(READY) \
1436 _(RUNNING)
1437
1438enum {
1439#define DEFTAG(st) st,
1440 STATES(DEFTAG)
1441#undef DEFTAG
1442 MAXSTATE
1443};
1444
1445static const char *statetab[] = {
1446#define DEFNAME(st) #st,
1447 STATES(DEFNAME)
1448#undef DEFNAME
1449};
1450
1451static unsigned state = INIT;
1452static int clientsk = -1;
1453
1454static const char *statename(unsigned st)
1455{
1456 if (st >= MAXSTATE) return ("<invalid>");
1457 else return (statetab[st]);
1458}
1459
1460static int ensure_state(JNIEnv *jni, unsigned want)
1461{
1462 unsigned cur;
1463
1464 lock_tripe(jni);
1465 cur = state;
1466 unlock_tripe(jni);
1467
1468 if (cur != want) {
1469 except(jni, STERR, "server is in state %s (%u), not %s (%u)",
1470 statename(cur), cur, statename(want), want);
1471 return (-1);
1472 }
1473 return (0);
1474}
1475
1476JNIEXPORT void JNICALL JNIFUNC(base_1init)(JNIEnv *jni, jobject cls)
1477{
1478 int fd[2];
1479 int i;
1480
1481 for (i = 0; i < N(fd); i++) fd[i] = -1;
1482
1483 lock_tripe(jni);
1484 jni_tripe = jni;
1485 if (ensure_state(jni, INIT)) goto end;
1486
1487 if (socketpair(PF_UNIX, SOCK_STREAM, 0, fd)) {
1488 except_syserror(jni, SYSERR, errno, "failed to create socket pair");
1489 goto end;
1490 }
1491
1492 clientsk = fd[0]; fd[0] = -1;
1493
1494 rand_noisesrc(RAND_GLOBAL, &javasource);
1495 rand_seed(RAND_GLOBAL, MAXHASHSZ);
1496 lp_init();
1497 a_create(fd[1], fd[1], AF_NOTE | AF_WARN | AF_TRACE); fd[1] = -1;
1498 a_switcherr();
1499 p_addtun(&tun_java); p_setdflttun(&tun_java);
1500 p_init();
1501 kx_init();
1502
1503 state++;
1504
1505end:
1506 for (i = 0; i < N(fd); i++) if (fd[i] != -1) close(fd[i]);
1507 jni_tripe = 0;
1508 unlock_tripe(jni);
1509}
1510
1511JNIEXPORT void JNICALL JNIFUNC(setup_1resolver)(JNIEnv *jni, jobject cls)
1512{
1513 lock_tripe(jni);
1514 if (ensure_state(jni, RESOLVE)) goto end;
1515
1516 if (a_init())
1517 { except(jni, INITERR, "failed to initialize resolver"); return; }
1518
1519 state++;
1520
1521end:
1522 unlock_tripe(jni);
1523}
1524
1525JNIEXPORT void JNICALL JNIFUNC(load_1keys)(JNIEnv *jni, jobject cls,
1526 jobject privstr, jobject pubstr,
1527 jobject tagstr)
1528{
1529 const char *priv = 0, *pub = 0, *tag = 0;
1530
1531 lock_tripe(jni);
1532 if (ensure_state(jni, KEYS)) return;
1533
1534 priv = get_cstring(jni, privstr); if (!priv) goto end;
1535 pub = get_cstring(jni, pubstr); if (!pub) goto end;
1536 tag = get_cstring(jni, tagstr); if (!tag) goto end;
1537
1538 if (km_init(priv, pub, tag))
1539 { except(jni, INITERR, "failed to load initial keys"); goto end; }
1540
1541 state++;
1542
1543end:
1544 put_cstring(jni, privstr, priv);
1545 put_cstring(jni, pubstr, pub);
1546 put_cstring(jni, tagstr, tag);
1547 unlock_tripe(jni);
1548}
1549
1550JNIEXPORT void JNICALL JNIFUNC(unload_1keys)(JNIEnv *jni, jobject cls)
1551{
1552 lock_tripe(jni);
1553 if (ensure_state(jni, KEYS + 1)) goto end;
1554
1555 km_clear();
1556
1557 state--;
1558
1559end:
1560 unlock_tripe(jni);
1561}
1562
1563JNIEXPORT void JNICALL JNIFUNC(bind)(JNIEnv *jni, jobject cls,
1564 jbyteArray hoststr, jbyteArray svcstr)
1565{
1566 const char *host = 0, *svc = 0;
1567 struct addrinfo hint, *ai = 0;
1568 int err;
1569
1570 lock_tripe(jni);
1571 if (ensure_state(jni, BIND)) goto end;
1572
1573 if (hoststr) { host = get_cstring(jni, hoststr); if (!host) goto end; }
1574 svc = get_cstring(jni, svcstr); if (!svc) goto end;
1575
1576 hint.ai_socktype = SOCK_DGRAM;
1577 hint.ai_family = AF_UNSPEC;
1578 hint.ai_protocol = IPPROTO_UDP;
1579 hint.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
1580 err = getaddrinfo(host, svc, &hint, &ai);
1581 if (err) {
1582 except(jni, NAMEERR, "failed to resolve %c%s%c, port `%s': %s",
1583 host ? '`' : '<', host ? host : "nil", host ? '\'' : '>',
1584 svc, gai_strerror(err));
1585 goto end;
1586 }
1587
1588 if (p_bind(ai))
1589 { except(jni, INITERR, "failed to bind master socket"); goto end; }
1590
1591 state++;
1592
1593end:
1594 if (ai) freeaddrinfo(ai);
1595 put_cstring(jni, hoststr, host);
1596 put_cstring(jni, svcstr, svc);
1597 unlock_tripe(jni);
1598}
1599
1600JNIEXPORT void JNICALL JNIFUNC(unbind)(JNIEnv *jni, jobject cls)
1601{
1602 lock_tripe(jni);
1603 if (ensure_state(jni, BIND + 1)) goto end;
1604
1605 p_unbind();
1606
1607 state--;
1608
1609end:
1610 unlock_tripe(jni);
1611}
1612
1613JNIEXPORT void JNICALL JNIFUNC(mark)(JNIEnv *jni, jobject cls, jint seq)
1614{
1615 lock_tripe(jni);
1616 a_notify("MARK", "%d", seq, A_END);
1617 unlock_tripe(jni);
1618}
1619
1620JNIEXPORT void JNICALL JNIFUNC(run)(JNIEnv *jni, jobject cls)
1621{
1622 lock_tripe(jni);
1623 if (ensure_state(jni, READY)) goto end;
1624 assert(!jni_tripe);
1625 jni_tripe = jni;
1626 state = RUNNING;
1627 unlock_tripe(jni);
1628
1629 lp_run();
1630
1631 lock_tripe(jni);
1632 jni_tripe = 0;
1633 state = READY;
1634
1635end:
1636 unlock_tripe(jni);
1637}
1638
8eabb4ff
MW
1639static int check_buffer_bounds(JNIEnv *jni, const char *what,
1640 jbyteArray buf, jint start, jint len)
3a2f1a4b 1641{
3a2f1a4b 1642 jsize bufsz;
8eabb4ff 1643 jclass cls;
3a2f1a4b 1644
8eabb4ff
MW
1645 cls = (*jni)->FindClass(jni, "[B"); assert(cls);
1646 if (!(*jni)->IsInstanceOf(jni, buf, cls)) {
1647 except(jni, ARGERR,
1648 "expected a byte array");
1649 return (-1);
1650 }
3a2f1a4b 1651 bufsz = (*jni)->GetArrayLength(jni, buf);
8eabb4ff
MW
1652 if (start > bufsz) {
1653 except(jni, BOUNDSERR,
1654 "bad %s buffer bounds: start %d > buffer size %d", start, bufsz);
1655 return (-1);
1656 }
1657 if (len > bufsz - start) {
1658 except(jni, BOUNDSERR,
1659 "bad %s buffer bounds: length %d > remaining buffer size %d",
1660 len, bufsz - start);
1661 return (-1);
3a2f1a4b 1662 }
8eabb4ff
MW
1663 return (0);
1664}
1665
1666JNIEXPORT void JNICALL JNIFUNC(send)(JNIEnv *jni, jobject cls,
3bb2303d 1667 jbyteArray buf,
04a5abae
MW
1668 jint start, jint len,
1669 wrapper wtrig)
8eabb4ff 1670{
04a5abae
MW
1671 struct trigger trig;
1672 int rc, maxfd;
8eabb4ff 1673 ssize_t n;
04a5abae 1674 fd_set rfds, wfds;
8eabb4ff
MW
1675 jbyte *p = 0;
1676
3bb2303d
MW
1677 if (ensure_state(jni, RUNNING)) goto end;
1678
04a5abae 1679 if (unwrap(jni, &trig, &trigger_type, wtrig)) goto end;
8eabb4ff 1680 if (check_buffer_bounds(jni, "send", buf, start, len)) goto end;
3a2f1a4b 1681
8eabb4ff 1682 p = (*jni)->GetByteArrayElements(jni, buf, 0);
3a2f1a4b
MW
1683 if (!p) goto end;
1684
04a5abae 1685 maxfd = trig.rfd;
3bb2303d 1686 if (maxfd < clientsk) maxfd = clientsk;
3a2f1a4b 1687 while (len) {
04a5abae 1688 FD_ZERO(&rfds); FD_SET(trig.rfd, &rfds);
3bb2303d 1689 FD_ZERO(&wfds); FD_SET(clientsk, &wfds);
04a5abae
MW
1690 rc = select(maxfd + 1, &rfds, &wfds, 0, 0); if (rc < 0) goto err;
1691 if (FD_ISSET(trig.rfd, &rfds)) break;
3bb2303d
MW
1692 if (FD_ISSET(clientsk, &wfds)) {
1693 n = send(clientsk, p + start, len, 0);
04a5abae
MW
1694 if (n >= 0) { start += n; len -= n; }
1695 else if (errno != EAGAIN && errno != EWOULDBLOCK) goto err;
3a2f1a4b 1696 }
3a2f1a4b 1697 }
04a5abae 1698 goto end;
3a2f1a4b 1699
04a5abae
MW
1700err:
1701 except_syserror(jni, SYSERR, errno, "failed to send on connection");
3a2f1a4b
MW
1702end:
1703 if (p) (*jni)->ReleaseByteArrayElements(jni, buf, p, JNI_ABORT);
3a2f1a4b
MW
1704 return;
1705}
1706
8eabb4ff 1707JNIEXPORT jint JNICALL JNIFUNC(recv)(JNIEnv *jni, jobject cls,
3bb2303d 1708 jbyteArray buf,
04a5abae
MW
1709 jint start, jint len,
1710 wrapper wtrig)
3a2f1a4b 1711{
04a5abae
MW
1712 struct trigger trig;
1713 int maxfd;
1714 fd_set rfds;
3a2f1a4b
MW
1715 jbyte *p = 0;
1716 jint rc = -1;
1717
3bb2303d
MW
1718 lock_tripe(jni);
1719 if (clientsk == -1) {
1720 except(jni, STERR, "client connection not established");
1721 unlock_tripe(jni);
1722 goto end;
1723 }
1724 unlock_tripe(jni);
1725
04a5abae 1726 if (unwrap(jni, &trig, &trigger_type, wtrig)) goto end;
8eabb4ff 1727 if (check_buffer_bounds(jni, "send", buf, start, len)) goto end;
3a2f1a4b 1728
8eabb4ff 1729 p = (*jni)->GetByteArrayElements(jni, buf, 0);
3a2f1a4b
MW
1730 if (!p) goto end;
1731
04a5abae 1732 maxfd = trig.rfd;
3bb2303d 1733 if (maxfd < clientsk) maxfd = clientsk;
04a5abae 1734 for (;;) {
3bb2303d 1735 FD_ZERO(&rfds); FD_SET(trig.rfd, &rfds); FD_SET(clientsk, &rfds);
04a5abae
MW
1736 rc = select(maxfd + 1, &rfds, 0, 0, 0); if (rc < 0) goto err;
1737 if (FD_ISSET(trig.rfd, &rfds)) {
1738 break;
1739 }
3bb2303d
MW
1740 if (FD_ISSET(clientsk, &rfds)) {
1741 rc = recv(clientsk, p + start, len, 0);
04a5abae
MW
1742 if (rc >= 0) break;
1743 else if (errno != EAGAIN && errno != EWOULDBLOCK) goto err;
1744 }
3a2f1a4b
MW
1745 }
1746 if (!rc) rc = -1;
04a5abae 1747 goto end;
3a2f1a4b 1748
04a5abae
MW
1749err:
1750 except_syserror(jni, SYSERR, errno, "failed to read from connection");
3a2f1a4b
MW
1751end:
1752 if (p) (*jni)->ReleaseByteArrayElements(jni, buf, p, 0);
3a2f1a4b
MW
1753 return (rc);
1754}
1755
8eabb4ff 1756/*----- That's all, folks -------------------------------------------------*/