chiark / gitweb /
9aa79f8cb6e7c2a5e74a55a3ebe74e3cf312bfa1
[tripe-android] / jni.c
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
30 #include <assert.h>
31 #include <ctype.h>
32 #include <errno.h>
33 #include <inttypes.h>
34 #include <stdarg.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38
39 #include <jni.h>
40
41 #include <sys/types.h>
42 #include <sys/select.h>
43 #include <sys/socket.h>
44 #include <sys/stat.h>
45 #include <sys/sysmacros.h>
46 #include <sys/un.h>
47 #include <fcntl.h>
48 #include <unistd.h>
49 #include <dirent.h>
50
51 #include <mLib/align.h>
52 #include <mLib/bits.h>
53 #include <mLib/dstr.h>
54 #include <mLib/macros.h>
55
56 #include <catacomb/ghash.h>
57
58 #undef sun
59
60 /*----- Magic class names and similar -------------------------------------*/
61
62 /* The name decoration is horrific.  Hide it. */
63 #define JNIFUNC(f) Java_uk_org_distorted_tripe_sys_package_00024_##f
64
65 /* The little class for bundling up error codes. */
66 #define ERRENTRY "uk/org/distorted/tripe/sys/package$ErrorEntry"
67
68 /* The `stat' class. */
69 #define STAT "uk/org/distorted/tripe/sys/package$FileInfo"
70
71 /* Exception class names. */
72 #define NULLERR "java/lang/NullPointerException"
73 #define TYPEERR "uk/org/distorted/tripe/sys/package$NativeObjectTypeException"
74 #define SYSERR "uk/org/distorted/tripe/sys/package$SystemError"
75 #define ARGERR "java/lang/IllegalArgumentException"
76 #define BOUNDSERR "java/lang/IndexOutOfBoundsException"
77
78 /*----- Miscellaneous utilities -------------------------------------------*/
79
80 static void put_cstring(JNIEnv *jni, jbyteArray v, const char *p)
81   { if (p) (*jni)->ReleaseByteArrayElements(jni, v, (jbyte *)p, JNI_ABORT); }
82
83 static void vexcept(JNIEnv *jni, const char *clsname,
84                     const char *msg, va_list *ap)
85 {
86   jclass cls;
87   int rc;
88   dstr d = DSTR_INIT;
89
90   cls = (*jni)->FindClass(jni, clsname); assert(cls);
91   if (!msg)
92     rc = (*jni)->ThrowNew(jni, cls, 0);
93   else {
94     dstr_vputf(&d, msg, ap);
95     rc = (*jni)->ThrowNew(jni, cls, d.buf);
96     assert(!rc);
97     dstr_destroy(&d);
98   }
99   assert(!rc);
100 }
101
102 static void except(JNIEnv *jni, const char *clsname, const char *msg, ...)
103 {
104   va_list ap;
105
106   va_start(ap, msg);
107   vexcept(jni, clsname, msg, &ap);
108   va_end(ap);
109 }
110
111 #ifdef DEBUG
112 static void dump_bytes(const void *p, size_t n, size_t o)
113 {
114   const unsigned char *q = p;
115   size_t i;
116
117   if (!n) return;
118   for (;;) {
119     fprintf(stderr, ";;   %08zx\n", o);
120     for (i = 0; i < 8; i++)
121       if (i < n) fprintf(stderr, "%02x ", q[i]);
122       else fprintf(stderr, "** ");
123     fprintf(stderr, ": ");
124     for (i = 0; i < 8; i++)
125       fputc(i >= n ? '*' : isprint(q[i]) ? q[i] : '.', stderr);
126     fputc('\n', stderr);
127     if (n <= 8) break;
128     q += 8; n -= 8;
129   }
130 }
131
132 static void dump_byte_array(JNIEnv *jni, const char *what, jbyteArray v)
133 {
134   jsize n;
135   jbyte *p;
136
137   fprintf(stderr, ";; %s\n", what);
138   if (!v) { fprintf(stderr, ";;   <null>\n"); return; }
139   n = (*jni)->GetArrayLength(jni, v);
140   p = (*jni)->GetByteArrayElements(jni, v, 0);
141   dump_bytes(p, n, 0);
142   (*jni)->ReleaseByteArrayElements(jni, v, p, JNI_ABORT);
143 }
144 #endif
145
146 static jbyteArray wrap_cstring(JNIEnv *jni, const char *p)
147 {
148   size_t n;
149   jbyteArray v;
150   jbyte *q;
151
152   if (!p) return (0);
153   n = strlen(p) + 1;
154   v = (*jni)->NewByteArray(jni, n); if (!v) return (0);
155   q = (*jni)->GetByteArrayElements(jni, v, 0); if (!q) return (0);
156   memcpy(q, p, n);
157   (*jni)->ReleaseByteArrayElements(jni, v, q, 0);
158   return (v);
159 }
160
161 static const char *get_cstring(JNIEnv *jni, jbyteArray v)
162 {
163   if (!v) { except(jni, NULLERR, 0); return (0); }
164   return ((const char *)(*jni)->GetByteArrayElements(jni, v, 0));
165 }
166
167 static void vexcept_syserror(JNIEnv *jni, const char *clsname,
168                              int err, const char *msg, va_list *ap)
169 {
170   jclass cls;
171   int rc;
172   dstr d = DSTR_INIT;
173   jbyteArray msgstr;
174   jthrowable e;
175   jmethodID init;
176
177   cls = (*jni)->FindClass(jni, clsname); assert(cls);
178   init = (*jni)->GetMethodID(jni, cls, "<init>", "(I[B)V"); assert(init);
179   dstr_vputf(&d, msg, ap);
180   msgstr = wrap_cstring(jni, d.buf); assert(msgstr);
181   dstr_destroy(&d);
182   e = (*jni)->NewObject(jni, cls, init, err, msgstr); assert(e);
183   rc = (*jni)->Throw(jni, e); assert(!rc);
184 }
185
186 static void except_syserror(JNIEnv *jni, const char *clsname,
187                             int err, const char *msg, ...)
188 {
189   va_list ap;
190
191   va_start(ap, msg);
192   vexcept_syserror(jni, clsname, err, msg, &ap);
193   va_end(ap);
194 }
195
196 static int set_nonblocking(JNIEnv *jni, int fd, int nb)
197 {
198   int f0 = fcntl(fd, F_GETFL), f1;
199   if (f0 < 0) goto err;
200   if (nb) f1 = f0 | O_NONBLOCK;
201   else f1 = f0&~O_NONBLOCK;
202   if (fcntl(fd, F_SETFL, f1)) goto err;
203   return (f0 & O_NONBLOCK);
204 err:
205     except_syserror(jni, SYSERR, errno,
206                     "failed to set descriptor nonblocking");
207     return (-1);
208 }
209
210 static int set_closeonexec(JNIEnv *jni, int fd)
211 {
212   int f = fcntl(fd, F_GETFD);
213   if (f < 0 || fcntl(fd, F_SETFD, f | FD_CLOEXEC)) {
214     except_syserror(jni, SYSERR, errno,
215                     "failed to set descriptor close-on-exec");
216     return (-1);
217   }
218   return (0);
219 }
220
221 /*----- Wrapping native types ---------------------------------------------*/
222
223 /* There's no way defined in the JNI to stash a C pointer in a Java object.
224  * It seems that the usual approach is to cast to `jlong', but this is
225  * clearly unsatisfactory.  Instead, we store structures as Java byte arrays,
226  * with a 32-bit tag on the front.
227  */
228
229 struct native_type {
230   const char *name;
231   size_t sz;
232   uint32 tag;
233 };
234
235 typedef jbyteArray wrapper;
236
237 struct native_base {
238   uint32 tag;
239 };
240
241 static int unwrap(JNIEnv *jni, void *p,
242                   const struct native_type *ty, wrapper w)
243 {
244   jbyte *q;
245   jclass cls;
246   struct native_base *b = p;
247   jsize n;
248
249   if (!w) { except(jni, NULLERR, 0); return (-1); }
250   cls = (*jni)->FindClass(jni, "[B"); assert(cls);
251   if (!(*jni)->IsInstanceOf(jni, w, cls)) {
252     except(jni, TYPEERR,
253            "corrupted native object wrapper: expected a byte array");
254     return (-1);
255   }
256   n = (*jni)->GetArrayLength(jni, w);
257   if (n != ty->sz) {
258     except(jni, TYPEERR,
259            "corrupted native object wrapper: wrong size for `%s'",
260            ty->name);
261     return (-1);
262   }
263   q = (*jni)->GetByteArrayElements(jni, w, 0); if (!q) return (-1);
264   memcpy(b, q, ty->sz);
265   (*jni)->ReleaseByteArrayElements(jni, w, q, JNI_ABORT);
266   if (b->tag != ty->tag) {
267     except(jni, TYPEERR,
268            "corrupted native object wrapper: expected tag for `%s'",
269            ty->name);
270     return (-1);
271   }
272   return (0);
273 }
274
275 static int update_wrapper(JNIEnv *jni, const struct native_type *ty,
276                           wrapper w, const void *p)
277 {
278   jbyte *q;
279
280   q = (*jni)->GetByteArrayElements(jni, w, 0); if (!q) return (-1);
281   memcpy(q, p, ty->sz);
282   (*jni)->ReleaseByteArrayElements(jni, w, q, 0);
283   return (0);
284 }
285
286 static wrapper wrap(JNIEnv *jni, const struct native_type *ty, const void *p)
287 {
288   wrapper w;
289
290   w = (*jni)->NewByteArray(jni, ty->sz); if (!w) return (0);
291   if (update_wrapper(jni, ty, w, p)) return (0);
292   return (w);
293 }
294
295 #define INIT_NATIVE(type, p) do (p)->_base.tag = type##_type.tag; while (0)
296
297 /*----- Crypto information ------------------------------------------------*/
298
299 JNIEXPORT jint JNICALL JNIFUNC(hashsz)(JNIEnv *jni, jobject cls,
300                                        jstring hnamestr)
301 {
302   jint rc = -1;
303   const char *hname;
304   const gchash *hc;
305
306   hname = (*jni)->GetStringUTFChars(jni, hnamestr, 0);
307   if (!hname) goto end;
308   hc = ghash_byname(hname); if (!hc) goto end;
309   rc = hc->hashsz;
310
311 end:
312   if (hname) (*jni)->ReleaseStringUTFChars(jni, hnamestr, hname);
313   return (rc);
314 }
315
316 /*----- System errors -----------------------------------------------------*/
317
318 static const struct errtab { const char *tag; int err; } errtab[] = {
319   /*
320      ;;; The errno name table is very boring to type.  To make life less
321      ;;; awful, put the errno names in this list and evaluate the code to
322      ;;; get Emacs to regenerate it.
323
324      (let ((errors '(EPERM ENOENT ESRCH EINTR EIO ENXIO E2BIG ENOEXEC EBADF
325                      ECHILD EAGAIN ENOMEM EACCES EFAULT ENOTBLK EBUSY EEXIST
326                      EXDEV ENODEV ENOTDIR EISDIR EINVAL ENFILE EMFILE ENOTTY
327                      ETXTBSY EFBIG ENOSPC ESPIPE EROFS EMLINK EPIPE EDOM
328                      ERANGE
329
330                      EDEADLK ENAMETOOLONG ENOLCK ENOSYS ENOTEMPTY ELOOP
331                      EWOULDBLOCK ENOMSG EIDRM ECHRNG EL2NSYNC EL3HLT EL3RST
332                      ELNRNG EUNATCH ENOCSI EL2HLT EBADE EBADR EXFULL ENOANO
333                      EBADRQC EBADSLT EDEADLOCK EBFONT ENOSTR ENODATA ETIME
334                      ENOSR ENONET ENOPKG EREMOTE ENOLINK EADV ESRMNT ECOMM
335                      EPROTO EMULTIHOP EDOTDOT EBADMSG EOVERFLOW ENOTUNIQ
336                      EBADFD EREMCHG ELIBACC ELIBBAD ELIBSCN ELIBMAX ELIBEXEC
337                      EILSEQ ERESTART ESTRPIPE EUSERS ENOTSOCK EDESTADDRREQ
338                      EMSGSIZE EPROTOTYPE ENOPROTOOPT EPROTONOSUPPORT
339                      ESOCKTNOSUPPORT EOPNOTSUPP EPFNOSUPPORT EAFNOSUPPORT
340                      EADDRINUSE EADDRNOTAVAIL ENETDOWN ENETUNREACH ENETRESET
341                      ECONNABORTED ECONNRESET ENOBUFS EISCONN ENOTCONN
342                      ESHUTDOWN ETOOMANYREFS ETIMEDOUT ECONNREFUSED EHOSTDOWN
343                      EHOSTUNREACH EALREADY EINPROGRESS ESTALE EUCLEAN ENOTNAM
344                      ENAVAIL EISNAM EREMOTEIO EDQUOT ENOMEDIUM EMEDIUMTYPE
345                      ECANCELED ENOKEY EKEYEXPIRED EKEYREVOKED EKEYREJECTED
346                      EOWNERDEAD ENOTRECOVERABLE ERFKILL EHWPOISON)))
347        (save-excursion
348          (goto-char (point-min))
349          (search-forward (concat "***" "BEGIN errtab" "***"))
350          (beginning-of-line 2)
351          (delete-region (point)
352                         (progn
353                           (search-forward "***END***")
354                           (beginning-of-line)
355                           (point)))
356          (dolist (err errors)
357            (insert (format "#ifdef %s\n  { \"%s\", %s },\n#endif\n"
358                            err err err)))))
359   */
360   /***BEGIN errtab***/
361 #ifdef EPERM
362   { "EPERM", EPERM },
363 #endif
364 #ifdef ENOENT
365   { "ENOENT", ENOENT },
366 #endif
367 #ifdef ESRCH
368   { "ESRCH", ESRCH },
369 #endif
370 #ifdef EINTR
371   { "EINTR", EINTR },
372 #endif
373 #ifdef EIO
374   { "EIO", EIO },
375 #endif
376 #ifdef ENXIO
377   { "ENXIO", ENXIO },
378 #endif
379 #ifdef E2BIG
380   { "E2BIG", E2BIG },
381 #endif
382 #ifdef ENOEXEC
383   { "ENOEXEC", ENOEXEC },
384 #endif
385 #ifdef EBADF
386   { "EBADF", EBADF },
387 #endif
388 #ifdef ECHILD
389   { "ECHILD", ECHILD },
390 #endif
391 #ifdef EAGAIN
392   { "EAGAIN", EAGAIN },
393 #endif
394 #ifdef ENOMEM
395   { "ENOMEM", ENOMEM },
396 #endif
397 #ifdef EACCES
398   { "EACCES", EACCES },
399 #endif
400 #ifdef EFAULT
401   { "EFAULT", EFAULT },
402 #endif
403 #ifdef ENOTBLK
404   { "ENOTBLK", ENOTBLK },
405 #endif
406 #ifdef EBUSY
407   { "EBUSY", EBUSY },
408 #endif
409 #ifdef EEXIST
410   { "EEXIST", EEXIST },
411 #endif
412 #ifdef EXDEV
413   { "EXDEV", EXDEV },
414 #endif
415 #ifdef ENODEV
416   { "ENODEV", ENODEV },
417 #endif
418 #ifdef ENOTDIR
419   { "ENOTDIR", ENOTDIR },
420 #endif
421 #ifdef EISDIR
422   { "EISDIR", EISDIR },
423 #endif
424 #ifdef EINVAL
425   { "EINVAL", EINVAL },
426 #endif
427 #ifdef ENFILE
428   { "ENFILE", ENFILE },
429 #endif
430 #ifdef EMFILE
431   { "EMFILE", EMFILE },
432 #endif
433 #ifdef ENOTTY
434   { "ENOTTY", ENOTTY },
435 #endif
436 #ifdef ETXTBSY
437   { "ETXTBSY", ETXTBSY },
438 #endif
439 #ifdef EFBIG
440   { "EFBIG", EFBIG },
441 #endif
442 #ifdef ENOSPC
443   { "ENOSPC", ENOSPC },
444 #endif
445 #ifdef ESPIPE
446   { "ESPIPE", ESPIPE },
447 #endif
448 #ifdef EROFS
449   { "EROFS", EROFS },
450 #endif
451 #ifdef EMLINK
452   { "EMLINK", EMLINK },
453 #endif
454 #ifdef EPIPE
455   { "EPIPE", EPIPE },
456 #endif
457 #ifdef EDOM
458   { "EDOM", EDOM },
459 #endif
460 #ifdef ERANGE
461   { "ERANGE", ERANGE },
462 #endif
463 #ifdef EDEADLK
464   { "EDEADLK", EDEADLK },
465 #endif
466 #ifdef ENAMETOOLONG
467   { "ENAMETOOLONG", ENAMETOOLONG },
468 #endif
469 #ifdef ENOLCK
470   { "ENOLCK", ENOLCK },
471 #endif
472 #ifdef ENOSYS
473   { "ENOSYS", ENOSYS },
474 #endif
475 #ifdef ENOTEMPTY
476   { "ENOTEMPTY", ENOTEMPTY },
477 #endif
478 #ifdef ELOOP
479   { "ELOOP", ELOOP },
480 #endif
481 #ifdef EWOULDBLOCK
482   { "EWOULDBLOCK", EWOULDBLOCK },
483 #endif
484 #ifdef ENOMSG
485   { "ENOMSG", ENOMSG },
486 #endif
487 #ifdef EIDRM
488   { "EIDRM", EIDRM },
489 #endif
490 #ifdef ECHRNG
491   { "ECHRNG", ECHRNG },
492 #endif
493 #ifdef EL2NSYNC
494   { "EL2NSYNC", EL2NSYNC },
495 #endif
496 #ifdef EL3HLT
497   { "EL3HLT", EL3HLT },
498 #endif
499 #ifdef EL3RST
500   { "EL3RST", EL3RST },
501 #endif
502 #ifdef ELNRNG
503   { "ELNRNG", ELNRNG },
504 #endif
505 #ifdef EUNATCH
506   { "EUNATCH", EUNATCH },
507 #endif
508 #ifdef ENOCSI
509   { "ENOCSI", ENOCSI },
510 #endif
511 #ifdef EL2HLT
512   { "EL2HLT", EL2HLT },
513 #endif
514 #ifdef EBADE
515   { "EBADE", EBADE },
516 #endif
517 #ifdef EBADR
518   { "EBADR", EBADR },
519 #endif
520 #ifdef EXFULL
521   { "EXFULL", EXFULL },
522 #endif
523 #ifdef ENOANO
524   { "ENOANO", ENOANO },
525 #endif
526 #ifdef EBADRQC
527   { "EBADRQC", EBADRQC },
528 #endif
529 #ifdef EBADSLT
530   { "EBADSLT", EBADSLT },
531 #endif
532 #ifdef EDEADLOCK
533   { "EDEADLOCK", EDEADLOCK },
534 #endif
535 #ifdef EBFONT
536   { "EBFONT", EBFONT },
537 #endif
538 #ifdef ENOSTR
539   { "ENOSTR", ENOSTR },
540 #endif
541 #ifdef ENODATA
542   { "ENODATA", ENODATA },
543 #endif
544 #ifdef ETIME
545   { "ETIME", ETIME },
546 #endif
547 #ifdef ENOSR
548   { "ENOSR", ENOSR },
549 #endif
550 #ifdef ENONET
551   { "ENONET", ENONET },
552 #endif
553 #ifdef ENOPKG
554   { "ENOPKG", ENOPKG },
555 #endif
556 #ifdef EREMOTE
557   { "EREMOTE", EREMOTE },
558 #endif
559 #ifdef ENOLINK
560   { "ENOLINK", ENOLINK },
561 #endif
562 #ifdef EADV
563   { "EADV", EADV },
564 #endif
565 #ifdef ESRMNT
566   { "ESRMNT", ESRMNT },
567 #endif
568 #ifdef ECOMM
569   { "ECOMM", ECOMM },
570 #endif
571 #ifdef EPROTO
572   { "EPROTO", EPROTO },
573 #endif
574 #ifdef EMULTIHOP
575   { "EMULTIHOP", EMULTIHOP },
576 #endif
577 #ifdef EDOTDOT
578   { "EDOTDOT", EDOTDOT },
579 #endif
580 #ifdef EBADMSG
581   { "EBADMSG", EBADMSG },
582 #endif
583 #ifdef EOVERFLOW
584   { "EOVERFLOW", EOVERFLOW },
585 #endif
586 #ifdef ENOTUNIQ
587   { "ENOTUNIQ", ENOTUNIQ },
588 #endif
589 #ifdef EBADFD
590   { "EBADFD", EBADFD },
591 #endif
592 #ifdef EREMCHG
593   { "EREMCHG", EREMCHG },
594 #endif
595 #ifdef ELIBACC
596   { "ELIBACC", ELIBACC },
597 #endif
598 #ifdef ELIBBAD
599   { "ELIBBAD", ELIBBAD },
600 #endif
601 #ifdef ELIBSCN
602   { "ELIBSCN", ELIBSCN },
603 #endif
604 #ifdef ELIBMAX
605   { "ELIBMAX", ELIBMAX },
606 #endif
607 #ifdef ELIBEXEC
608   { "ELIBEXEC", ELIBEXEC },
609 #endif
610 #ifdef EILSEQ
611   { "EILSEQ", EILSEQ },
612 #endif
613 #ifdef ERESTART
614   { "ERESTART", ERESTART },
615 #endif
616 #ifdef ESTRPIPE
617   { "ESTRPIPE", ESTRPIPE },
618 #endif
619 #ifdef EUSERS
620   { "EUSERS", EUSERS },
621 #endif
622 #ifdef ENOTSOCK
623   { "ENOTSOCK", ENOTSOCK },
624 #endif
625 #ifdef EDESTADDRREQ
626   { "EDESTADDRREQ", EDESTADDRREQ },
627 #endif
628 #ifdef EMSGSIZE
629   { "EMSGSIZE", EMSGSIZE },
630 #endif
631 #ifdef EPROTOTYPE
632   { "EPROTOTYPE", EPROTOTYPE },
633 #endif
634 #ifdef ENOPROTOOPT
635   { "ENOPROTOOPT", ENOPROTOOPT },
636 #endif
637 #ifdef EPROTONOSUPPORT
638   { "EPROTONOSUPPORT", EPROTONOSUPPORT },
639 #endif
640 #ifdef ESOCKTNOSUPPORT
641   { "ESOCKTNOSUPPORT", ESOCKTNOSUPPORT },
642 #endif
643 #ifdef EOPNOTSUPP
644   { "EOPNOTSUPP", EOPNOTSUPP },
645 #endif
646 #ifdef EPFNOSUPPORT
647   { "EPFNOSUPPORT", EPFNOSUPPORT },
648 #endif
649 #ifdef EAFNOSUPPORT
650   { "EAFNOSUPPORT", EAFNOSUPPORT },
651 #endif
652 #ifdef EADDRINUSE
653   { "EADDRINUSE", EADDRINUSE },
654 #endif
655 #ifdef EADDRNOTAVAIL
656   { "EADDRNOTAVAIL", EADDRNOTAVAIL },
657 #endif
658 #ifdef ENETDOWN
659   { "ENETDOWN", ENETDOWN },
660 #endif
661 #ifdef ENETUNREACH
662   { "ENETUNREACH", ENETUNREACH },
663 #endif
664 #ifdef ENETRESET
665   { "ENETRESET", ENETRESET },
666 #endif
667 #ifdef ECONNABORTED
668   { "ECONNABORTED", ECONNABORTED },
669 #endif
670 #ifdef ECONNRESET
671   { "ECONNRESET", ECONNRESET },
672 #endif
673 #ifdef ENOBUFS
674   { "ENOBUFS", ENOBUFS },
675 #endif
676 #ifdef EISCONN
677   { "EISCONN", EISCONN },
678 #endif
679 #ifdef ENOTCONN
680   { "ENOTCONN", ENOTCONN },
681 #endif
682 #ifdef ESHUTDOWN
683   { "ESHUTDOWN", ESHUTDOWN },
684 #endif
685 #ifdef ETOOMANYREFS
686   { "ETOOMANYREFS", ETOOMANYREFS },
687 #endif
688 #ifdef ETIMEDOUT
689   { "ETIMEDOUT", ETIMEDOUT },
690 #endif
691 #ifdef ECONNREFUSED
692   { "ECONNREFUSED", ECONNREFUSED },
693 #endif
694 #ifdef EHOSTDOWN
695   { "EHOSTDOWN", EHOSTDOWN },
696 #endif
697 #ifdef EHOSTUNREACH
698   { "EHOSTUNREACH", EHOSTUNREACH },
699 #endif
700 #ifdef EALREADY
701   { "EALREADY", EALREADY },
702 #endif
703 #ifdef EINPROGRESS
704   { "EINPROGRESS", EINPROGRESS },
705 #endif
706 #ifdef ESTALE
707   { "ESTALE", ESTALE },
708 #endif
709 #ifdef EUCLEAN
710   { "EUCLEAN", EUCLEAN },
711 #endif
712 #ifdef ENOTNAM
713   { "ENOTNAM", ENOTNAM },
714 #endif
715 #ifdef ENAVAIL
716   { "ENAVAIL", ENAVAIL },
717 #endif
718 #ifdef EISNAM
719   { "EISNAM", EISNAM },
720 #endif
721 #ifdef EREMOTEIO
722   { "EREMOTEIO", EREMOTEIO },
723 #endif
724 #ifdef EDQUOT
725   { "EDQUOT", EDQUOT },
726 #endif
727 #ifdef ENOMEDIUM
728   { "ENOMEDIUM", ENOMEDIUM },
729 #endif
730 #ifdef EMEDIUMTYPE
731   { "EMEDIUMTYPE", EMEDIUMTYPE },
732 #endif
733 #ifdef ECANCELED
734   { "ECANCELED", ECANCELED },
735 #endif
736 #ifdef ENOKEY
737   { "ENOKEY", ENOKEY },
738 #endif
739 #ifdef EKEYEXPIRED
740   { "EKEYEXPIRED", EKEYEXPIRED },
741 #endif
742 #ifdef EKEYREVOKED
743   { "EKEYREVOKED", EKEYREVOKED },
744 #endif
745 #ifdef EKEYREJECTED
746   { "EKEYREJECTED", EKEYREJECTED },
747 #endif
748 #ifdef EOWNERDEAD
749   { "EOWNERDEAD", EOWNERDEAD },
750 #endif
751 #ifdef ENOTRECOVERABLE
752   { "ENOTRECOVERABLE", ENOTRECOVERABLE },
753 #endif
754 #ifdef ERFKILL
755   { "ERFKILL", ERFKILL },
756 #endif
757 #ifdef EHWPOISON
758   { "EHWPOISON", EHWPOISON },
759 #endif
760   /***END***/
761 };
762
763 JNIEXPORT jobject JNIFUNC(errtab)(JNIEnv *jni, jobject cls)
764 {
765   size_t i;
766   jclass eltcls;
767   jarray v;
768   jmethodID init;
769   jobject e;
770
771   eltcls =
772     (*jni)->FindClass(jni, ERRENTRY);
773   assert(eltcls);
774   v = (*jni)->NewObjectArray(jni, N(errtab), eltcls, 0); if (!v) return (0);
775   init = (*jni)->GetMethodID(jni, eltcls, "<init>",
776                              "(Ljava/lang/String;I)V");
777   assert(init);
778
779   for (i = 0; i < N(errtab); i++) {
780     e = (*jni)->NewObject(jni, eltcls, init,
781                           (*jni)->NewStringUTF(jni, errtab[i].tag),
782                           errtab[i].err);
783     (*jni)->SetObjectArrayElement(jni, v, i, e);
784   }
785   return (v);
786 }
787
788 JNIEXPORT jobject JNIFUNC(strerror)(JNIEnv *jni, jobject cls, jint err)
789   { return (wrap_cstring(jni, strerror(err))); }
790
791 /*----- Messing with file descriptors -------------------------------------*/
792
793 static void fdguts(JNIEnv *jni, jclass *cls, jfieldID *fid)
794 {
795   *cls = (*jni)->FindClass(jni, "java/io/FileDescriptor"); assert(cls);
796   *fid = (*jni)->GetFieldID(jni, *cls, "fd", "I"); // OpenJDK
797   if (!*fid) *fid = (*jni)->GetFieldID(jni, *cls, "descriptor", "I"); // Android
798   assert(*fid);
799 }
800
801 static int fdint(JNIEnv *jni, jobject jfd)
802 {
803   jclass cls;
804   jfieldID fid;
805
806   fdguts(jni, &cls, &fid);
807   return ((*jni)->GetIntField(jni, jfd, fid));
808 }
809
810 static jobject newfd(JNIEnv *jni, int fd)
811 {
812   jobject jfd;
813   jclass cls;
814   jmethodID init;
815   jfieldID fid;
816
817   fdguts(jni, &cls, &fid);
818   init = (*jni)->GetMethodID(jni, cls, "<init>", "()V"); assert(init);
819   jfd = (*jni)->NewObject(jni, cls, init);
820   (*jni)->SetIntField(jni, jfd, fid, fd);
821   return (jfd);
822 }
823
824 JNIEXPORT jint JNIFUNC(fdint)(JNIEnv *jni, jobject cls, jobject jfd)
825   { return (fdint(jni, jfd)); }
826
827 JNIEXPORT jobject JNIFUNC(newfd)(JNIEnv *jni, jobject cls, jint fd)
828   { return (newfd(jni, fd)); }
829
830 JNIEXPORT jboolean JNIFUNC(isatty)(JNIEnv *jni, jobject cls, jobject jfd)
831   { return (isatty(fdint(jni, jfd))); }
832
833 /*----- Low-level file operations -----------------------------------------*/
834
835 /* Java has these already, as methods on `java.io.File' objects.  Alas, these
836  * methods are useless at reporting errors: they tend to return a `boolean'
837  * success/fail indicator, and throw away any more detailed information.
838  * There's better functionality in `java.nio.file.Files', but that only turns
839  * up in Android API 26 (in 7.0 Nougat).  There's `android.system.Os', which
840  * has a bunch of POSIX-shaped functions -- but they're only in Android API
841  * 21 (in 5.0 Lollipop), and there's nothing in the support library to help.
842  *
843  * So the other option is to implement them ourselves.
844  */
845
846 JNIEXPORT void JNIFUNC(unlink)(JNIEnv *jni, jobject cls, jobject path)
847 {
848   const char *pathstr = 0;
849
850   pathstr = get_cstring(jni, path); if (!pathstr) goto end;
851   if (unlink(pathstr)) {
852     except_syserror(jni, SYSERR, errno,
853                     "failed to delete file `%s'", pathstr);
854     goto end;
855   }
856 end:
857   put_cstring(jni, path, pathstr);
858 }
859
860 JNIEXPORT void JNIFUNC(rmdir)(JNIEnv *jni, jobject cls, jobject path)
861 {
862   const char *pathstr = 0;
863
864   pathstr = get_cstring(jni, path); if (!pathstr) goto end;
865   if (rmdir(pathstr)) {
866     except_syserror(jni, SYSERR, errno,
867                     "failed to delete directory `%s'", pathstr);
868     goto end;
869   }
870 end:
871   put_cstring(jni, path, pathstr);
872 }
873
874 JNIEXPORT void JNIFUNC(mkdir)(JNIEnv *jni, jobject cls,
875                               jobject path, jint mode)
876 {
877   const char *pathstr = 0;
878
879   pathstr = get_cstring(jni, path); if (!pathstr) goto end;
880   if (mkdir(pathstr, mode)) {
881     except_syserror(jni, SYSERR, errno,
882                     "failed to create directory `%s'", pathstr);
883     goto end;
884   }
885 end:
886   put_cstring(jni, path, pathstr);
887 }
888
889 JNIEXPORT void JNIFUNC(mkfile)(JNIEnv *jni, jobject cls,
890                             jobject path, jint mode)
891 {
892   const char *pathstr = 0;
893   int fd = -1;
894
895   pathstr = get_cstring(jni, path); if (!pathstr) goto end;
896   fd = open(pathstr, O_WRONLY | O_CREAT | O_EXCL, mode);
897   if (fd < 0) {
898     except_syserror(jni, SYSERR, errno,
899                     "failed to create fresh file `%s'", pathstr);
900     goto end;
901   }
902 end:
903   if (fd != -1) close(fd);
904   put_cstring(jni, path, pathstr);
905 }
906
907 JNIEXPORT void JNIFUNC(rename)(JNIEnv *jni, jobject cls,
908                                jobject from, jobject to)
909 {
910   const char *fromstr = 0, *tostr = 0;
911
912   fromstr = get_cstring(jni, from); if (!fromstr) goto end;
913   tostr = get_cstring(jni, to); if (!tostr) goto end;
914   if (rename(fromstr, tostr)) {
915     except_syserror(jni, SYSERR, errno,
916                     "failed to rename `%s' as `%s'", fromstr, tostr);
917     goto end;
918   }
919 end:
920   put_cstring(jni, from, fromstr);
921   put_cstring(jni, to, tostr);
922 }
923
924 #define LKF_EXCL 0x1000u
925 #define LKF_WAIT 0x2000u
926 struct lockf {
927   struct native_base _base;
928   int fd;
929 };
930 static struct native_type lockf_type =
931         { "lock", sizeof(struct lockf), 0xb2648926};
932 JNIEXPORT wrapper JNIFUNC(lock)(JNIEnv *jni, jobject cls,
933                                 jobject path, jint flags)
934 {
935   const char *pathstr = 0;
936   int fd = -1;
937   struct flock l;
938   struct lockf lk;
939   struct stat st0, st1;
940   int f;
941   wrapper r = 0;
942
943   pathstr = get_cstring(jni, path); if (!pathstr) goto end;
944
945 again:
946   fd = open(pathstr, O_RDWR | O_CREAT, flags&07777); if (fd < 0) goto err;
947   if (fstat(fd, &st0)) goto err;
948   f = fcntl(fd, F_GETFD); if (f < 0) goto err;
949   if (fcntl(fd, F_SETFD, f | FD_CLOEXEC)) goto err;
950   l.l_type = (flags&LKF_EXCL) ? F_WRLCK : F_RDLCK;
951   l.l_whence = SEEK_SET;
952   l.l_start = 0;
953   l.l_len = 0;
954   if (fcntl(fd, (flags&LKF_WAIT) ? F_SETLKW : F_SETLK, &l)) goto err;
955   if (stat(pathstr, &st1))
956     { if (errno == ENOENT) goto again; else goto err; }
957   if (st0.st_dev != st1.st_dev || st0.st_ino != st1.st_ino)
958     { close(fd); fd = -1; goto again; }
959
960   INIT_NATIVE(lockf, &lk); lk.fd = fd; fd = -1;
961   r = wrap(jni, &lockf_type, &lk);
962   goto end;
963
964 err:
965   except_syserror(jni, SYSERR, errno, "failed to lock file `%s'", pathstr);
966 end:
967   if (fd != -1) close(fd);
968   put_cstring(jni, path, pathstr);
969   return (r);
970 }
971
972 JNIEXPORT void JNIFUNC(unlock)(JNIEnv *jni, jobject cls, wrapper wlk)
973 {
974   struct lockf lk;
975   struct flock l;
976   int rc;
977
978   if (unwrap(jni, &lk, &lockf_type, wlk)) goto end;
979   if (lk.fd == -1) goto end;
980   l.l_type = F_UNLCK;
981   l.l_whence = SEEK_SET;
982   l.l_start = 0;
983   l.l_len = 0;
984   if (fcntl(lk.fd, F_SETLK, &l)) goto end;
985   close(lk.fd); lk.fd = -1;
986   rc = update_wrapper(jni, &lockf_type, wlk, &lk); assert(!rc);
987 end:;
988 }
989
990 static jlong xlttimespec(const struct timespec *ts)
991   { return (1000*(jlong)ts->tv_sec + ts->tv_nsec/1000000); }
992
993 static jobject xltstat(JNIEnv *jni, const struct stat *st)
994 {
995   jclass cls;
996   jmethodID init;
997   jint modehack;
998
999   modehack = st->st_mode&07777;
1000   if (S_ISFIFO(st->st_mode)) modehack |= 0010000;
1001   else if (S_ISCHR(st->st_mode)) modehack |= 0020000;
1002   else if (S_ISDIR(st->st_mode)) modehack |= 0040000;
1003   else if (S_ISBLK(st->st_mode)) modehack |= 0060000;
1004   else if (S_ISREG(st->st_mode)) modehack |= 0100000;
1005   else if (S_ISLNK(st->st_mode)) modehack |= 0120000;
1006   else if (S_ISSOCK(st->st_mode)) modehack |= 0140000;
1007
1008   cls = (*jni)->FindClass(jni, STAT); assert(cls);
1009   init = (*jni)->GetMethodID(jni, cls, "<init>", "(IIJIIIIIIJIJJJJ)V");
1010   assert(init);
1011   return ((*jni)->NewObject(jni, cls, init,
1012                             (jint)major(st->st_dev), (jint)minor(st->st_dev),
1013                             (jlong)st->st_ino,
1014                             modehack,
1015                             (jint)st->st_nlink,
1016                             (jint)st->st_uid, (jint)st->st_gid,
1017                             (jint)major(st->st_rdev), (jint)minor(st->st_rdev),
1018                             (jlong)st->st_size,
1019                             (jint)st->st_blksize, (jlong)st->st_blocks,
1020                             xlttimespec(&st->st_atim),
1021                             xlttimespec(&st->st_mtim),
1022                             xlttimespec(&st->st_ctim)));
1023 }
1024
1025 JNIEXPORT jobject JNIFUNC(stat)(JNIEnv *jni, jobject cls, jobject path)
1026 {
1027   jobject r = 0;
1028   const char *pathstr = 0;
1029   struct stat st;
1030
1031   pathstr = get_cstring(jni, path); if (!pathstr) goto end;
1032   if (stat(pathstr, &st)) {
1033     except_syserror(jni, SYSERR, errno,
1034                     "failed to read information about `%s'", pathstr);
1035     goto end;
1036   }
1037   r = xltstat(jni, &st);
1038 end:
1039   put_cstring(jni, path, pathstr);
1040   return (r);
1041 }
1042
1043 JNIEXPORT jobject JNIFUNC(lstat)(JNIEnv *jni, jobject cls, jobject path)
1044 {
1045   jobject r = 0;
1046   const char *pathstr = 0;
1047   struct stat st;
1048
1049   pathstr = get_cstring(jni, path); if (!pathstr) goto end;
1050   if (lstat(pathstr, &st)) {
1051     except_syserror(jni, SYSERR, errno,
1052                     "failed to read information about `%s'", pathstr);
1053     goto end;
1054   }
1055   r = xltstat(jni, &st);
1056 end:
1057   put_cstring(jni, path, pathstr);
1058   return (r);
1059 }
1060
1061 struct dir {
1062   struct native_base _base;
1063   DIR *d;
1064 };
1065 static const struct native_type dir_type =
1066         { "dir", sizeof(struct dir), 0x0f5ca477 };
1067
1068 JNIEXPORT jobject JNIFUNC(opendir)(JNIEnv *jni, jobject cls, jobject path)
1069 {
1070   const char *pathstr = 0;
1071   struct dir dir;
1072   wrapper r = 0;
1073
1074   pathstr = get_cstring(jni, path); if (!pathstr) goto end;
1075   INIT_NATIVE(dir, &dir);
1076   dir.d = opendir(pathstr);
1077   if (!dir.d) {
1078     except_syserror(jni, SYSERR, errno,
1079                     "failed to open directory `%s'", pathstr);
1080     goto end;
1081   }
1082   r = wrap(jni, &dir_type, &dir);
1083 end:
1084   put_cstring(jni, path, pathstr);
1085   return (r);
1086 }
1087
1088 JNIEXPORT jbyteArray JNIFUNC(readdir)(JNIEnv *jni, jobject cls,
1089                                       jobject path, jobject wdir)
1090 {
1091   const char *pathstr = 0;
1092   struct dir dir;
1093   struct dirent *d;
1094   jbyteArray r = 0;
1095
1096   if (unwrap(jni, &dir, &dir_type, wdir)) goto end;
1097   if (!dir.d) { except(jni, ARGERR, "directory has been closed"); goto end; }
1098   errno = 0; d = readdir(dir.d);
1099   if (errno) {
1100     pathstr = get_cstring(jni, path); if (!pathstr) goto end;
1101     except_syserror(jni, SYSERR, errno,
1102                     "failed to read directory `%s'", pathstr);
1103     goto end;
1104   }
1105   if (d) r = wrap_cstring(jni, d->d_name);
1106 end:
1107   put_cstring(jni, path, pathstr);
1108   return (r);
1109 }
1110
1111 JNIEXPORT void JNIFUNC(closedir)(JNIEnv *jni, jobject cls,
1112                                  jobject path, jobject wdir)
1113 {
1114   const char *pathstr = 0;
1115   struct dir dir;
1116
1117   if (unwrap(jni, &dir, &dir_type, wdir)) goto end;
1118   if (!dir.d) goto end;
1119   if (closedir(dir.d)) {
1120     pathstr = get_cstring(jni, path); if (!pathstr) goto end;
1121     except_syserror(jni, SYSERR, errno,
1122                     "failed to close directory `%s'", pathstr);
1123     goto end;
1124   }
1125   dir.d = 0;
1126   if (update_wrapper(jni, &dir_type, wdir, &dir)) goto end;
1127 end:
1128   put_cstring(jni, path, pathstr);
1129 }
1130
1131 /*----- Triggers ----------------------------------------------------------*/
1132
1133 /* A trigger is a gadget for waking up a thread which is blocking on I/O,
1134  * and it's used to implement interruptability.
1135  *
1136  * Really, a trigger is a pipe.  A `blocking' I/O operation secretly uses
1137  * select(2) to block on the descriptor of interest /and/ the read side of
1138  * the trigger pipe.  To wake up a thread that's blocked, we just write a
1139  * byte (nobody cares /which/ byte) to the write end.
1140  */
1141
1142 struct trigger {
1143   struct native_base _base;
1144   int rfd, wfd;
1145 };
1146 static const struct native_type trigger_type =
1147         { "trigger", sizeof(struct trigger), 0x65ffd8b4 };
1148
1149 JNIEXPORT wrapper JNICALL JNIFUNC(makeTrigger)(JNIEnv *jni, jobject cls)
1150 {
1151   struct trigger trig;
1152   int fd[2];
1153   int i;
1154   wrapper ret = 0;
1155
1156   fd[0] = fd[1] = -1;
1157   if (pipe(fd)) {
1158     except_syserror(jni, SYSERR, errno, "failed to create pipe");
1159     goto end;
1160   }
1161   for (i = 0; i < 2; i++) {
1162     if (set_nonblocking(jni, fd[i], 1) < 0 || set_closeonexec(jni, fd[i]))
1163       goto end;
1164   }
1165
1166   INIT_NATIVE(trigger, &trig);
1167   trig.rfd = fd[0]; fd[0] = -1;
1168   trig.wfd = fd[1]; fd[1] = -1;
1169   ret = wrap(jni, &trigger_type, &trig);
1170
1171 end:
1172   for (i = 0; i < 2; i++)
1173     if (fd[i] != -1) close(fd[i]);
1174   return (ret);
1175 }
1176
1177 JNIEXPORT void JNICALL JNIFUNC(destroyTrigger)(JNIEnv *jni, jobject cls,
1178                                                wrapper wtrig)
1179 {
1180   struct trigger trig;
1181
1182   if (unwrap(jni, &trig, &trigger_type, wtrig)) return;
1183   if (trig.rfd != -1) { close(trig.rfd); trig.rfd = -1; }
1184   if (trig.wfd != -1) { close(trig.wfd); trig.wfd = -1; }
1185   update_wrapper(jni, &trigger_type, wtrig, &trig);
1186 }
1187
1188 JNIEXPORT void JNICALL JNIFUNC(resetTrigger)(JNIEnv *jni, jobject cls,
1189                                              wrapper wtrig)
1190 {
1191   struct trigger trig;
1192   char buf[64];
1193   ssize_t n;
1194
1195   if (unwrap(jni, &trig, &trigger_type, wtrig)) return;
1196   for (;;) {
1197     n = read(trig.rfd, buf, sizeof(buf));
1198     if (n > 0) continue;
1199     assert(n < 0);
1200     if (errno == EAGAIN || errno == EWOULDBLOCK) break;
1201     else {
1202       except_syserror(jni, SYSERR, errno, "failed to reset trigger");
1203       break;
1204     }
1205   }
1206 }
1207
1208 JNIEXPORT void JNICALL JNIFUNC(trigger)(JNIEnv *jni, jobject cls,
1209                                         wrapper wtrig)
1210 {
1211   struct trigger trig;
1212   ssize_t n;
1213   char c = 0;
1214
1215   if (unwrap(jni, &trig, &trigger_type, wtrig)) return;
1216   n = write(trig.wfd, &c, 1);
1217   if (n < 0 && errno != EAGAIN && errno != EWOULDBLOCK)
1218     except_syserror(jni, SYSERR, errno, "failed to pull trigger");
1219 }
1220
1221 /*----- A server connection, using a Unix-domain socket -------------------*/
1222
1223 struct conn {
1224   struct native_base _base;
1225   int fd;
1226   unsigned f;
1227 #define CF_CLOSERD 1u
1228 #define CF_CLOSEWR 2u
1229 #define CF_CLOSEMASK (CF_CLOSERD | CF_CLOSEWR)
1230 };
1231 static const struct native_type conn_type =
1232         { "conn", sizeof(struct conn), 0xed030167 };
1233
1234 JNIEXPORT wrapper JNICALL JNIFUNC(connect)(JNIEnv *jni, jobject cls,
1235                                            jobject path, wrapper wtrig)
1236 {
1237   struct conn conn;
1238   struct trigger trig;
1239   struct sockaddr_un sun;
1240   int rc, maxfd;
1241   fd_set rfds, wfds;
1242   const char *pathstr = 0;
1243   int err;
1244   socklen_t sz;
1245   wrapper ret = 0;
1246   int nb;
1247   int fd = -1;
1248
1249   if (unwrap(jni, &trig, &trigger_type, wtrig)) goto end;
1250   pathstr = get_cstring(jni, path); if (!pathstr) goto end;
1251   if (strlen(pathstr) >= sizeof(sun.sun_path)) {
1252     except(jni, ARGERR,
1253            "Unix-domain socket path `%s' too long", pathstr);
1254     goto end;
1255   }
1256
1257   INIT_NATIVE(conn, &conn);
1258   fd = socket(PF_UNIX, SOCK_STREAM, 0); if (fd < 0) goto err;
1259   nb = set_nonblocking(jni, fd, 1); if (nb < 0) goto end;
1260
1261   sun.sun_family = AF_UNIX;
1262   strcpy(sun.sun_path, (char *)pathstr);
1263   if (!connect(fd, (struct sockaddr *)&sun, sizeof(sun))) goto connected;
1264   else if (errno != EINPROGRESS) goto err;
1265
1266   maxfd = trig.rfd;
1267   if (maxfd < fd) maxfd = fd;
1268   for (;;) {
1269     FD_ZERO(&rfds); FD_SET(trig.rfd, &rfds);
1270     FD_ZERO(&wfds); FD_SET(fd, &wfds);
1271     rc = select(maxfd + 1, &rfds, &wfds, 0, 0); if (rc < 0) goto err;
1272     if (FD_ISSET(trig.rfd, &rfds)) goto end;
1273     if (FD_ISSET(fd, &wfds)) {
1274       sz = sizeof(sun);
1275       if (!getpeername(fd, (struct sockaddr *)&sun, &sz)) goto connected;
1276       else if (errno != ENOTCONN) goto err;
1277       sz = sizeof(err);
1278       if (!getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &sz)) errno = err;
1279       goto err;
1280     }
1281   }
1282
1283 connected:
1284   if (set_nonblocking(jni, fd, nb) < 0) goto end;
1285   conn.fd = fd; fd = -1;
1286   conn.f = 0;
1287   ret = wrap(jni, &conn_type, &conn);
1288   goto end;
1289
1290 err:
1291   except_syserror(jni, SYSERR, errno,
1292                   "failed to connect to Unix-domain socket `%s'", pathstr);
1293 end:
1294   if (fd != -1) close(fd);
1295   put_cstring(jni, path, pathstr);
1296   return (ret);
1297 }
1298
1299 static int check_buffer_bounds(JNIEnv *jni, const char *what,
1300                                jbyteArray buf, jint start, jint len)
1301 {
1302   jsize bufsz;
1303   jclass cls;
1304
1305   cls = (*jni)->FindClass(jni, "[B"); assert(cls);
1306   if (!(*jni)->IsInstanceOf(jni, buf, cls)) {
1307     except(jni, ARGERR,
1308            "expected a byte array");
1309     return (-1);
1310   }
1311   bufsz = (*jni)->GetArrayLength(jni, buf);
1312   if (start > bufsz) {
1313     except(jni, BOUNDSERR,
1314            "bad %s buffer bounds: start %d > buffer size %d", start, bufsz);
1315     return (-1);
1316   }
1317   if (len > bufsz - start) {
1318     except(jni, BOUNDSERR,
1319            "bad %s buffer bounds: length %d > remaining buffer size %d",
1320            len, bufsz - start);
1321     return (-1);
1322   }
1323   return (0);
1324 }
1325
1326 JNIEXPORT void JNICALL JNIFUNC(send)(JNIEnv *jni, jobject cls,
1327                                      wrapper wconn, jbyteArray buf,
1328                                      jint start, jint len,
1329                                      wrapper wtrig)
1330 {
1331   struct conn conn;
1332   struct trigger trig;
1333   int rc, maxfd;
1334   ssize_t n;
1335   fd_set rfds, wfds;
1336   jbyte *p = 0;
1337
1338   if (unwrap(jni, &conn, &conn_type, wconn)) goto end;
1339   if (unwrap(jni, &trig, &trigger_type, wtrig)) goto end;
1340   if (check_buffer_bounds(jni, "send", buf, start, len)) goto end;
1341
1342   p = (*jni)->GetByteArrayElements(jni, buf, 0);
1343   if (!p) goto end;
1344
1345   maxfd = trig.rfd;
1346   if (maxfd < conn.fd) maxfd = conn.fd;
1347   while (len) {
1348     FD_ZERO(&rfds); FD_SET(trig.rfd, &rfds);
1349     FD_ZERO(&wfds); FD_SET(conn.fd, &wfds);
1350     rc = select(maxfd + 1, &rfds, &wfds, 0, 0); if (rc < 0) goto err;
1351     if (FD_ISSET(trig.rfd, &rfds)) break;
1352     if (FD_ISSET(conn.fd, &wfds)) {
1353       n = send(conn.fd, p + start, len, 0);
1354       if (n >= 0) { start += n; len -= n; }
1355       else if (errno != EAGAIN && errno != EWOULDBLOCK) goto err;
1356     }
1357   }
1358   goto end;
1359
1360 err:
1361   except_syserror(jni, SYSERR, errno, "failed to send on connection");
1362 end:
1363   if (p) (*jni)->ReleaseByteArrayElements(jni, buf, p, JNI_ABORT);
1364   return;
1365 }
1366
1367 JNIEXPORT jint JNICALL JNIFUNC(recv)(JNIEnv *jni, jobject cls,
1368                                      wrapper wconn, jbyteArray buf,
1369                                      jint start, jint len,
1370                                      wrapper wtrig)
1371 {
1372   struct conn conn;
1373   struct trigger trig;
1374   int maxfd;
1375   fd_set rfds;
1376   jbyte *p = 0;
1377   jint rc = -1;
1378
1379   if (unwrap(jni, &conn, &conn_type, wconn)) goto end;
1380   if (unwrap(jni, &trig, &trigger_type, wtrig)) goto end;
1381   if (check_buffer_bounds(jni, "send", buf, start, len)) goto end;
1382
1383   p = (*jni)->GetByteArrayElements(jni, buf, 0);
1384   if (!p) goto end;
1385
1386   maxfd = trig.rfd;
1387   if (maxfd < conn.fd) maxfd = conn.fd;
1388   for (;;) {
1389     FD_ZERO(&rfds); FD_SET(trig.rfd, &rfds); FD_SET(conn.fd, &rfds);
1390     rc = select(maxfd + 1, &rfds, 0, 0, 0); if (rc < 0) goto err;
1391     if (FD_ISSET(trig.rfd, &rfds)) {
1392       break;
1393     }
1394     if (FD_ISSET(conn.fd, &rfds)) {
1395       rc = recv(conn.fd, p + start, len, 0);
1396       if (rc >= 0) break;
1397       else if (errno != EAGAIN && errno != EWOULDBLOCK) goto err;
1398     }
1399   }
1400   if (!rc) rc = -1;
1401   goto end;
1402
1403 err:
1404   except_syserror(jni, SYSERR, errno, "failed to read from connection");
1405 end:
1406   if (p) (*jni)->ReleaseByteArrayElements(jni, buf, p, 0);
1407   return (rc);
1408 }
1409
1410 JNIEXPORT void JNICALL JNIFUNC(closeconn)(JNIEnv *jni, jobject cls,
1411                                           wrapper wconn, jint how)
1412 {
1413   struct conn conn;
1414   int rc;
1415
1416   if (unwrap(jni, &conn, &conn_type, wconn)) goto end;
1417   if (conn.fd == -1) goto end;
1418
1419   how &= CF_CLOSEMASK&~conn.f;
1420   conn.f |= how;
1421   if ((conn.f&CF_CLOSEMASK) == CF_CLOSEMASK) {
1422     close(conn.fd);
1423     conn.fd = -1;
1424   } else {
1425     if (how&CF_CLOSERD) shutdown(conn.fd, SHUT_RD);
1426     if (how&CF_CLOSEWR) shutdown(conn.fd, SHUT_WR);
1427   }
1428   rc = update_wrapper(jni, &conn_type, wconn, &conn); assert(!rc);
1429
1430 end:
1431   return;
1432 }
1433
1434 /*----- That's all, folks -------------------------------------------------*/