1 | /* |
2 | * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved. |
3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
4 | * |
5 | * This code is free software; you can redistribute it and/or modify it |
6 | * under the terms of the GNU General Public License version 2 only, as |
7 | * published by the Free Software Foundation. Oracle designates this |
8 | * particular file as subject to the "Classpath" exception as provided |
9 | * by Oracle in the LICENSE file that accompanied this code. |
10 | * |
11 | * This code is distributed in the hope that it will be useful, but WITHOUT |
12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
14 | * version 2 for more details (a copy is included in the LICENSE file that |
15 | * accompanied this code). |
16 | * |
17 | * You should have received a copy of the GNU General Public License version |
18 | * 2 along with this work; if not, write to the Free Software Foundation, |
19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
20 | * |
21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
22 | * or visit www.oracle.com if you need additional information or have any |
23 | * questions. |
24 | */ |
25 | #include <dlfcn.h> |
26 | #include <errno.h> |
27 | #include <net/if.h> |
28 | #include <netinet/tcp.h> // defines TCP_NODELAY |
29 | #include <stdlib.h> |
30 | #include <string.h> |
31 | #include <sys/ioctl.h> |
32 | #include <sys/time.h> |
33 | |
34 | #if defined(__linux__) |
35 | #include <arpa/inet.h> |
36 | #include <net/route.h> |
37 | #include <sys/utsname.h> |
38 | #endif |
39 | |
40 | #if defined(__solaris__) |
41 | #include <inet/nd.h> |
42 | #include <limits.h> |
43 | #include <stropts.h> |
44 | #include <sys/filio.h> |
45 | #include <sys/sockio.h> |
46 | #endif |
47 | |
48 | #if defined(MACOSX) |
49 | #include <sys/sysctl.h> |
50 | #endif |
51 | |
52 | #include "jvm.h" |
53 | #include "net_util.h" |
54 | |
55 | #include "java_net_SocketOptions.h" |
56 | #include "java_net_InetAddress.h" |
57 | |
58 | #if defined(__linux__) && !defined(IPV6_FLOWINFO_SEND) |
59 | #define IPV6_FLOWINFO_SEND 33 |
60 | #endif |
61 | |
62 | #if defined(__solaris__) && !defined(MAXINT) |
63 | #define MAXINT INT_MAX |
64 | #endif |
65 | |
66 | /* |
67 | * EXCLBIND socket options only on Solaris |
68 | */ |
69 | #if defined(__solaris__) && !defined(TCP_EXCLBIND) |
70 | #define TCP_EXCLBIND 0x21 |
71 | #endif |
72 | #if defined(__solaris__) && !defined(UDP_EXCLBIND) |
73 | #define UDP_EXCLBIND 0x0101 |
74 | #endif |
75 | |
76 | void setDefaultScopeID(JNIEnv *env, struct sockaddr *him) |
77 | { |
78 | #ifdef MACOSX |
79 | static jclass ni_class = NULL; |
80 | static jfieldID ni_defaultIndexID; |
81 | if (ni_class == NULL) { |
82 | jclass c = (*env)->FindClass(env, "java/net/NetworkInterface" ); |
83 | CHECK_NULL(c); |
84 | c = (*env)->NewGlobalRef(env, c); |
85 | CHECK_NULL(c); |
86 | ni_defaultIndexID = (*env)->GetStaticFieldID(env, c, "defaultIndex" , "I" ); |
87 | CHECK_NULL(ni_defaultIndexID); |
88 | ni_class = c; |
89 | } |
90 | int defaultIndex; |
91 | struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)him; |
92 | if (sin6->sin6_family == AF_INET6 && (sin6->sin6_scope_id == 0) && |
93 | (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) || |
94 | IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))) { |
95 | defaultIndex = (*env)->GetStaticIntField(env, ni_class, |
96 | ni_defaultIndexID); |
97 | sin6->sin6_scope_id = defaultIndex; |
98 | } |
99 | #endif |
100 | } |
101 | |
102 | int getDefaultScopeID(JNIEnv *env) { |
103 | int defaultIndex = 0; |
104 | static jclass ni_class = NULL; |
105 | static jfieldID ni_defaultIndexID; |
106 | if (ni_class == NULL) { |
107 | jclass c = (*env)->FindClass(env, "java/net/NetworkInterface" ); |
108 | CHECK_NULL_RETURN(c, 0); |
109 | c = (*env)->NewGlobalRef(env, c); |
110 | CHECK_NULL_RETURN(c, 0); |
111 | ni_defaultIndexID = (*env)->GetStaticFieldID(env, c, "defaultIndex" , "I" ); |
112 | CHECK_NULL_RETURN(ni_defaultIndexID, 0); |
113 | ni_class = c; |
114 | } |
115 | defaultIndex = (*env)->GetStaticIntField(env, ni_class, |
116 | ni_defaultIndexID); |
117 | return defaultIndex; |
118 | } |
119 | |
120 | #define RESTARTABLE(_cmd, _result) do { \ |
121 | do { \ |
122 | _result = _cmd; \ |
123 | } while((_result == -1) && (errno == EINTR)); \ |
124 | } while(0) |
125 | |
126 | int NET_SocketAvailable(int s, int *pbytes) { |
127 | int result; |
128 | RESTARTABLE(ioctl(s, FIONREAD, pbytes), result); |
129 | return result; |
130 | } |
131 | |
132 | #ifdef __solaris__ |
133 | static int init_tcp_max_buf, init_udp_max_buf; |
134 | static int tcp_max_buf; |
135 | static int udp_max_buf; |
136 | static int useExclBind = 0; |
137 | |
138 | /* |
139 | * Get the specified parameter from the specified driver. The value |
140 | * of the parameter is assumed to be an 'int'. If the parameter |
141 | * cannot be obtained return -1 |
142 | */ |
143 | int net_getParam(char *driver, char *param) |
144 | { |
145 | struct strioctl stri; |
146 | char buf [64]; |
147 | int s; |
148 | int value; |
149 | |
150 | s = open (driver, O_RDWR); |
151 | if (s < 0) { |
152 | return -1; |
153 | } |
154 | strncpy (buf, param, sizeof(buf)); |
155 | stri.ic_cmd = ND_GET; |
156 | stri.ic_timout = 0; |
157 | stri.ic_dp = buf; |
158 | stri.ic_len = sizeof(buf); |
159 | if (ioctl (s, I_STR, &stri) < 0) { |
160 | value = -1; |
161 | } else { |
162 | value = atoi(buf); |
163 | } |
164 | close (s); |
165 | return value; |
166 | } |
167 | |
168 | /* |
169 | * Iterative way to find the max value that SO_SNDBUF or SO_RCVBUF |
170 | * for Solaris versions that do not support the ioctl() in net_getParam(). |
171 | * Ugly, but only called once (for each sotype). |
172 | * |
173 | * As an optimization, we make a guess using the default values for Solaris |
174 | * assuming they haven't been modified with ndd. |
175 | */ |
176 | |
177 | #define MAX_TCP_GUESS 1024 * 1024 |
178 | #define MAX_UDP_GUESS 2 * 1024 * 1024 |
179 | |
180 | #define FAIL_IF_NOT_ENOBUFS if (errno != ENOBUFS) return -1 |
181 | |
182 | static int findMaxBuf(int fd, int opt, int sotype) { |
183 | int a = 0; |
184 | int b = MAXINT; |
185 | int initial_guess; |
186 | int limit = -1; |
187 | |
188 | if (sotype == SOCK_DGRAM) { |
189 | initial_guess = MAX_UDP_GUESS; |
190 | } else { |
191 | initial_guess = MAX_TCP_GUESS; |
192 | } |
193 | |
194 | if (setsockopt(fd, SOL_SOCKET, opt, &initial_guess, sizeof(int)) == 0) { |
195 | initial_guess++; |
196 | if (setsockopt(fd, SOL_SOCKET, opt, &initial_guess,sizeof(int)) < 0) { |
197 | FAIL_IF_NOT_ENOBUFS; |
198 | return initial_guess - 1; |
199 | } |
200 | a = initial_guess; |
201 | } else { |
202 | FAIL_IF_NOT_ENOBUFS; |
203 | b = initial_guess - 1; |
204 | } |
205 | do { |
206 | int mid = a + (b-a)/2; |
207 | if (setsockopt(fd, SOL_SOCKET, opt, &mid, sizeof(int)) == 0) { |
208 | limit = mid; |
209 | a = mid + 1; |
210 | } else { |
211 | FAIL_IF_NOT_ENOBUFS; |
212 | b = mid - 1; |
213 | } |
214 | } while (b >= a); |
215 | |
216 | return limit; |
217 | } |
218 | #endif |
219 | |
220 | #ifdef __linux__ |
221 | static int vinit = 0; |
222 | static int kernelV24 = 0; |
223 | static int vinit24 = 0; |
224 | |
225 | int kernelIsV24 () { |
226 | if (!vinit24) { |
227 | struct utsname sysinfo; |
228 | if (uname(&sysinfo) == 0) { |
229 | sysinfo.release[3] = '\0'; |
230 | if (strcmp(sysinfo.release, "2.4" ) == 0) { |
231 | kernelV24 = JNI_TRUE; |
232 | } |
233 | } |
234 | vinit24 = 1; |
235 | } |
236 | return kernelV24; |
237 | } |
238 | #endif |
239 | |
240 | void |
241 | NET_ThrowByNameWithLastError(JNIEnv *env, const char *name, |
242 | const char *defaultDetail) { |
243 | JNU_ThrowByNameWithMessageAndLastError(env, name, defaultDetail); |
244 | } |
245 | |
246 | void |
247 | NET_ThrowCurrent(JNIEnv *env, char *msg) { |
248 | NET_ThrowNew(env, errno, msg); |
249 | } |
250 | |
251 | void |
252 | NET_ThrowNew(JNIEnv *env, int errorNumber, char *msg) { |
253 | char fullMsg[512]; |
254 | if (!msg) { |
255 | msg = "no further information" ; |
256 | } |
257 | switch(errorNumber) { |
258 | case EBADF: |
259 | jio_snprintf(fullMsg, sizeof(fullMsg), "socket closed: %s" , msg); |
260 | JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException" , fullMsg); |
261 | break; |
262 | case EINTR: |
263 | JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException" , msg); |
264 | break; |
265 | default: |
266 | errno = errorNumber; |
267 | JNU_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException" , msg); |
268 | break; |
269 | } |
270 | } |
271 | |
272 | |
273 | jfieldID |
274 | NET_GetFileDescriptorID(JNIEnv *env) |
275 | { |
276 | jclass cls = (*env)->FindClass(env, "java/io/FileDescriptor" ); |
277 | CHECK_NULL_RETURN(cls, NULL); |
278 | return (*env)->GetFieldID(env, cls, "fd" , "I" ); |
279 | } |
280 | |
281 | jint IPv4_supported() |
282 | { |
283 | int fd = socket(AF_INET, SOCK_STREAM, 0) ; |
284 | if (fd < 0) { |
285 | return JNI_FALSE; |
286 | } |
287 | close(fd); |
288 | return JNI_TRUE; |
289 | } |
290 | |
291 | #if defined(DONT_ENABLE_IPV6) |
292 | jint IPv6_supported() |
293 | { |
294 | return JNI_FALSE; |
295 | } |
296 | |
297 | #else /* !DONT_ENABLE_IPV6 */ |
298 | |
299 | jint IPv6_supported() |
300 | { |
301 | int fd; |
302 | void *ipv6_fn; |
303 | SOCKETADDRESS sa; |
304 | socklen_t sa_len = sizeof(SOCKETADDRESS); |
305 | |
306 | fd = socket(AF_INET6, SOCK_STREAM, 0) ; |
307 | if (fd < 0) { |
308 | /* |
309 | * TODO: We really cant tell since it may be an unrelated error |
310 | * for now we will assume that AF_INET6 is not available |
311 | */ |
312 | return JNI_FALSE; |
313 | } |
314 | |
315 | /* |
316 | * If fd 0 is a socket it means we may have been launched from inetd or |
317 | * xinetd. If it's a socket then check the family - if it's an |
318 | * IPv4 socket then we need to disable IPv6. |
319 | */ |
320 | if (getsockname(0, &sa.sa, &sa_len) == 0) { |
321 | if (sa.sa.sa_family == AF_INET) { |
322 | close(fd); |
323 | return JNI_FALSE; |
324 | } |
325 | } |
326 | |
327 | /** |
328 | * Linux - check if any interface has an IPv6 address. |
329 | * Don't need to parse the line - we just need an indication. |
330 | */ |
331 | #ifdef __linux__ |
332 | { |
333 | FILE *fP = fopen("/proc/net/if_inet6" , "r" ); |
334 | char buf[255]; |
335 | char *bufP; |
336 | |
337 | if (fP == NULL) { |
338 | close(fd); |
339 | return JNI_FALSE; |
340 | } |
341 | bufP = fgets(buf, sizeof(buf), fP); |
342 | fclose(fP); |
343 | if (bufP == NULL) { |
344 | close(fd); |
345 | return JNI_FALSE; |
346 | } |
347 | } |
348 | #endif |
349 | |
350 | /** |
351 | * On Solaris 8 it's possible to create INET6 sockets even |
352 | * though IPv6 is not enabled on all interfaces. Thus we |
353 | * query the number of IPv6 addresses to verify that IPv6 |
354 | * has been configured on at least one interface. |
355 | * |
356 | * On Linux it doesn't matter - if IPv6 is built-in the |
357 | * kernel then IPv6 addresses will be bound automatically |
358 | * to all interfaces. |
359 | */ |
360 | #ifdef __solaris__ |
361 | |
362 | #ifdef SIOCGLIFNUM |
363 | { |
364 | struct lifnum numifs; |
365 | |
366 | numifs.lifn_family = AF_INET6; |
367 | numifs.lifn_flags = 0; |
368 | if (ioctl(fd, SIOCGLIFNUM, (char *)&numifs) < 0) { |
369 | /** |
370 | * SIOCGLIFNUM failed - assume IPv6 not configured |
371 | */ |
372 | close(fd); |
373 | return JNI_FALSE; |
374 | } |
375 | /** |
376 | * If no IPv6 addresses then return false. If count > 0 |
377 | * it's possible that all IPv6 addresses are "down" but |
378 | * that's okay as they may be brought "up" while the |
379 | * VM is running. |
380 | */ |
381 | if (numifs.lifn_count == 0) { |
382 | close(fd); |
383 | return JNI_FALSE; |
384 | } |
385 | } |
386 | #else |
387 | /* SIOCGLIFNUM not defined in build environment ??? */ |
388 | close(fd); |
389 | return JNI_FALSE; |
390 | #endif |
391 | |
392 | #endif /* __solaris */ |
393 | |
394 | /* |
395 | * OK we may have the stack available in the kernel, |
396 | * we should also check if the APIs are available. |
397 | */ |
398 | ipv6_fn = JVM_FindLibraryEntry(RTLD_DEFAULT, "inet_pton" ); |
399 | close(fd); |
400 | if (ipv6_fn == NULL ) { |
401 | return JNI_FALSE; |
402 | } else { |
403 | return JNI_TRUE; |
404 | } |
405 | } |
406 | #endif /* DONT_ENABLE_IPV6 */ |
407 | |
408 | jint reuseport_supported() |
409 | { |
410 | /* Do a simple dummy call, and try to figure out from that */ |
411 | int one = 1; |
412 | int rv, s; |
413 | s = socket(PF_INET, SOCK_STREAM, 0); |
414 | if (s < 0) { |
415 | return JNI_FALSE; |
416 | } |
417 | rv = setsockopt(s, SOL_SOCKET, SO_REUSEPORT, (void *)&one, sizeof(one)); |
418 | if (rv != 0) { |
419 | rv = JNI_FALSE; |
420 | } else { |
421 | rv = JNI_TRUE; |
422 | } |
423 | close(s); |
424 | return rv; |
425 | } |
426 | |
427 | void NET_ThrowUnknownHostExceptionWithGaiError(JNIEnv *env, |
428 | const char* hostname, |
429 | int gai_error) |
430 | { |
431 | int size; |
432 | char *buf; |
433 | const char *format = "%s: %s" ; |
434 | const char *error_string = gai_strerror(gai_error); |
435 | if (error_string == NULL) |
436 | error_string = "unknown error" ; |
437 | |
438 | size = strlen(format) + strlen(hostname) + strlen(error_string) + 2; |
439 | buf = (char *) malloc(size); |
440 | if (buf) { |
441 | jstring s; |
442 | sprintf(buf, format, hostname, error_string); |
443 | s = JNU_NewStringPlatform(env, buf); |
444 | if (s != NULL) { |
445 | jobject x = JNU_NewObjectByName(env, |
446 | "java/net/UnknownHostException" , |
447 | "(Ljava/lang/String;)V" , s); |
448 | if (x != NULL) |
449 | (*env)->Throw(env, x); |
450 | } |
451 | free(buf); |
452 | } |
453 | } |
454 | |
455 | #if defined(_AIX) |
456 | |
457 | /* Initialize stubs for blocking I/O workarounds (see src/solaris/native/java/net/linux_close.c) */ |
458 | extern void aix_close_init(); |
459 | |
460 | void platformInit () { |
461 | aix_close_init(); |
462 | } |
463 | |
464 | #else |
465 | |
466 | void platformInit () {} |
467 | |
468 | #endif |
469 | |
470 | void parseExclusiveBindProperty(JNIEnv *env) { |
471 | #ifdef __solaris__ |
472 | jstring s, flagSet; |
473 | jclass iCls; |
474 | jmethodID mid; |
475 | |
476 | s = (*env)->NewStringUTF(env, "sun.net.useExclusiveBind" ); |
477 | CHECK_NULL(s); |
478 | iCls = (*env)->FindClass(env, "java/lang/System" ); |
479 | CHECK_NULL(iCls); |
480 | mid = (*env)->GetStaticMethodID(env, iCls, "getProperty" , |
481 | "(Ljava/lang/String;)Ljava/lang/String;" ); |
482 | CHECK_NULL(mid); |
483 | flagSet = (*env)->CallStaticObjectMethod(env, iCls, mid, s); |
484 | if (flagSet != NULL) { |
485 | useExclBind = 1; |
486 | } |
487 | #endif |
488 | } |
489 | |
490 | JNIEXPORT jint JNICALL |
491 | NET_EnableFastTcpLoopback(int fd) { |
492 | return 0; |
493 | } |
494 | |
495 | /** |
496 | * See net_util.h for documentation |
497 | */ |
498 | JNIEXPORT int JNICALL |
499 | NET_InetAddressToSockaddr(JNIEnv *env, jobject iaObj, int port, |
500 | SOCKETADDRESS *sa, int *len, |
501 | jboolean v4MappedAddress) |
502 | { |
503 | jint family = getInetAddress_family(env, iaObj); |
504 | JNU_CHECK_EXCEPTION_RETURN(env, -1); |
505 | memset((char *)sa, 0, sizeof(SOCKETADDRESS)); |
506 | |
507 | if (ipv6_available() && |
508 | !(family == java_net_InetAddress_IPv4 && |
509 | v4MappedAddress == JNI_FALSE)) |
510 | { |
511 | jbyte caddr[16]; |
512 | jint address; |
513 | |
514 | if (family == java_net_InetAddress_IPv4) { |
515 | // convert to IPv4-mapped address |
516 | memset((char *)caddr, 0, 16); |
517 | address = getInetAddress_addr(env, iaObj); |
518 | JNU_CHECK_EXCEPTION_RETURN(env, -1); |
519 | if (address == INADDR_ANY) { |
520 | /* we would always prefer IPv6 wildcard address |
521 | * caddr[10] = 0xff; |
522 | * caddr[11] = 0xff; */ |
523 | } else { |
524 | caddr[10] = 0xff; |
525 | caddr[11] = 0xff; |
526 | caddr[12] = ((address >> 24) & 0xff); |
527 | caddr[13] = ((address >> 16) & 0xff); |
528 | caddr[14] = ((address >> 8) & 0xff); |
529 | caddr[15] = (address & 0xff); |
530 | } |
531 | } else { |
532 | getInet6Address_ipaddress(env, iaObj, (char *)caddr); |
533 | } |
534 | sa->sa6.sin6_port = htons(port); |
535 | memcpy((void *)&sa->sa6.sin6_addr, caddr, sizeof(struct in6_addr)); |
536 | sa->sa6.sin6_family = AF_INET6; |
537 | if (len != NULL) { |
538 | *len = sizeof(struct sockaddr_in6); |
539 | } |
540 | |
541 | /* handle scope_id */ |
542 | if (family != java_net_InetAddress_IPv4) { |
543 | if (ia6_scopeidID) { |
544 | sa->sa6.sin6_scope_id = getInet6Address_scopeid(env, iaObj); |
545 | } |
546 | } |
547 | } else { |
548 | jint address; |
549 | if (family != java_net_InetAddress_IPv4) { |
550 | JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException" , "Protocol family unavailable" ); |
551 | return -1; |
552 | } |
553 | address = getInetAddress_addr(env, iaObj); |
554 | JNU_CHECK_EXCEPTION_RETURN(env, -1); |
555 | sa->sa4.sin_port = htons(port); |
556 | sa->sa4.sin_addr.s_addr = htonl(address); |
557 | sa->sa4.sin_family = AF_INET; |
558 | if (len != NULL) { |
559 | *len = sizeof(struct sockaddr_in); |
560 | } |
561 | } |
562 | return 0; |
563 | } |
564 | |
565 | void |
566 | NET_SetTrafficClass(SOCKETADDRESS *sa, int trafficClass) { |
567 | if (sa->sa.sa_family == AF_INET6) { |
568 | sa->sa6.sin6_flowinfo = htonl((trafficClass & 0xff) << 20); |
569 | } |
570 | } |
571 | |
572 | int |
573 | NET_IsIPv4Mapped(jbyte* caddr) { |
574 | int i; |
575 | for (i = 0; i < 10; i++) { |
576 | if (caddr[i] != 0x00) { |
577 | return 0; /* false */ |
578 | } |
579 | } |
580 | |
581 | if (((caddr[10] & 0xff) == 0xff) && ((caddr[11] & 0xff) == 0xff)) { |
582 | return 1; /* true */ |
583 | } |
584 | return 0; /* false */ |
585 | } |
586 | |
587 | int |
588 | NET_IPv4MappedToIPv4(jbyte* caddr) { |
589 | return ((caddr[12] & 0xff) << 24) | ((caddr[13] & 0xff) << 16) | ((caddr[14] & 0xff) << 8) |
590 | | (caddr[15] & 0xff); |
591 | } |
592 | |
593 | int |
594 | NET_IsEqual(jbyte* caddr1, jbyte* caddr2) { |
595 | int i; |
596 | for (i = 0; i < 16; i++) { |
597 | if (caddr1[i] != caddr2[i]) { |
598 | return 0; /* false */ |
599 | } |
600 | } |
601 | return 1; |
602 | } |
603 | |
604 | int NET_IsZeroAddr(jbyte* caddr) { |
605 | int i; |
606 | for (i = 0; i < 16; i++) { |
607 | if (caddr[i] != 0) { |
608 | return 0; |
609 | } |
610 | } |
611 | return 1; |
612 | } |
613 | |
614 | /* |
615 | * Map the Java level socket option to the platform specific |
616 | * level and option name. |
617 | */ |
618 | int |
619 | NET_MapSocketOption(jint cmd, int *level, int *optname) { |
620 | static struct { |
621 | jint cmd; |
622 | int level; |
623 | int optname; |
624 | } const opts[] = { |
625 | { java_net_SocketOptions_TCP_NODELAY, IPPROTO_TCP, TCP_NODELAY }, |
626 | { java_net_SocketOptions_SO_OOBINLINE, SOL_SOCKET, SO_OOBINLINE }, |
627 | { java_net_SocketOptions_SO_LINGER, SOL_SOCKET, SO_LINGER }, |
628 | { java_net_SocketOptions_SO_SNDBUF, SOL_SOCKET, SO_SNDBUF }, |
629 | { java_net_SocketOptions_SO_RCVBUF, SOL_SOCKET, SO_RCVBUF }, |
630 | { java_net_SocketOptions_SO_KEEPALIVE, SOL_SOCKET, SO_KEEPALIVE }, |
631 | { java_net_SocketOptions_SO_REUSEADDR, SOL_SOCKET, SO_REUSEADDR }, |
632 | { java_net_SocketOptions_SO_REUSEPORT, SOL_SOCKET, SO_REUSEPORT }, |
633 | { java_net_SocketOptions_SO_BROADCAST, SOL_SOCKET, SO_BROADCAST }, |
634 | { java_net_SocketOptions_IP_TOS, IPPROTO_IP, IP_TOS }, |
635 | { java_net_SocketOptions_IP_MULTICAST_IF, IPPROTO_IP, IP_MULTICAST_IF }, |
636 | { java_net_SocketOptions_IP_MULTICAST_IF2, IPPROTO_IP, IP_MULTICAST_IF }, |
637 | { java_net_SocketOptions_IP_MULTICAST_LOOP, IPPROTO_IP, IP_MULTICAST_LOOP }, |
638 | }; |
639 | |
640 | int i; |
641 | |
642 | if (ipv6_available()) { |
643 | switch (cmd) { |
644 | // Different multicast options if IPv6 is enabled |
645 | case java_net_SocketOptions_IP_MULTICAST_IF: |
646 | case java_net_SocketOptions_IP_MULTICAST_IF2: |
647 | *level = IPPROTO_IPV6; |
648 | *optname = IPV6_MULTICAST_IF; |
649 | return 0; |
650 | |
651 | case java_net_SocketOptions_IP_MULTICAST_LOOP: |
652 | *level = IPPROTO_IPV6; |
653 | *optname = IPV6_MULTICAST_LOOP; |
654 | return 0; |
655 | #if (defined(__solaris__) || defined(MACOSX)) |
656 | // Map IP_TOS request to IPV6_TCLASS |
657 | case java_net_SocketOptions_IP_TOS: |
658 | *level = IPPROTO_IPV6; |
659 | *optname = IPV6_TCLASS; |
660 | return 0; |
661 | #endif |
662 | } |
663 | } |
664 | |
665 | /* |
666 | * Map the Java level option to the native level |
667 | */ |
668 | for (i=0; i<(int)(sizeof(opts) / sizeof(opts[0])); i++) { |
669 | if (cmd == opts[i].cmd) { |
670 | *level = opts[i].level; |
671 | *optname = opts[i].optname; |
672 | return 0; |
673 | } |
674 | } |
675 | |
676 | /* not found */ |
677 | return -1; |
678 | } |
679 | |
680 | /* |
681 | * Wrapper for getsockopt system routine - does any necessary |
682 | * pre/post processing to deal with OS specific oddities :- |
683 | * |
684 | * On Linux the SO_SNDBUF/SO_RCVBUF values must be post-processed |
685 | * to compensate for an incorrect value returned by the kernel. |
686 | */ |
687 | int |
688 | NET_GetSockOpt(int fd, int level, int opt, void *result, |
689 | int *len) |
690 | { |
691 | int rv; |
692 | socklen_t socklen = *len; |
693 | |
694 | rv = getsockopt(fd, level, opt, result, &socklen); |
695 | *len = socklen; |
696 | |
697 | if (rv < 0) { |
698 | return rv; |
699 | } |
700 | |
701 | #ifdef __linux__ |
702 | /* |
703 | * On Linux SO_SNDBUF/SO_RCVBUF aren't symmetric. This |
704 | * stems from additional socket structures in the send |
705 | * and receive buffers. |
706 | */ |
707 | if ((level == SOL_SOCKET) && ((opt == SO_SNDBUF) |
708 | || (opt == SO_RCVBUF))) { |
709 | int n = *((int *)result); |
710 | n /= 2; |
711 | *((int *)result) = n; |
712 | } |
713 | #endif |
714 | |
715 | /* Workaround for Mac OS treating linger value as |
716 | * signed integer |
717 | */ |
718 | #ifdef MACOSX |
719 | if (level == SOL_SOCKET && opt == SO_LINGER) { |
720 | struct linger* to_cast = (struct linger*)result; |
721 | to_cast->l_linger = (unsigned short)to_cast->l_linger; |
722 | } |
723 | #endif |
724 | return rv; |
725 | } |
726 | |
727 | /* |
728 | * Wrapper for setsockopt system routine - performs any |
729 | * necessary pre/post processing to deal with OS specific |
730 | * issue :- |
731 | * |
732 | * On Solaris need to limit the suggested value for SO_SNDBUF |
733 | * and SO_RCVBUF to the kernel configured limit |
734 | * |
735 | * For IP_TOS socket option need to mask off bits as this |
736 | * aren't automatically masked by the kernel and results in |
737 | * an error. |
738 | */ |
739 | int |
740 | NET_SetSockOpt(int fd, int level, int opt, const void *arg, |
741 | int len) |
742 | { |
743 | |
744 | #ifndef IPTOS_TOS_MASK |
745 | #define IPTOS_TOS_MASK 0x1e |
746 | #endif |
747 | #ifndef IPTOS_PREC_MASK |
748 | #define IPTOS_PREC_MASK 0xe0 |
749 | #endif |
750 | |
751 | #if defined(_ALLBSD_SOURCE) |
752 | #if defined(KIPC_MAXSOCKBUF) |
753 | int mib[3]; |
754 | size_t rlen; |
755 | #endif |
756 | |
757 | int *bufsize; |
758 | |
759 | #ifdef __APPLE__ |
760 | static int maxsockbuf = -1; |
761 | #else |
762 | static long maxsockbuf = -1; |
763 | #endif |
764 | #endif |
765 | |
766 | /* |
767 | * IPPROTO/IP_TOS :- |
768 | * 1. IPv6 on Solaris/Mac OS: |
769 | * Set the TOS OR Traffic Class value to cater for |
770 | * IPv6 and IPv4 scenarios. |
771 | * 2. IPv6 on Linux: By default Linux ignores flowinfo |
772 | * field so enable IPV6_FLOWINFO_SEND so that flowinfo |
773 | * will be examined. We also set the IPv4 TOS option in this case. |
774 | * 3. IPv4: set socket option based on ToS and Precedence |
775 | * fields (otherwise get invalid argument) |
776 | */ |
777 | if (level == IPPROTO_IP && opt == IP_TOS) { |
778 | int *iptos; |
779 | |
780 | #if defined(__linux__) |
781 | if (ipv6_available()) { |
782 | int optval = 1; |
783 | if (setsockopt(fd, IPPROTO_IPV6, IPV6_FLOWINFO_SEND, |
784 | (void *)&optval, sizeof(optval)) < 0) { |
785 | return -1; |
786 | } |
787 | /* |
788 | * Let's also set the IPV6_TCLASS flag. |
789 | * Linux appears to allow both IP_TOS and IPV6_TCLASS to be set |
790 | * This helps in mixed environments where IPv4 and IPv6 sockets |
791 | * are connecting. |
792 | */ |
793 | if (setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, |
794 | arg, len) < 0) { |
795 | return -1; |
796 | } |
797 | } |
798 | #endif |
799 | |
800 | iptos = (int *)arg; |
801 | *iptos &= (IPTOS_TOS_MASK | IPTOS_PREC_MASK); |
802 | } |
803 | |
804 | /* |
805 | * SOL_SOCKET/{SO_SNDBUF,SO_RCVBUF} - On Solaris we may need to clamp |
806 | * the value when it exceeds the system limit. |
807 | */ |
808 | #ifdef __solaris__ |
809 | if (level == SOL_SOCKET) { |
810 | if (opt == SO_SNDBUF || opt == SO_RCVBUF) { |
811 | int sotype=0; |
812 | socklen_t arglen; |
813 | int *bufsize, maxbuf; |
814 | int ret; |
815 | |
816 | /* Attempt with the original size */ |
817 | ret = setsockopt(fd, level, opt, arg, len); |
818 | if ((ret == 0) || (ret == -1 && errno != ENOBUFS)) |
819 | return ret; |
820 | |
821 | /* Exceeded system limit so clamp and retry */ |
822 | |
823 | arglen = sizeof(sotype); |
824 | if (getsockopt(fd, SOL_SOCKET, SO_TYPE, (void *)&sotype, |
825 | &arglen) < 0) { |
826 | return -1; |
827 | } |
828 | |
829 | /* |
830 | * We try to get tcp_maxbuf (and udp_max_buf) using |
831 | * an ioctl() that isn't available on all versions of Solaris. |
832 | * If that fails, we use the search algorithm in findMaxBuf() |
833 | */ |
834 | if (!init_tcp_max_buf && sotype == SOCK_STREAM) { |
835 | tcp_max_buf = net_getParam("/dev/tcp" , "tcp_max_buf" ); |
836 | if (tcp_max_buf == -1) { |
837 | tcp_max_buf = findMaxBuf(fd, opt, SOCK_STREAM); |
838 | if (tcp_max_buf == -1) { |
839 | return -1; |
840 | } |
841 | } |
842 | init_tcp_max_buf = 1; |
843 | } else if (!init_udp_max_buf && sotype == SOCK_DGRAM) { |
844 | udp_max_buf = net_getParam("/dev/udp" , "udp_max_buf" ); |
845 | if (udp_max_buf == -1) { |
846 | udp_max_buf = findMaxBuf(fd, opt, SOCK_DGRAM); |
847 | if (udp_max_buf == -1) { |
848 | return -1; |
849 | } |
850 | } |
851 | init_udp_max_buf = 1; |
852 | } |
853 | |
854 | maxbuf = (sotype == SOCK_STREAM) ? tcp_max_buf : udp_max_buf; |
855 | bufsize = (int *)arg; |
856 | if (*bufsize > maxbuf) { |
857 | *bufsize = maxbuf; |
858 | } |
859 | } |
860 | } |
861 | #endif |
862 | |
863 | #ifdef _AIX |
864 | if (level == SOL_SOCKET) { |
865 | if (opt == SO_SNDBUF || opt == SO_RCVBUF) { |
866 | /* |
867 | * Just try to set the requested size. If it fails we will leave the |
868 | * socket option as is. Setting the buffer size means only a hint in |
869 | * the jse2/java software layer, see javadoc. In the previous |
870 | * solution the buffer has always been truncated to a length of |
871 | * 0x100000 Byte, even if the technical limit has not been reached. |
872 | * This kind of absolute truncation was unexpected in the jck tests. |
873 | */ |
874 | int ret = setsockopt(fd, level, opt, arg, len); |
875 | if ((ret == 0) || (ret == -1 && errno == ENOBUFS)) { |
876 | // Accept failure because of insufficient buffer memory resources. |
877 | return 0; |
878 | } else { |
879 | // Deliver all other kinds of errors. |
880 | return ret; |
881 | } |
882 | } |
883 | } |
884 | #endif |
885 | |
886 | /* |
887 | * On Linux the receive buffer is used for both socket |
888 | * structures and the packet payload. The implication |
889 | * is that if SO_RCVBUF is too small then small packets |
890 | * must be discarded. |
891 | */ |
892 | #ifdef __linux__ |
893 | if (level == SOL_SOCKET && opt == SO_RCVBUF) { |
894 | int *bufsize = (int *)arg; |
895 | if (*bufsize < 1024) { |
896 | *bufsize = 1024; |
897 | } |
898 | } |
899 | #endif |
900 | |
901 | #if defined(_ALLBSD_SOURCE) |
902 | /* |
903 | * SOL_SOCKET/{SO_SNDBUF,SO_RCVBUF} - On FreeBSD need to |
904 | * ensure that value is <= kern.ipc.maxsockbuf as otherwise we get |
905 | * an ENOBUFS error. |
906 | */ |
907 | if (level == SOL_SOCKET) { |
908 | if (opt == SO_SNDBUF || opt == SO_RCVBUF) { |
909 | #ifdef KIPC_MAXSOCKBUF |
910 | if (maxsockbuf == -1) { |
911 | mib[0] = CTL_KERN; |
912 | mib[1] = KERN_IPC; |
913 | mib[2] = KIPC_MAXSOCKBUF; |
914 | rlen = sizeof(maxsockbuf); |
915 | if (sysctl(mib, 3, &maxsockbuf, &rlen, NULL, 0) == -1) |
916 | maxsockbuf = 1024; |
917 | |
918 | #if 1 |
919 | /* XXXBSD: This is a hack to workaround mb_max/mb_max_adj |
920 | problem. It should be removed when kern.ipc.maxsockbuf |
921 | will be real value. */ |
922 | maxsockbuf = (maxsockbuf/5)*4; |
923 | #endif |
924 | } |
925 | #elif defined(__OpenBSD__) |
926 | maxsockbuf = SB_MAX; |
927 | #else |
928 | maxsockbuf = 64 * 1024; /* XXX: NetBSD */ |
929 | #endif |
930 | |
931 | bufsize = (int *)arg; |
932 | if (*bufsize > maxsockbuf) { |
933 | *bufsize = maxsockbuf; |
934 | } |
935 | |
936 | if (opt == SO_RCVBUF && *bufsize < 1024) { |
937 | *bufsize = 1024; |
938 | } |
939 | |
940 | } |
941 | } |
942 | #endif |
943 | |
944 | #if defined(_ALLBSD_SOURCE) || defined(_AIX) |
945 | /* |
946 | * On Solaris, SO_REUSEADDR will allow multiple datagram |
947 | * sockets to bind to the same port. The network jck tests check |
948 | * for this "feature", so we need to emulate it by turning on |
949 | * SO_REUSEPORT as well for that combination. |
950 | */ |
951 | if (level == SOL_SOCKET && opt == SO_REUSEADDR) { |
952 | int sotype; |
953 | socklen_t arglen; |
954 | |
955 | arglen = sizeof(sotype); |
956 | if (getsockopt(fd, SOL_SOCKET, SO_TYPE, (void *)&sotype, &arglen) < 0) { |
957 | return -1; |
958 | } |
959 | |
960 | if (sotype == SOCK_DGRAM) { |
961 | setsockopt(fd, level, SO_REUSEPORT, arg, len); |
962 | } |
963 | } |
964 | #endif |
965 | |
966 | return setsockopt(fd, level, opt, arg, len); |
967 | } |
968 | |
969 | /* |
970 | * Wrapper for bind system call - performs any necessary pre/post |
971 | * processing to deal with OS specific issues :- |
972 | * |
973 | * Linux allows a socket to bind to 127.0.0.255 which must be |
974 | * caught. |
975 | * |
976 | * On Solaris with IPv6 enabled we must use an exclusive |
977 | * bind to guarantee a unique port number across the IPv4 and |
978 | * IPv6 port spaces. |
979 | * |
980 | */ |
981 | int |
982 | NET_Bind(int fd, SOCKETADDRESS *sa, int len) |
983 | { |
984 | #if defined(__solaris__) |
985 | int level = -1; |
986 | int exclbind = -1; |
987 | #endif |
988 | int rv; |
989 | int arg, alen; |
990 | |
991 | #ifdef __linux__ |
992 | /* |
993 | * ## get bugId for this issue - goes back to 1.2.2 port ## |
994 | * ## When IPv6 is enabled this will be an IPv4-mapped |
995 | * ## with family set to AF_INET6 |
996 | */ |
997 | if (sa->sa.sa_family == AF_INET) { |
998 | if ((ntohl(sa->sa4.sin_addr.s_addr) & 0x7f0000ff) == 0x7f0000ff) { |
999 | errno = EADDRNOTAVAIL; |
1000 | return -1; |
1001 | } |
1002 | } |
1003 | #endif |
1004 | |
1005 | #if defined(__solaris__) |
1006 | /* |
1007 | * Solaris has separate IPv4 and IPv6 port spaces so we |
1008 | * use an exclusive bind when SO_REUSEADDR is not used to |
1009 | * give the illusion of a unified port space. |
1010 | * This also avoids problems with IPv6 sockets connecting |
1011 | * to IPv4 mapped addresses whereby the socket conversion |
1012 | * results in a late bind that fails because the |
1013 | * corresponding IPv4 port is in use. |
1014 | */ |
1015 | alen = sizeof(arg); |
1016 | |
1017 | if (useExclBind || |
1018 | getsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&arg, &alen) == 0) |
1019 | { |
1020 | if (useExclBind || arg == 0) { |
1021 | /* |
1022 | * SO_REUSEADDR is disabled or sun.net.useExclusiveBind |
1023 | * property is true so enable TCP_EXCLBIND or |
1024 | * UDP_EXCLBIND |
1025 | */ |
1026 | alen = sizeof(arg); |
1027 | if (getsockopt(fd, SOL_SOCKET, SO_TYPE, (char *)&arg, &alen) == 0) |
1028 | { |
1029 | if (arg == SOCK_STREAM) { |
1030 | level = IPPROTO_TCP; |
1031 | exclbind = TCP_EXCLBIND; |
1032 | } else { |
1033 | level = IPPROTO_UDP; |
1034 | exclbind = UDP_EXCLBIND; |
1035 | } |
1036 | } |
1037 | |
1038 | arg = 1; |
1039 | setsockopt(fd, level, exclbind, (char *)&arg, sizeof(arg)); |
1040 | } |
1041 | } |
1042 | |
1043 | #endif |
1044 | |
1045 | rv = bind(fd, &sa->sa, len); |
1046 | |
1047 | #if defined(__solaris__) |
1048 | if (rv < 0) { |
1049 | int en = errno; |
1050 | /* Restore *_EXCLBIND if the bind fails */ |
1051 | if (exclbind != -1) { |
1052 | int arg = 0; |
1053 | setsockopt(fd, level, exclbind, (char *)&arg, |
1054 | sizeof(arg)); |
1055 | } |
1056 | errno = en; |
1057 | } |
1058 | #endif |
1059 | |
1060 | return rv; |
1061 | } |
1062 | |
1063 | /** |
1064 | * Wrapper for poll with timeout on a single file descriptor. |
1065 | * |
1066 | * flags (defined in net_util_md.h can be any combination of |
1067 | * NET_WAIT_READ, NET_WAIT_WRITE & NET_WAIT_CONNECT. |
1068 | * |
1069 | * The function will return when either the socket is ready for one |
1070 | * of the specified operations or the timeout expired. |
1071 | * |
1072 | * It returns the time left from the timeout (possibly 0), or -1 if it expired. |
1073 | */ |
1074 | |
1075 | jint |
1076 | NET_Wait(JNIEnv *env, jint fd, jint flags, jint timeout) |
1077 | { |
1078 | jlong prevNanoTime = JVM_NanoTime(env, 0); |
1079 | jlong nanoTimeout = (jlong) timeout * NET_NSEC_PER_MSEC; |
1080 | jint read_rv; |
1081 | |
1082 | while (1) { |
1083 | jlong newNanoTime; |
1084 | struct pollfd pfd; |
1085 | pfd.fd = fd; |
1086 | pfd.events = 0; |
1087 | if (flags & NET_WAIT_READ) |
1088 | pfd.events |= POLLIN; |
1089 | if (flags & NET_WAIT_WRITE) |
1090 | pfd.events |= POLLOUT; |
1091 | if (flags & NET_WAIT_CONNECT) |
1092 | pfd.events |= POLLOUT; |
1093 | |
1094 | errno = 0; |
1095 | read_rv = NET_Poll(&pfd, 1, nanoTimeout / NET_NSEC_PER_MSEC); |
1096 | |
1097 | newNanoTime = JVM_NanoTime(env, 0); |
1098 | nanoTimeout -= (newNanoTime - prevNanoTime); |
1099 | if (nanoTimeout < NET_NSEC_PER_MSEC) { |
1100 | return read_rv > 0 ? 0 : -1; |
1101 | } |
1102 | prevNanoTime = newNanoTime; |
1103 | |
1104 | if (read_rv > 0) { |
1105 | break; |
1106 | } |
1107 | } /* while */ |
1108 | return (nanoTimeout / NET_NSEC_PER_MSEC); |
1109 | } |
1110 | |