1 | /* |
2 | * Copyright (c) 2001, 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 | |
26 | #include <poll.h> |
27 | #include <sys/types.h> |
28 | #include <sys/socket.h> |
29 | #include <string.h> |
30 | #include <netinet/in.h> |
31 | #include <netinet/tcp.h> |
32 | #include <limits.h> |
33 | |
34 | #include "jni.h" |
35 | #include "jni_util.h" |
36 | #include "jvm.h" |
37 | #include "jlong.h" |
38 | #include "sun_nio_ch_Net.h" |
39 | #include "net_util.h" |
40 | #include "net_util_md.h" |
41 | #include "nio_util.h" |
42 | #include "nio.h" |
43 | |
44 | #ifdef _AIX |
45 | #include <sys/utsname.h> |
46 | #endif |
47 | |
48 | /** |
49 | * IP_MULTICAST_ALL supported since 2.6.31 but may not be available at |
50 | * build time. |
51 | */ |
52 | #ifdef __linux__ |
53 | #ifndef IP_MULTICAST_ALL |
54 | #define IP_MULTICAST_ALL 49 |
55 | #endif |
56 | #endif |
57 | |
58 | /** |
59 | * IPV6_ADD_MEMBERSHIP/IPV6_DROP_MEMBERSHIP may not be defined on OSX and AIX |
60 | */ |
61 | #if defined(__APPLE__) || defined(_AIX) |
62 | #ifndef IPV6_ADD_MEMBERSHIP |
63 | #define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP |
64 | #define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP |
65 | #endif |
66 | #endif |
67 | |
68 | #if defined(_AIX) |
69 | #ifndef IP_BLOCK_SOURCE |
70 | #define IP_BLOCK_SOURCE 58 /* Block data from a given source to a given group */ |
71 | #define IP_UNBLOCK_SOURCE 59 /* Unblock data from a given source to a given group */ |
72 | #define IP_ADD_SOURCE_MEMBERSHIP 60 /* Join a source-specific group */ |
73 | #define IP_DROP_SOURCE_MEMBERSHIP 61 /* Leave a source-specific group */ |
74 | #endif |
75 | |
76 | #ifndef MCAST_BLOCK_SOURCE |
77 | #define MCAST_BLOCK_SOURCE 64 |
78 | #define MCAST_UNBLOCK_SOURCE 65 |
79 | #define MCAST_JOIN_SOURCE_GROUP 66 |
80 | #define MCAST_LEAVE_SOURCE_GROUP 67 |
81 | |
82 | /* This means we're on AIX 5.3 and 'group_source_req' and 'ip_mreq_source' aren't defined as well */ |
83 | struct group_source_req { |
84 | uint32_t gsr_interface; |
85 | struct sockaddr_storage gsr_group; |
86 | struct sockaddr_storage gsr_source; |
87 | }; |
88 | struct ip_mreq_source { |
89 | struct in_addr imr_multiaddr; /* IP multicast address of group */ |
90 | struct in_addr imr_sourceaddr; /* IP address of source */ |
91 | struct in_addr imr_interface; /* local IP address of interface */ |
92 | }; |
93 | #endif |
94 | #endif /* _AIX */ |
95 | |
96 | #define COPY_INET6_ADDRESS(env, source, target) \ |
97 | (*env)->GetByteArrayRegion(env, source, 0, 16, target) |
98 | |
99 | /* |
100 | * Copy IPv6 group, interface index, and IPv6 source address |
101 | * into group_source_req structure. |
102 | */ |
103 | static void initGroupSourceReq(JNIEnv* env, jbyteArray group, jint index, |
104 | jbyteArray source, struct group_source_req *req) |
105 | { |
106 | struct sockaddr_in6* sin6; |
107 | |
108 | req->gsr_interface = (uint32_t)index; |
109 | |
110 | sin6 = (struct sockaddr_in6 *)&(req->gsr_group); |
111 | sin6->sin6_family = AF_INET6; |
112 | COPY_INET6_ADDRESS(env, group, (jbyte *)&(sin6->sin6_addr)); |
113 | |
114 | sin6 = (struct sockaddr_in6 *)&(req->gsr_source); |
115 | sin6->sin6_family = AF_INET6; |
116 | COPY_INET6_ADDRESS(env, source, (jbyte *)&(sin6->sin6_addr)); |
117 | } |
118 | |
119 | #ifdef _AIX |
120 | |
121 | /* |
122 | * Checks whether or not "socket extensions for multicast source filters" is supported. |
123 | * Returns JNI_TRUE if it is supported, JNI_FALSE otherwise |
124 | */ |
125 | static jboolean isSourceFilterSupported(){ |
126 | static jboolean alreadyChecked = JNI_FALSE; |
127 | static jboolean result = JNI_TRUE; |
128 | if (alreadyChecked != JNI_TRUE){ |
129 | struct utsname uts; |
130 | memset(&uts, 0, sizeof(uts)); |
131 | strcpy(uts.sysname, "?" ); |
132 | const int utsRes = uname(&uts); |
133 | int major = -1; |
134 | int minor = -1; |
135 | major = atoi(uts.version); |
136 | minor = atoi(uts.release); |
137 | if (strcmp(uts.sysname, "AIX" ) == 0) { |
138 | if (major < 6 || (major == 6 && minor < 1)) {// unsupported on aix < 6.1 |
139 | result = JNI_FALSE; |
140 | } |
141 | } |
142 | alreadyChecked = JNI_TRUE; |
143 | } |
144 | return result; |
145 | } |
146 | |
147 | #endif /* _AIX */ |
148 | |
149 | static jclass isa_class; /* java.net.InetSocketAddress */ |
150 | static jmethodID isa_ctorID; /* InetSocketAddress(InetAddress, int) */ |
151 | |
152 | JNIEXPORT void JNICALL |
153 | Java_sun_nio_ch_Net_initIDs(JNIEnv *env, jclass clazz) |
154 | { |
155 | jclass cls = (*env)->FindClass(env, "java/net/InetSocketAddress" ); |
156 | CHECK_NULL(cls); |
157 | isa_class = (*env)->NewGlobalRef(env, cls); |
158 | if (isa_class == NULL) { |
159 | JNU_ThrowOutOfMemoryError(env, NULL); |
160 | return; |
161 | } |
162 | isa_ctorID = (*env)->GetMethodID(env, cls, "<init>" , "(Ljava/net/InetAddress;I)V" ); |
163 | CHECK_NULL(isa_ctorID); |
164 | |
165 | initInetAddressIDs(env); |
166 | } |
167 | |
168 | JNIEXPORT jboolean JNICALL |
169 | Java_sun_nio_ch_Net_isIPv6Available0(JNIEnv* env, jclass cl) |
170 | { |
171 | return (ipv6_available()) ? JNI_TRUE : JNI_FALSE; |
172 | } |
173 | |
174 | JNIEXPORT jboolean JNICALL |
175 | Java_sun_nio_ch_Net_isReusePortAvailable0(JNIEnv* env, jclass c1) |
176 | { |
177 | return (reuseport_available()) ? JNI_TRUE : JNI_FALSE; |
178 | } |
179 | |
180 | JNIEXPORT jint JNICALL |
181 | Java_sun_nio_ch_Net_isExclusiveBindAvailable(JNIEnv *env, jclass clazz) { |
182 | return -1; |
183 | } |
184 | |
185 | JNIEXPORT jboolean JNICALL |
186 | Java_sun_nio_ch_Net_canIPv6SocketJoinIPv4Group0(JNIEnv* env, jclass cl) |
187 | { |
188 | #if defined(__APPLE__) || defined(_AIX) |
189 | /* for now IPv6 sockets cannot join IPv4 multicast groups */ |
190 | return JNI_FALSE; |
191 | #else |
192 | return JNI_TRUE; |
193 | #endif |
194 | } |
195 | |
196 | JNIEXPORT jboolean JNICALL |
197 | Java_sun_nio_ch_Net_canJoin6WithIPv4Group0(JNIEnv* env, jclass cl) |
198 | { |
199 | #ifdef __solaris__ |
200 | return JNI_TRUE; |
201 | #else |
202 | return JNI_FALSE; |
203 | #endif |
204 | } |
205 | |
206 | JNIEXPORT jint JNICALL |
207 | Java_sun_nio_ch_Net_socket0(JNIEnv *env, jclass cl, jboolean preferIPv6, |
208 | jboolean stream, jboolean reuse, jboolean ignored) |
209 | { |
210 | int fd; |
211 | int type = (stream ? SOCK_STREAM : SOCK_DGRAM); |
212 | int domain = (ipv6_available() && preferIPv6) ? AF_INET6 : AF_INET; |
213 | |
214 | fd = socket(domain, type, 0); |
215 | if (fd < 0) { |
216 | return handleSocketError(env, errno); |
217 | } |
218 | |
219 | /* |
220 | * If IPv4 is available, disable IPV6_V6ONLY to ensure dual-socket support. |
221 | */ |
222 | if (domain == AF_INET6 && ipv4_available()) { |
223 | int arg = 0; |
224 | if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&arg, |
225 | sizeof(int)) < 0) { |
226 | JNU_ThrowByNameWithLastError(env, |
227 | JNU_JAVANETPKG "SocketException" , |
228 | "Unable to set IPV6_V6ONLY" ); |
229 | close(fd); |
230 | return -1; |
231 | } |
232 | } |
233 | |
234 | if (reuse) { |
235 | int arg = 1; |
236 | if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&arg, |
237 | sizeof(arg)) < 0) { |
238 | JNU_ThrowByNameWithLastError(env, |
239 | JNU_JAVANETPKG "SocketException" , |
240 | "Unable to set SO_REUSEADDR" ); |
241 | close(fd); |
242 | return -1; |
243 | } |
244 | } |
245 | |
246 | #if defined(__linux__) |
247 | if (type == SOCK_DGRAM) { |
248 | int arg = 0; |
249 | int level = (domain == AF_INET6) ? IPPROTO_IPV6 : IPPROTO_IP; |
250 | if ((setsockopt(fd, level, IP_MULTICAST_ALL, (char*)&arg, sizeof(arg)) < 0) && |
251 | (errno != ENOPROTOOPT)) { |
252 | JNU_ThrowByNameWithLastError(env, |
253 | JNU_JAVANETPKG "SocketException" , |
254 | "Unable to set IP_MULTICAST_ALL" ); |
255 | close(fd); |
256 | return -1; |
257 | } |
258 | } |
259 | |
260 | /* By default, Linux uses the route default */ |
261 | if (domain == AF_INET6 && type == SOCK_DGRAM) { |
262 | int arg = 1; |
263 | if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &arg, |
264 | sizeof(arg)) < 0) { |
265 | JNU_ThrowByNameWithLastError(env, |
266 | JNU_JAVANETPKG "SocketException" , |
267 | "Unable to set IPV6_MULTICAST_HOPS" ); |
268 | close(fd); |
269 | return -1; |
270 | } |
271 | } |
272 | #endif |
273 | return fd; |
274 | } |
275 | |
276 | JNIEXPORT void JNICALL |
277 | Java_sun_nio_ch_Net_bind0(JNIEnv *env, jclass clazz, jobject fdo, jboolean preferIPv6, |
278 | jboolean useExclBind, jobject iao, int port) |
279 | { |
280 | SOCKETADDRESS sa; |
281 | int sa_len = 0; |
282 | int rv = 0; |
283 | |
284 | if (NET_InetAddressToSockaddr(env, iao, port, &sa, &sa_len, |
285 | preferIPv6) != 0) { |
286 | return; |
287 | } |
288 | |
289 | rv = NET_Bind(fdval(env, fdo), &sa, sa_len); |
290 | if (rv != 0) { |
291 | handleSocketError(env, errno); |
292 | } |
293 | } |
294 | |
295 | JNIEXPORT void JNICALL |
296 | Java_sun_nio_ch_Net_listen(JNIEnv *env, jclass cl, jobject fdo, jint backlog) |
297 | { |
298 | if (listen(fdval(env, fdo), backlog) < 0) |
299 | handleSocketError(env, errno); |
300 | } |
301 | |
302 | JNIEXPORT jint JNICALL |
303 | Java_sun_nio_ch_Net_connect0(JNIEnv *env, jclass clazz, jboolean preferIPv6, |
304 | jobject fdo, jobject iao, jint port) |
305 | { |
306 | SOCKETADDRESS sa; |
307 | int sa_len = 0; |
308 | int rv; |
309 | |
310 | if (NET_InetAddressToSockaddr(env, iao, port, &sa, &sa_len, preferIPv6) != 0) { |
311 | return IOS_THROWN; |
312 | } |
313 | |
314 | rv = connect(fdval(env, fdo), &sa.sa, sa_len); |
315 | if (rv != 0) { |
316 | if (errno == EINPROGRESS) { |
317 | return IOS_UNAVAILABLE; |
318 | } else if (errno == EINTR) { |
319 | return IOS_INTERRUPTED; |
320 | } |
321 | return handleSocketError(env, errno); |
322 | } |
323 | return 1; |
324 | } |
325 | |
326 | JNIEXPORT jint JNICALL |
327 | Java_sun_nio_ch_Net_accept(JNIEnv *env, jclass clazz, jobject fdo, jobject newfdo, |
328 | jobjectArray isaa) |
329 | { |
330 | jint fd = fdval(env, fdo); |
331 | jint newfd; |
332 | SOCKETADDRESS sa; |
333 | socklen_t sa_len = sizeof(SOCKETADDRESS); |
334 | jobject remote_ia; |
335 | jint remote_port = 0; |
336 | jobject isa; |
337 | |
338 | /* accept connection but ignore ECONNABORTED */ |
339 | for (;;) { |
340 | newfd = accept(fd, &sa.sa, &sa_len); |
341 | if (newfd >= 0) { |
342 | break; |
343 | } |
344 | if (errno != ECONNABORTED) { |
345 | break; |
346 | } |
347 | /* ECONNABORTED => restart accept */ |
348 | } |
349 | |
350 | if (newfd < 0) { |
351 | if (errno == EAGAIN || errno == EWOULDBLOCK) |
352 | return IOS_UNAVAILABLE; |
353 | if (errno == EINTR) |
354 | return IOS_INTERRUPTED; |
355 | JNU_ThrowIOExceptionWithLastError(env, "Accept failed" ); |
356 | return IOS_THROWN; |
357 | } |
358 | |
359 | setfdval(env, newfdo, newfd); |
360 | |
361 | remote_ia = NET_SockaddrToInetAddress(env, &sa, (int *)&remote_port); |
362 | CHECK_NULL_RETURN(remote_ia, IOS_THROWN); |
363 | |
364 | isa = (*env)->NewObject(env, isa_class, isa_ctorID, remote_ia, remote_port); |
365 | CHECK_NULL_RETURN(isa, IOS_THROWN); |
366 | (*env)->SetObjectArrayElement(env, isaa, 0, isa); |
367 | |
368 | return 1; |
369 | } |
370 | |
371 | JNIEXPORT jint JNICALL |
372 | Java_sun_nio_ch_Net_localPort(JNIEnv *env, jclass clazz, jobject fdo) |
373 | { |
374 | SOCKETADDRESS sa; |
375 | socklen_t sa_len = sizeof(SOCKETADDRESS); |
376 | if (getsockname(fdval(env, fdo), &sa.sa, &sa_len) < 0) { |
377 | #ifdef _ALLBSD_SOURCE |
378 | /* |
379 | * XXXBSD: |
380 | * ECONNRESET is specific to the BSDs. We can not return an error, |
381 | * as the calling Java code with raise a java.lang.Error given the expectation |
382 | * that getsockname() will never fail. According to the Single UNIX Specification, |
383 | * it shouldn't fail. As such, we just fill in generic Linux-compatible values. |
384 | */ |
385 | if (errno == ECONNRESET) { |
386 | bzero(&sa.sa4, sizeof(sa)); |
387 | sa.sa4.sin_len = sizeof(struct sockaddr_in); |
388 | sa.sa4.sin_family = AF_INET; |
389 | sa.sa4.sin_port = htonl(0); |
390 | sa.sa4.sin_addr.s_addr = INADDR_ANY; |
391 | } else { |
392 | handleSocketError(env, errno); |
393 | return -1; |
394 | } |
395 | #else /* _ALLBSD_SOURCE */ |
396 | handleSocketError(env, errno); |
397 | return -1; |
398 | #endif /* _ALLBSD_SOURCE */ |
399 | } |
400 | return NET_GetPortFromSockaddr(&sa); |
401 | } |
402 | |
403 | JNIEXPORT jobject JNICALL |
404 | Java_sun_nio_ch_Net_localInetAddress(JNIEnv *env, jclass clazz, jobject fdo) |
405 | { |
406 | SOCKETADDRESS sa; |
407 | socklen_t sa_len = sizeof(SOCKETADDRESS); |
408 | int port; |
409 | if (getsockname(fdval(env, fdo), &sa.sa, &sa_len) < 0) { |
410 | #ifdef _ALLBSD_SOURCE |
411 | /* |
412 | * XXXBSD: |
413 | * ECONNRESET is specific to the BSDs. We can not return an error, |
414 | * as the calling Java code with raise a java.lang.Error with the expectation |
415 | * that getsockname() will never fail. According to the Single UNIX Specification, |
416 | * it shouldn't fail. As such, we just fill in generic Linux-compatible values. |
417 | */ |
418 | if (errno == ECONNRESET) { |
419 | bzero(&sa.sa4, sizeof(sa)); |
420 | sa.sa4.sin_len = sizeof(struct sockaddr_in); |
421 | sa.sa4.sin_family = AF_INET; |
422 | sa.sa4.sin_port = htonl(0); |
423 | sa.sa4.sin_addr.s_addr = INADDR_ANY; |
424 | } else { |
425 | handleSocketError(env, errno); |
426 | return NULL; |
427 | } |
428 | #else /* _ALLBSD_SOURCE */ |
429 | handleSocketError(env, errno); |
430 | return NULL; |
431 | #endif /* _ALLBSD_SOURCE */ |
432 | } |
433 | return NET_SockaddrToInetAddress(env, &sa, &port); |
434 | } |
435 | |
436 | JNIEXPORT jint JNICALL |
437 | Java_sun_nio_ch_Net_remotePort(JNIEnv *env, jclass clazz, jobject fdo) |
438 | { |
439 | SOCKETADDRESS sa; |
440 | socklen_t sa_len = sizeof(sa); |
441 | |
442 | if (getpeername(fdval(env, fdo), &sa.sa, &sa_len) < 0) { |
443 | handleSocketError(env, errno); |
444 | return IOS_THROWN; |
445 | } |
446 | return NET_GetPortFromSockaddr(&sa); |
447 | } |
448 | |
449 | JNIEXPORT jobject JNICALL |
450 | Java_sun_nio_ch_Net_remoteInetAddress(JNIEnv *env, jclass clazz, jobject fdo) |
451 | { |
452 | SOCKETADDRESS sa; |
453 | socklen_t sa_len = sizeof(sa); |
454 | int port; |
455 | |
456 | if (getpeername(fdval(env, fdo), &sa.sa, &sa_len) < 0) { |
457 | handleSocketError(env, errno); |
458 | return NULL; |
459 | } |
460 | return NET_SockaddrToInetAddress(env, &sa, &port); |
461 | } |
462 | |
463 | JNIEXPORT jint JNICALL |
464 | Java_sun_nio_ch_Net_getIntOption0(JNIEnv *env, jclass clazz, jobject fdo, |
465 | jboolean mayNeedConversion, jint level, jint opt) |
466 | { |
467 | int result; |
468 | struct linger linger; |
469 | u_char carg; |
470 | void *arg; |
471 | socklen_t arglen; |
472 | int n; |
473 | |
474 | /* Option value is an int except for a few specific cases */ |
475 | |
476 | arg = (void *)&result; |
477 | arglen = sizeof(result); |
478 | |
479 | if (level == IPPROTO_IP && |
480 | (opt == IP_MULTICAST_TTL || opt == IP_MULTICAST_LOOP)) { |
481 | arg = (void*)&carg; |
482 | arglen = sizeof(carg); |
483 | } |
484 | |
485 | if (level == SOL_SOCKET && opt == SO_LINGER) { |
486 | arg = (void *)&linger; |
487 | arglen = sizeof(linger); |
488 | } |
489 | |
490 | if (mayNeedConversion) { |
491 | n = NET_GetSockOpt(fdval(env, fdo), level, opt, arg, (int*)&arglen); |
492 | } else { |
493 | n = getsockopt(fdval(env, fdo), level, opt, arg, &arglen); |
494 | } |
495 | if (n < 0) { |
496 | JNU_ThrowByNameWithLastError(env, |
497 | JNU_JAVANETPKG "SocketException" , |
498 | "sun.nio.ch.Net.getIntOption" ); |
499 | return -1; |
500 | } |
501 | |
502 | if (level == IPPROTO_IP && |
503 | (opt == IP_MULTICAST_TTL || opt == IP_MULTICAST_LOOP)) |
504 | { |
505 | return (jint)carg; |
506 | } |
507 | |
508 | if (level == SOL_SOCKET && opt == SO_LINGER) |
509 | return linger.l_onoff ? (jint)linger.l_linger : (jint)-1; |
510 | |
511 | return (jint)result; |
512 | } |
513 | |
514 | JNIEXPORT void JNICALL |
515 | Java_sun_nio_ch_Net_setIntOption0(JNIEnv *env, jclass clazz, jobject fdo, |
516 | jboolean mayNeedConversion, jint level, |
517 | jint opt, jint arg, jboolean isIPv6) |
518 | { |
519 | int result; |
520 | struct linger linger; |
521 | u_char carg; |
522 | void *parg; |
523 | socklen_t arglen; |
524 | int n; |
525 | |
526 | /* Option value is an int except for a few specific cases */ |
527 | |
528 | parg = (void*)&arg; |
529 | arglen = sizeof(arg); |
530 | |
531 | if (level == IPPROTO_IP && |
532 | (opt == IP_MULTICAST_TTL || opt == IP_MULTICAST_LOOP)) { |
533 | parg = (void*)&carg; |
534 | arglen = sizeof(carg); |
535 | carg = (u_char)arg; |
536 | } |
537 | |
538 | if (level == SOL_SOCKET && opt == SO_LINGER) { |
539 | parg = (void *)&linger; |
540 | arglen = sizeof(linger); |
541 | if (arg >= 0) { |
542 | linger.l_onoff = 1; |
543 | linger.l_linger = arg; |
544 | } else { |
545 | linger.l_onoff = 0; |
546 | linger.l_linger = 0; |
547 | } |
548 | } |
549 | |
550 | if (mayNeedConversion) { |
551 | n = NET_SetSockOpt(fdval(env, fdo), level, opt, parg, arglen); |
552 | } else { |
553 | n = setsockopt(fdval(env, fdo), level, opt, parg, arglen); |
554 | } |
555 | if (n < 0) { |
556 | JNU_ThrowByNameWithLastError(env, |
557 | JNU_JAVANETPKG "SocketException" , |
558 | "sun.nio.ch.Net.setIntOption" ); |
559 | } |
560 | #ifdef __linux__ |
561 | if (level == IPPROTO_IPV6 && opt == IPV6_TCLASS && isIPv6) { |
562 | // set the V4 option also |
563 | setsockopt(fdval(env, fdo), IPPROTO_IP, IP_TOS, parg, arglen); |
564 | } |
565 | #endif |
566 | } |
567 | |
568 | JNIEXPORT jint JNICALL |
569 | Java_sun_nio_ch_Net_joinOrDrop4(JNIEnv *env, jobject this, jboolean join, jobject fdo, |
570 | jint group, jint interf, jint source) |
571 | { |
572 | struct ip_mreq mreq; |
573 | struct ip_mreq_source mreq_source; |
574 | int opt, n, optlen; |
575 | void* optval; |
576 | |
577 | if (source == 0) { |
578 | mreq.imr_multiaddr.s_addr = htonl(group); |
579 | mreq.imr_interface.s_addr = htonl(interf); |
580 | opt = (join) ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP; |
581 | optval = (void*)&mreq; |
582 | optlen = sizeof(mreq); |
583 | } else { |
584 | |
585 | #ifdef _AIX |
586 | /* check AIX for support of source filtering */ |
587 | if (isSourceFilterSupported() != JNI_TRUE){ |
588 | return IOS_UNAVAILABLE; |
589 | } |
590 | #endif |
591 | |
592 | mreq_source.imr_multiaddr.s_addr = htonl(group); |
593 | mreq_source.imr_sourceaddr.s_addr = htonl(source); |
594 | mreq_source.imr_interface.s_addr = htonl(interf); |
595 | opt = (join) ? IP_ADD_SOURCE_MEMBERSHIP : IP_DROP_SOURCE_MEMBERSHIP; |
596 | optval = (void*)&mreq_source; |
597 | optlen = sizeof(mreq_source); |
598 | } |
599 | |
600 | n = setsockopt(fdval(env,fdo), IPPROTO_IP, opt, optval, optlen); |
601 | if (n < 0) { |
602 | if (join && (errno == ENOPROTOOPT || errno == EOPNOTSUPP)) |
603 | return IOS_UNAVAILABLE; |
604 | handleSocketError(env, errno); |
605 | } |
606 | return 0; |
607 | } |
608 | |
609 | JNIEXPORT jint JNICALL |
610 | Java_sun_nio_ch_Net_blockOrUnblock4(JNIEnv *env, jobject this, jboolean block, jobject fdo, |
611 | jint group, jint interf, jint source) |
612 | { |
613 | #ifdef __APPLE__ |
614 | /* no IPv4 exclude-mode filtering for now */ |
615 | return IOS_UNAVAILABLE; |
616 | #else |
617 | struct ip_mreq_source mreq_source; |
618 | int n; |
619 | int opt = (block) ? IP_BLOCK_SOURCE : IP_UNBLOCK_SOURCE; |
620 | |
621 | #ifdef _AIX |
622 | /* check AIX for support of source filtering */ |
623 | if (isSourceFilterSupported() != JNI_TRUE){ |
624 | return IOS_UNAVAILABLE; |
625 | } |
626 | #endif |
627 | |
628 | mreq_source.imr_multiaddr.s_addr = htonl(group); |
629 | mreq_source.imr_sourceaddr.s_addr = htonl(source); |
630 | mreq_source.imr_interface.s_addr = htonl(interf); |
631 | |
632 | n = setsockopt(fdval(env,fdo), IPPROTO_IP, opt, |
633 | (void*)&mreq_source, sizeof(mreq_source)); |
634 | if (n < 0) { |
635 | if (block && (errno == ENOPROTOOPT || errno == EOPNOTSUPP)) |
636 | return IOS_UNAVAILABLE; |
637 | handleSocketError(env, errno); |
638 | } |
639 | return 0; |
640 | #endif |
641 | } |
642 | |
643 | JNIEXPORT jint JNICALL |
644 | Java_sun_nio_ch_Net_joinOrDrop6(JNIEnv *env, jobject this, jboolean join, jobject fdo, |
645 | jbyteArray group, jint index, jbyteArray source) |
646 | { |
647 | struct ipv6_mreq mreq6; |
648 | struct group_source_req req; |
649 | int opt, n, optlen; |
650 | void* optval; |
651 | |
652 | if (source == NULL) { |
653 | COPY_INET6_ADDRESS(env, group, (jbyte*)&(mreq6.ipv6mr_multiaddr)); |
654 | mreq6.ipv6mr_interface = (int)index; |
655 | opt = (join) ? IPV6_ADD_MEMBERSHIP : IPV6_DROP_MEMBERSHIP; |
656 | optval = (void*)&mreq6; |
657 | optlen = sizeof(mreq6); |
658 | } else { |
659 | #ifdef __APPLE__ |
660 | /* no IPv6 include-mode filtering for now */ |
661 | return IOS_UNAVAILABLE; |
662 | #else |
663 | initGroupSourceReq(env, group, index, source, &req); |
664 | opt = (join) ? MCAST_JOIN_SOURCE_GROUP : MCAST_LEAVE_SOURCE_GROUP; |
665 | optval = (void*)&req; |
666 | optlen = sizeof(req); |
667 | #endif |
668 | } |
669 | |
670 | n = setsockopt(fdval(env,fdo), IPPROTO_IPV6, opt, optval, optlen); |
671 | if (n < 0) { |
672 | if (join && (errno == ENOPROTOOPT || errno == EOPNOTSUPP)) |
673 | return IOS_UNAVAILABLE; |
674 | handleSocketError(env, errno); |
675 | } |
676 | return 0; |
677 | } |
678 | |
679 | JNIEXPORT jint JNICALL |
680 | Java_sun_nio_ch_Net_blockOrUnblock6(JNIEnv *env, jobject this, jboolean block, jobject fdo, |
681 | jbyteArray group, jint index, jbyteArray source) |
682 | { |
683 | #ifdef __APPLE__ |
684 | /* no IPv6 exclude-mode filtering for now */ |
685 | return IOS_UNAVAILABLE; |
686 | #else |
687 | struct group_source_req req; |
688 | int n; |
689 | int opt = (block) ? MCAST_BLOCK_SOURCE : MCAST_UNBLOCK_SOURCE; |
690 | |
691 | initGroupSourceReq(env, group, index, source, &req); |
692 | |
693 | n = setsockopt(fdval(env,fdo), IPPROTO_IPV6, opt, |
694 | (void*)&req, sizeof(req)); |
695 | if (n < 0) { |
696 | if (block && (errno == ENOPROTOOPT || errno == EOPNOTSUPP)) |
697 | return IOS_UNAVAILABLE; |
698 | handleSocketError(env, errno); |
699 | } |
700 | return 0; |
701 | #endif |
702 | } |
703 | |
704 | JNIEXPORT void JNICALL |
705 | Java_sun_nio_ch_Net_setInterface4(JNIEnv* env, jobject this, jobject fdo, jint interf) |
706 | { |
707 | struct in_addr in; |
708 | socklen_t arglen = sizeof(struct in_addr); |
709 | int n; |
710 | |
711 | in.s_addr = htonl(interf); |
712 | |
713 | n = setsockopt(fdval(env, fdo), IPPROTO_IP, IP_MULTICAST_IF, |
714 | (void*)&(in.s_addr), arglen); |
715 | if (n < 0) { |
716 | handleSocketError(env, errno); |
717 | } |
718 | } |
719 | |
720 | JNIEXPORT jint JNICALL |
721 | Java_sun_nio_ch_Net_getInterface4(JNIEnv* env, jobject this, jobject fdo) |
722 | { |
723 | struct in_addr in; |
724 | socklen_t arglen = sizeof(struct in_addr); |
725 | int n; |
726 | |
727 | n = getsockopt(fdval(env, fdo), IPPROTO_IP, IP_MULTICAST_IF, (void*)&in, &arglen); |
728 | if (n < 0) { |
729 | handleSocketError(env, errno); |
730 | return -1; |
731 | } |
732 | return ntohl(in.s_addr); |
733 | } |
734 | |
735 | JNIEXPORT void JNICALL |
736 | Java_sun_nio_ch_Net_setInterface6(JNIEnv* env, jobject this, jobject fdo, jint index) |
737 | { |
738 | int value = (jint)index; |
739 | socklen_t arglen = sizeof(value); |
740 | int n; |
741 | |
742 | n = setsockopt(fdval(env, fdo), IPPROTO_IPV6, IPV6_MULTICAST_IF, |
743 | (void*)&(index), arglen); |
744 | if (n < 0) { |
745 | handleSocketError(env, errno); |
746 | } |
747 | } |
748 | |
749 | JNIEXPORT jint JNICALL |
750 | Java_sun_nio_ch_Net_getInterface6(JNIEnv* env, jobject this, jobject fdo) |
751 | { |
752 | int index; |
753 | socklen_t arglen = sizeof(index); |
754 | int n; |
755 | |
756 | n = getsockopt(fdval(env, fdo), IPPROTO_IPV6, IPV6_MULTICAST_IF, (void*)&index, &arglen); |
757 | if (n < 0) { |
758 | handleSocketError(env, errno); |
759 | return -1; |
760 | } |
761 | return (jint)index; |
762 | } |
763 | |
764 | JNIEXPORT void JNICALL |
765 | Java_sun_nio_ch_Net_shutdown(JNIEnv *env, jclass cl, jobject fdo, jint jhow) |
766 | { |
767 | int how = (jhow == sun_nio_ch_Net_SHUT_RD) ? SHUT_RD : |
768 | (jhow == sun_nio_ch_Net_SHUT_WR) ? SHUT_WR : SHUT_RDWR; |
769 | if ((shutdown(fdval(env, fdo), how) < 0) && (errno != ENOTCONN)) |
770 | handleSocketError(env, errno); |
771 | } |
772 | |
773 | JNIEXPORT jint JNICALL |
774 | Java_sun_nio_ch_Net_available(JNIEnv *env, jclass cl, jobject fdo) |
775 | { |
776 | int count = 0; |
777 | if (NET_SocketAvailable(fdval(env, fdo), &count) != 0) { |
778 | handleSocketError(env, errno); |
779 | return IOS_THROWN; |
780 | } |
781 | return (jint) count; |
782 | } |
783 | |
784 | JNIEXPORT jint JNICALL |
785 | Java_sun_nio_ch_Net_poll(JNIEnv* env, jclass this, jobject fdo, jint events, jlong timeout) |
786 | { |
787 | struct pollfd pfd; |
788 | int rv; |
789 | pfd.fd = fdval(env, fdo); |
790 | pfd.events = events; |
791 | if (timeout < -1) { |
792 | timeout = -1; |
793 | } else if (timeout > INT_MAX) { |
794 | timeout = INT_MAX; |
795 | } |
796 | rv = poll(&pfd, 1, (int)timeout); |
797 | |
798 | if (rv >= 0) { |
799 | return pfd.revents; |
800 | } else if (errno == EINTR) { |
801 | // interrupted, no events to return |
802 | return 0; |
803 | } else { |
804 | handleSocketError(env, errno); |
805 | return IOS_THROWN; |
806 | } |
807 | } |
808 | |
809 | JNIEXPORT jboolean JNICALL |
810 | Java_sun_nio_ch_Net_pollConnect(JNIEnv *env, jobject this, jobject fdo, jlong timeout) |
811 | { |
812 | jint fd = fdval(env, fdo); |
813 | struct pollfd poller; |
814 | int result; |
815 | |
816 | poller.fd = fd; |
817 | poller.events = POLLOUT; |
818 | poller.revents = 0; |
819 | if (timeout < -1) { |
820 | timeout = -1; |
821 | } else if (timeout > INT_MAX) { |
822 | timeout = INT_MAX; |
823 | } |
824 | |
825 | result = poll(&poller, 1, (int)timeout); |
826 | |
827 | if (result > 0) { |
828 | int error = 0; |
829 | socklen_t n = sizeof(int); |
830 | errno = 0; |
831 | result = getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &n); |
832 | if (result < 0) { |
833 | handleSocketError(env, errno); |
834 | return JNI_FALSE; |
835 | } else if (error) { |
836 | handleSocketError(env, error); |
837 | return JNI_FALSE; |
838 | } else if ((poller.revents & POLLHUP) != 0) { |
839 | handleSocketError(env, ENOTCONN); |
840 | return JNI_FALSE; |
841 | } |
842 | // connected |
843 | return JNI_TRUE; |
844 | } else if (result == 0 || errno == EINTR) { |
845 | return JNI_FALSE; |
846 | } else { |
847 | JNU_ThrowIOExceptionWithLastError(env, "poll failed" ); |
848 | return JNI_FALSE; |
849 | } |
850 | } |
851 | |
852 | JNIEXPORT jshort JNICALL |
853 | Java_sun_nio_ch_Net_pollinValue(JNIEnv *env, jclass this) |
854 | { |
855 | return (jshort)POLLIN; |
856 | } |
857 | |
858 | JNIEXPORT jshort JNICALL |
859 | Java_sun_nio_ch_Net_polloutValue(JNIEnv *env, jclass this) |
860 | { |
861 | return (jshort)POLLOUT; |
862 | } |
863 | |
864 | JNIEXPORT jshort JNICALL |
865 | Java_sun_nio_ch_Net_pollerrValue(JNIEnv *env, jclass this) |
866 | { |
867 | return (jshort)POLLERR; |
868 | } |
869 | |
870 | JNIEXPORT jshort JNICALL |
871 | Java_sun_nio_ch_Net_pollhupValue(JNIEnv *env, jclass this) |
872 | { |
873 | return (jshort)POLLHUP; |
874 | } |
875 | |
876 | JNIEXPORT jshort JNICALL |
877 | Java_sun_nio_ch_Net_pollnvalValue(JNIEnv *env, jclass this) |
878 | { |
879 | return (jshort)POLLNVAL; |
880 | } |
881 | |
882 | JNIEXPORT jshort JNICALL |
883 | Java_sun_nio_ch_Net_pollconnValue(JNIEnv *env, jclass this) |
884 | { |
885 | return (jshort)POLLOUT; |
886 | } |
887 | |
888 | JNIEXPORT jint JNICALL |
889 | Java_sun_nio_ch_Net_sendOOB(JNIEnv* env, jclass this, jobject fdo, jbyte b) |
890 | { |
891 | int n = send(fdval(env, fdo), (const void*)&b, 1, MSG_OOB); |
892 | return convertReturnVal(env, n, JNI_FALSE); |
893 | } |
894 | |
895 | /* Declared in nio_util.h */ |
896 | |
897 | jint handleSocketError(JNIEnv *env, jint errorValue) |
898 | { |
899 | char *xn; |
900 | switch (errorValue) { |
901 | case EINPROGRESS: /* Non-blocking connect */ |
902 | return 0; |
903 | #ifdef EPROTO |
904 | case EPROTO: |
905 | xn = JNU_JAVANETPKG "ProtocolException" ; |
906 | break; |
907 | #endif |
908 | case ECONNREFUSED: |
909 | case ETIMEDOUT: |
910 | case ENOTCONN: |
911 | xn = JNU_JAVANETPKG "ConnectException" ; |
912 | break; |
913 | |
914 | case EHOSTUNREACH: |
915 | xn = JNU_JAVANETPKG "NoRouteToHostException" ; |
916 | break; |
917 | case EADDRINUSE: /* Fall through */ |
918 | case EADDRNOTAVAIL: |
919 | case EACCES: |
920 | xn = JNU_JAVANETPKG "BindException" ; |
921 | break; |
922 | default: |
923 | xn = JNU_JAVANETPKG "SocketException" ; |
924 | break; |
925 | } |
926 | errno = errorValue; |
927 | JNU_ThrowByNameWithLastError(env, xn, "NioSocketError" ); |
928 | return IOS_THROWN; |
929 | } |
930 | |