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(mkfile
)(JNIEnv
*jni
, jobject cls
,
916 jobject path
, jint mode
)
918 const char *pathstr
= 0;
921 pathstr
= get_cstring(jni
, path
); if (!pathstr
) goto end
;
922 fd
= open(pathstr
, O_WRONLY
| O_CREAT
| O_EXCL
, mode
);
924 except_syserror(jni
, SYSERR
, errno
,
925 "failed to create fresh file `%s'", pathstr
);
929 if (fd
!= -1) close(fd
);
930 put_cstring(jni
, path
, pathstr
);
933 JNIEXPORT
void JNIFUNC(rename
)(JNIEnv
*jni
, jobject cls
,
934 jobject from
, jobject to
)
936 const char *fromstr
= 0, *tostr
= 0;
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
);
946 put_cstring(jni
, from
, fromstr
);
947 put_cstring(jni
, to
, tostr
);
950 #define LKF_EXCL 0x1000u
951 #define LKF_WAIT 0x2000u
953 struct native_base _base
;
956 static struct native_type lockf_type
=
957 { "lock", sizeof(struct lockf
), 0xb2648926};
958 JNIEXPORT wrapper
JNIFUNC(lock
)(JNIEnv
*jni
, jobject cls
,
959 jobject path
, jint flags
)
961 const char *pathstr
= 0;
965 struct stat st0
, st1
;
969 pathstr
= get_cstring(jni
, path
); if (!pathstr
) goto end
;
972 fd
= open(pathstr
, O_RDWR
| O_CREAT
, flags
&07777); if (fd
< 0) goto err
;
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
;
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
; }
986 INIT_NATIVE(lockf
, &lk
); lk
.fd
= fd
; fd
= -1;
987 r
= wrap(jni
, &lockf_type
, &lk
);
991 except_syserror(jni
, SYSERR
, errno
, "failed to lock file `%s'", pathstr
);
993 if (fd
!= -1) close(fd
);
994 put_cstring(jni
, path
, pathstr
);
998 JNIEXPORT
void JNIFUNC(unlock
)(JNIEnv
*jni
, jobject cls
, wrapper wlk
)
1004 if (unwrap(jni
, &lk
, &lockf_type
, wlk
)) goto end
;
1005 if (lk
.fd
== -1) goto end
;
1007 l
.l_whence
= SEEK_SET
;
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
);
1016 static jlong
xlttimespec(const struct timespec
*ts
)
1017 { return (1000*(jlong
)ts
->tv_sec
+ ts
->tv_nsec
/1000000); }
1019 static jobject
xltstat(JNIEnv
*jni
, const struct stat
*st
)
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;
1034 cls
= (*jni
)->FindClass(jni
, STATCLS
); assert(cls
);
1035 init
= (*jni
)->GetMethodID(jni
, cls
, "<init>", "(IIJIIIIIIJIJJJJ)V");
1037 return ((*jni
)->NewObject(jni
, cls
, init
,
1038 (jint
)major(st
->st_dev
), (jint
)minor(st
->st_dev
),
1042 (jint
)st
->st_uid
, (jint
)st
->st_gid
,
1043 (jint
)major(st
->st_rdev
), (jint
)minor(st
->st_rdev
),
1045 (jint
)st
->st_blksize
, (jlong
)st
->st_blocks
,
1046 xlttimespec(&st
->st_atim
),
1047 xlttimespec(&st
->st_mtim
),
1048 xlttimespec(&st
->st_ctim
)));
1051 JNIEXPORT jobject
JNIFUNC(stat
)(JNIEnv
*jni
, jobject cls
, jobject path
)
1054 const char *pathstr
= 0;
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
);
1063 r
= xltstat(jni
, &st
);
1065 put_cstring(jni
, path
, pathstr
);
1069 JNIEXPORT jobject
JNIFUNC(lstat
)(JNIEnv
*jni
, jobject cls
, jobject path
)
1072 const char *pathstr
= 0;
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
);
1081 r
= xltstat(jni
, &st
);
1083 put_cstring(jni
, path
, pathstr
);
1088 struct native_base _base
;
1091 static const struct native_type dir_type
=
1092 { "dir", sizeof(struct dir
), 0x0f5ca477 };
1094 JNIEXPORT jobject
JNIFUNC(opendir
)(JNIEnv
*jni
, jobject cls
, jobject path
)
1096 const char *pathstr
= 0;
1100 pathstr
= get_cstring(jni
, path
); if (!pathstr
) goto end
;
1101 INIT_NATIVE(dir
, &dir
);
1102 dir
.d
= opendir(pathstr
);
1104 except_syserror(jni
, SYSERR
, errno
,
1105 "failed to open directory `%s'", pathstr
);
1108 r
= wrap(jni
, &dir_type
, &dir
);
1110 put_cstring(jni
, path
, pathstr
);
1114 JNIEXPORT jbyteArray
JNIFUNC(readdir
)(JNIEnv
*jni
, jobject cls
,
1115 jobject path
, jobject wdir
)
1117 const char *pathstr
= 0;
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
);
1126 pathstr
= get_cstring(jni
, path
); if (!pathstr
) goto end
;
1127 except_syserror(jni
, SYSERR
, errno
,
1128 "failed to read directory `%s'", pathstr
);
1131 if (d
) r
= wrap_cstring(jni
, d
->d_name
);
1133 put_cstring(jni
, path
, pathstr
);
1137 JNIEXPORT
void JNIFUNC(closedir
)(JNIEnv
*jni
, jobject cls
,
1138 jobject path
, jobject wdir
)
1140 const char *pathstr
= 0;
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
);
1152 if (update_wrapper(jni
, &dir_type
, wdir
, &dir
)) goto end
;
1154 put_cstring(jni
, path
, pathstr
);
1157 /*----- Triggers ----------------------------------------------------------*/
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.
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.
1169 struct native_base _base
;
1172 static const struct native_type trigger_type
=
1173 { "trigger", sizeof(struct trigger
), 0x65ffd8b4 };
1175 JNIEXPORT wrapper JNICALL
JNIFUNC(make_1trigger
)(JNIEnv
*jni
, jobject cls
)
1177 struct trigger trig
;
1184 except_syserror(jni
, SYSERR
, errno
, "failed to create pipe");
1187 for (i
= 0; i
< 2; i
++) {
1188 if (set_nonblocking(jni
, fd
[i
], 1) < 0 || set_closeonexec(jni
, fd
[i
]))
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
);
1198 for (i
= 0; i
< 2; i
++)
1199 if (fd
[i
] != -1) close(fd
[i
]);
1203 JNIEXPORT
void JNICALL
JNIFUNC(destroy_1trigger
)(JNIEnv
*jni
, jobject cls
,
1206 struct trigger trig
;
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
);
1214 JNIEXPORT
void JNICALL
JNIFUNC(reset_1trigger
)(JNIEnv
*jni
, jobject cls
,
1217 struct trigger trig
;
1221 if (unwrap(jni
, &trig
, &trigger_type
, wtrig
)) return;
1223 n
= read(trig
.rfd
, buf
, sizeof(buf
));
1224 if (n
> 0) continue;
1226 if (errno
== EAGAIN
|| errno
== EWOULDBLOCK
) break;
1228 except_syserror(jni
, SYSERR
, errno
, "failed to reset trigger");
1234 JNIEXPORT
void JNICALL
JNIFUNC(trigger
)(JNIEnv
*jni
, jobject cls
,
1237 struct trigger trig
;
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");
1247 /*----- A tunnel supplied by Java -----------------------------------------*/
1250 const tunnel_ops
*ops
;
1255 static const struct tunnel_ops tun_java
;
1257 static int t_init(void) { return (0); }
1259 static void t_read(int fd
, unsigned mode
, void *v
)
1265 n
= read(fd
, buf_i
, sizeof(buf_i
));
1267 a_warn("TUN", "%s", p_ifname(t
->p
), "java",
1268 "read-error", "?ERRNO", A_END
);
1271 IF_TRACING(T_TUNNEL
, {
1272 trace(T_TUNNEL
, "tun-java: packet arrived");
1273 trace_block(T_PACKET
, "tunnel: packet contents", buf_i
, n
);
1275 buf_init(&b
, buf_i
, n
);
1279 static tunnel
*t_create(peer
*p
, int fd
, char **ifn
)
1281 JNIEnv
*jni
= jni_tripe
;
1283 const char *name
= p_name(p
);
1285 size_t n
= strlen(p_name(p
));
1286 jclass cls
, metacls
;
1287 jstring jclsname
, jexcmsg
;
1288 const char *clsname
, *excmsg
;
1294 jname
= wrap_cstring(jni
, name
);
1295 cls
= (*jni
)->FindClass(jni
, SYSCLS
); assert(cls
);
1296 mid
= (*jni
)->GetStaticMethodID(jni
, cls
, "getTunnelFd", "([B)I");
1298 fd
= (*jni
)->CallStaticIntMethod(jni
, cls
, mid
, jname
);
1300 exc
= (*jni
)->ExceptionOccurred(jni
);
1302 cls
= (*jni
)->GetObjectClass(jni
, exc
);
1303 metacls
= (*jni
)->GetObjectClass(jni
, cls
);
1304 mid
= (*jni
)->GetMethodID(jni
, metacls
,
1305 "getName", "()L"STRCLS
";");
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
);
1324 sel_initfile(&sel
, &t
->f
, fd
, SEL_READ
, t_read
, t
);
1327 *ifn
= xmalloc(n
+ 5);
1328 sprintf(*ifn
, "vpn-%s", name
);
1335 static void t_inject(tunnel
*t
, buf
*b
)
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
));
1341 DISCARD(write(t
->f
.fd
, BBASE(b
), BLEN(b
)));
1344 static void t_destroy(tunnel
*t
)
1345 { sel_rmfile(&t
->f
); close(t
->f
.fd
); DESTROY(t
); }
1347 static const struct tunnel_ops tun_java
= {
1350 /* create */ t_create
,
1352 /* inject */ t_inject
,
1353 /* destroy */ t_destroy
1357 JNIEXPORT jint JNICALL
JNIFUNC(open_1tun
)(JNIEnv
*jni
, jobject cls
)
1363 if ((fd
= open("/dev/net/tun", O_RDWR
)) < 0) {
1364 except_syserror(jni
, SYSERR
, errno
, "failed to open tunnel device");
1368 if (set_nonblocking(jni
, fd
, 1) || set_closeonexec(jni
, fd
)) goto end
;
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");
1381 if (fd
!= -1) close(fd
);
1385 /*----- A custom noise source ---------------------------------------------*/
1387 static void javanoise(rand_pool
*r
)
1389 JNIEnv
*jni
= jni_tripe
;
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);
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
);
1408 if ((*jni
)->ExceptionOccurred(jni
)) {
1409 (*jni
)->ExceptionDescribe(jni
);
1410 (*jni
)->ExceptionClear(jni
);
1414 static const rand_source javasource
= { javanoise
, noise_timer
};
1416 /*----- Embedding the TrIPE server ----------------------------------------*/
1418 static void lock_tripe(JNIEnv
*jni
)
1420 jclass cls
= (*jni
)->FindClass(jni
, LOCKCLS
); assert(cls
);
1421 (*jni
)->MonitorEnter(jni
, cls
);
1424 static void unlock_tripe(JNIEnv
*jni
)
1426 jclass cls
= (*jni
)->FindClass(jni
, LOCKCLS
); assert(cls
);
1427 (*jni
)->MonitorExit(jni
, cls
);
1439 #define DEFTAG(st) st,
1445 static const char *statetab
[] = {
1446 #define DEFNAME(st) #st,
1451 static unsigned state
= INIT
;
1452 static int clientsk
= -1;
1454 static const char *statename(unsigned st
)
1456 if (st
>= MAXSTATE
) return ("<invalid>");
1457 else return (statetab
[st
]);
1460 static int ensure_state(JNIEnv
*jni
, unsigned want
)
1469 except(jni
, STERR
, "server is in state %s (%u), not %s (%u)",
1470 statename(cur
), cur
, statename(want
), want
);
1476 JNIEXPORT
void JNICALL
JNIFUNC(base_1init
)(JNIEnv
*jni
, jobject cls
)
1481 for (i
= 0; i
< N(fd
); i
++) fd
[i
] = -1;
1485 if (ensure_state(jni
, INIT
)) goto end
;
1487 if (socketpair(PF_UNIX
, SOCK_STREAM
, 0, fd
)) {
1488 except_syserror(jni
, SYSERR
, errno
, "failed to create socket pair");
1492 clientsk
= fd
[0]; fd
[0] = -1;
1494 rand_noisesrc(RAND_GLOBAL
, &javasource
);
1495 rand_seed(RAND_GLOBAL
, MAXHASHSZ
);
1497 a_create(fd
[1], fd
[1], AF_NOTE
| AF_WARN
| AF_TRACE
); fd
[1] = -1;
1499 p_addtun(&tun_java
); p_setdflttun(&tun_java
);
1506 for (i
= 0; i
< N(fd
); i
++) if (fd
[i
] != -1) close(fd
[i
]);
1511 JNIEXPORT
void JNICALL
JNIFUNC(setup_1resolver
)(JNIEnv
*jni
, jobject cls
)
1514 if (ensure_state(jni
, RESOLVE
)) goto end
;
1517 { except(jni
, INITERR
, "failed to initialize resolver"); return; }
1525 JNIEXPORT
void JNICALL
JNIFUNC(load_1keys
)(JNIEnv
*jni
, jobject cls
,
1526 jobject privstr
, jobject pubstr
,
1529 const char *priv
= 0, *pub
= 0, *tag
= 0;
1532 if (ensure_state(jni
, KEYS
)) return;
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
;
1538 if (km_init(priv
, pub
, tag
))
1539 { except(jni
, INITERR
, "failed to load initial keys"); goto end
; }
1544 put_cstring(jni
, privstr
, priv
);
1545 put_cstring(jni
, pubstr
, pub
);
1546 put_cstring(jni
, tagstr
, tag
);
1550 JNIEXPORT
void JNICALL
JNIFUNC(unload_1keys
)(JNIEnv
*jni
, jobject cls
)
1553 if (ensure_state(jni
, KEYS
+ 1)) goto end
;
1563 JNIEXPORT
void JNICALL
JNIFUNC(bind
)(JNIEnv
*jni
, jobject cls
,
1564 jbyteArray hoststr
, jbyteArray svcstr
)
1566 const char *host
= 0, *svc
= 0;
1567 struct addrinfo hint
, *ai
= 0;
1571 if (ensure_state(jni
, BIND
)) goto end
;
1573 if (hoststr
) { host
= get_cstring(jni
, hoststr
); if (!host
) goto end
; }
1574 svc
= get_cstring(jni
, svcstr
); if (!svc
) goto end
;
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
);
1582 except(jni
, NAMEERR
, "failed to resolve %c%s%c, port `%s': %s",
1583 host ?
'`' : '<', host ? host
: "nil", host ?
'\'' : '>',
1584 svc
, gai_strerror(err
));
1589 { except(jni
, INITERR
, "failed to bind master socket"); goto end
; }
1594 if (ai
) freeaddrinfo(ai
);
1595 put_cstring(jni
, hoststr
, host
);
1596 put_cstring(jni
, svcstr
, svc
);
1600 JNIEXPORT
void JNICALL
JNIFUNC(unbind
)(JNIEnv
*jni
, jobject cls
)
1603 if (ensure_state(jni
, BIND
+ 1)) goto end
;
1613 JNIEXPORT
void JNICALL
JNIFUNC(mark
)(JNIEnv
*jni
, jobject cls
, jint seq
)
1616 a_notify("MARK", "%d", seq
, A_END
);
1620 JNIEXPORT
void JNICALL
JNIFUNC(run
)(JNIEnv
*jni
, jobject cls
)
1623 if (ensure_state(jni
, READY
)) goto end
;
1639 static int check_buffer_bounds(JNIEnv
*jni
, const char *what
,
1640 jbyteArray buf
, jint start
, jint len
)
1645 cls
= (*jni
)->FindClass(jni
, "[B"); assert(cls
);
1646 if (!(*jni
)->IsInstanceOf(jni
, buf
, cls
)) {
1648 "expected a byte array");
1651 bufsz
= (*jni
)->GetArrayLength(jni
, buf
);
1652 if (start
> bufsz
) {
1653 except(jni
, BOUNDSERR
,
1654 "bad %s buffer bounds: start %d > buffer size %d", start
, bufsz
);
1657 if (len
> bufsz
- start
) {
1658 except(jni
, BOUNDSERR
,
1659 "bad %s buffer bounds: length %d > remaining buffer size %d",
1660 len
, bufsz
- start
);
1666 JNIEXPORT
void JNICALL
JNIFUNC(send
)(JNIEnv
*jni
, jobject cls
,
1668 jint start
, jint len
,
1671 struct trigger trig
;
1677 if (ensure_state(jni
, RUNNING
)) goto end
;
1679 if (unwrap(jni
, &trig
, &trigger_type
, wtrig
)) goto end
;
1680 if (check_buffer_bounds(jni
, "send", buf
, start
, len
)) goto end
;
1682 p
= (*jni
)->GetByteArrayElements(jni
, buf
, 0);
1686 if (maxfd
< clientsk
) maxfd
= clientsk
;
1688 FD_ZERO(&rfds
); FD_SET(trig
.rfd
, &rfds
);
1689 FD_ZERO(&wfds
); FD_SET(clientsk
, &wfds
);
1690 rc
= select(maxfd
+ 1, &rfds
, &wfds
, 0, 0); if (rc
< 0) goto err
;
1691 if (FD_ISSET(trig
.rfd
, &rfds
)) break;
1692 if (FD_ISSET(clientsk
, &wfds
)) {
1693 n
= send(clientsk
, p
+ start
, len
, 0);
1694 if (n
>= 0) { start
+= n
; len
-= n
; }
1695 else if (errno
!= EAGAIN
&& errno
!= EWOULDBLOCK
) goto err
;
1701 except_syserror(jni
, SYSERR
, errno
, "failed to send on connection");
1703 if (p
) (*jni
)->ReleaseByteArrayElements(jni
, buf
, p
, JNI_ABORT
);
1707 JNIEXPORT jint JNICALL
JNIFUNC(recv
)(JNIEnv
*jni
, jobject cls
,
1709 jint start
, jint len
,
1712 struct trigger trig
;
1719 if (clientsk
== -1) {
1720 except(jni
, STERR
, "client connection not established");
1726 if (unwrap(jni
, &trig
, &trigger_type
, wtrig
)) goto end
;
1727 if (check_buffer_bounds(jni
, "send", buf
, start
, len
)) goto end
;
1729 p
= (*jni
)->GetByteArrayElements(jni
, buf
, 0);
1733 if (maxfd
< clientsk
) maxfd
= clientsk
;
1735 FD_ZERO(&rfds
); FD_SET(trig
.rfd
, &rfds
); FD_SET(clientsk
, &rfds
);
1736 rc
= select(maxfd
+ 1, &rfds
, 0, 0, 0); if (rc
< 0) goto err
;
1737 if (FD_ISSET(trig
.rfd
, &rfds
)) {
1740 if (FD_ISSET(clientsk
, &rfds
)) {
1741 rc
= recv(clientsk
, p
+ start
, len
, 0);
1743 else if (errno
!= EAGAIN
&& errno
!= EWOULDBLOCK
) goto err
;
1750 except_syserror(jni
, SYSERR
, errno
, "failed to read from connection");
1752 if (p
) (*jni
)->ReleaseByteArrayElements(jni
, buf
, p
, 0);
1756 /*----- That's all, folks -------------------------------------------------*/