1 /*
2 * Copyright (c) 1997, 2020, 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 <errno.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <sys/ioctl.h>
29
30 #include "net_util.h"
31
32 #include "java_net_PlainDatagramSocketImpl.h"
33 #include "java_net_InetAddress.h"
34 #include "java_net_NetworkInterface.h"
35 #include "java_net_SocketOptions.h"
36
37 #ifdef __linux__
38 #define IPV6_MULTICAST_IF 17
39 #ifndef SO_BSDCOMPAT
40 #define SO_BSDCOMPAT 14
41 #endif
42 /**
43 * IP_MULTICAST_ALL has been supported since kernel version 2.6.31
44 * but we may be building on a machine that is older than that.
45 */
46 #ifndef IP_MULTICAST_ALL
47 #define IP_MULTICAST_ALL 49
48 #endif
49 #endif // __linux__
50
51 #ifdef __APPLE__
52 #define IPV4_SNDBUF_LIMIT 65507
53 #define IPV6_SNDBUF_LIMIT 65527
54 #endif // __APPLE__
55
56 #ifndef IPTOS_TOS_MASK
57 #define IPTOS_TOS_MASK 0x1e
58 #endif
59 #ifndef IPTOS_PREC_MASK
60 #define IPTOS_PREC_MASK 0xe0
61 #endif
62
63 /************************************************************************
64 * PlainDatagramSocketImpl
65 */
66
67 static jfieldID IO_fd_fdID;
68
69 static jfieldID pdsi_fdID;
70 static jfieldID pdsi_timeoutID;
71 static jfieldID pdsi_trafficClassID;
72 static jfieldID pdsi_localPortID;
73 static jfieldID pdsi_connected;
74 static jfieldID pdsi_connectedAddress;
75 static jfieldID pdsi_connectedPort;
76
77 /*
78 * Returns a java.lang.Integer based on 'i'
79 */
createInteger(JNIEnv * env,int i)80 static jobject createInteger(JNIEnv *env, int i) {
81 static jclass i_class;
82 static jmethodID i_ctrID;
83
84 if (i_class == NULL) {
85 jclass c = (*env)->FindClass(env, "java/lang/Integer");
86 CHECK_NULL_RETURN(c, NULL);
87 i_ctrID = (*env)->GetMethodID(env, c, "<init>", "(I)V");
88 CHECK_NULL_RETURN(i_ctrID, NULL);
89 i_class = (*env)->NewGlobalRef(env, c);
90 CHECK_NULL_RETURN(i_class, NULL);
91 }
92
93 return (*env)->NewObject(env, i_class, i_ctrID, i);
94 }
95
96 /*
97 * Returns a java.lang.Boolean based on 'b'
98 */
createBoolean(JNIEnv * env,int b)99 static jobject createBoolean(JNIEnv *env, int b) {
100 static jclass b_class;
101 static jmethodID b_ctrID;
102
103 if (b_class == NULL) {
104 jclass c = (*env)->FindClass(env, "java/lang/Boolean");
105 CHECK_NULL_RETURN(c, NULL);
106 b_ctrID = (*env)->GetMethodID(env, c, "<init>", "(Z)V");
107 CHECK_NULL_RETURN(b_ctrID, NULL);
108 b_class = (*env)->NewGlobalRef(env, c);
109 CHECK_NULL_RETURN(b_class, NULL);
110 }
111
112 return (*env)->NewObject(env, b_class, b_ctrID, (jboolean)(b != 0));
113 }
114
115 /*
116 * Returns the fd for a PlainDatagramSocketImpl or -1
117 * if closed.
118 */
getFD(JNIEnv * env,jobject this)119 static int getFD(JNIEnv *env, jobject this) {
120 jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
121 if (fdObj == NULL) {
122 return -1;
123 }
124 return (*env)->GetIntField(env, fdObj, IO_fd_fdID);
125 }
126
127 /*
128 * Class: java_net_PlainDatagramSocketImpl
129 * Method: init
130 * Signature: ()V
131 */
132 JNIEXPORT void JNICALL
Java_java_net_PlainDatagramSocketImpl_init(JNIEnv * env,jclass cls)133 Java_java_net_PlainDatagramSocketImpl_init(JNIEnv *env, jclass cls) {
134
135 pdsi_fdID = (*env)->GetFieldID(env, cls, "fd",
136 "Ljava/io/FileDescriptor;");
137 CHECK_NULL(pdsi_fdID);
138 pdsi_timeoutID = (*env)->GetFieldID(env, cls, "timeout", "I");
139 CHECK_NULL(pdsi_timeoutID);
140 pdsi_trafficClassID = (*env)->GetFieldID(env, cls, "trafficClass", "I");
141 CHECK_NULL(pdsi_trafficClassID);
142 pdsi_localPortID = (*env)->GetFieldID(env, cls, "localPort", "I");
143 CHECK_NULL(pdsi_localPortID);
144 pdsi_connected = (*env)->GetFieldID(env, cls, "connected", "Z");
145 CHECK_NULL(pdsi_connected);
146 pdsi_connectedAddress = (*env)->GetFieldID(env, cls, "connectedAddress",
147 "Ljava/net/InetAddress;");
148 CHECK_NULL(pdsi_connectedAddress);
149 pdsi_connectedPort = (*env)->GetFieldID(env, cls, "connectedPort", "I");
150 CHECK_NULL(pdsi_connectedPort);
151
152 IO_fd_fdID = NET_GetFileDescriptorID(env);
153 CHECK_NULL(IO_fd_fdID);
154
155 initInetAddressIDs(env);
156 JNU_CHECK_EXCEPTION(env);
157 Java_java_net_NetworkInterface_init(env, 0);
158 }
159
160 /*
161 * Class: java_net_PlainDatagramSocketImpl
162 * Method: bind
163 * Signature: (ILjava/net/InetAddress;)V
164 */
165 JNIEXPORT void JNICALL
Java_java_net_PlainDatagramSocketImpl_bind0(JNIEnv * env,jobject this,jint localport,jobject iaObj)166 Java_java_net_PlainDatagramSocketImpl_bind0(JNIEnv *env, jobject this,
167 jint localport, jobject iaObj) {
168 /* fdObj is the FileDescriptor field on this */
169 jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
170 /* fd is an int field on fdObj */
171 int fd;
172 int len = 0;
173 SOCKETADDRESS sa;
174 socklen_t slen = sizeof(SOCKETADDRESS);
175
176 if (IS_NULL(fdObj)) {
177 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
178 "Socket closed");
179 return;
180 } else {
181 fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
182 }
183
184 if (IS_NULL(iaObj)) {
185 JNU_ThrowNullPointerException(env, "iaObj is null.");
186 return;
187 }
188
189 /* bind */
190 if (NET_InetAddressToSockaddr(env, iaObj, localport, &sa, &len,
191 JNI_TRUE) != 0) {
192 return;
193 }
194
195 if (NET_Bind(fd, &sa, len) < 0) {
196 if (errno == EADDRINUSE || errno == EADDRNOTAVAIL ||
197 errno == EPERM || errno == EACCES) {
198 NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "BindException",
199 "Bind failed");
200 } else {
201 JNU_ThrowByNameWithMessageAndLastError
202 (env, JNU_JAVANETPKG "SocketException", "Bind failed");
203 }
204 return;
205 }
206
207 /* initialize the local port */
208 if (localport == 0) {
209 /* Now that we're a connected socket, let's extract the port number
210 * that the system chose for us and store it in the Socket object.
211 */
212 if (getsockname(fd, &sa.sa, &slen) == -1) {
213 JNU_ThrowByNameWithMessageAndLastError
214 (env, JNU_JAVANETPKG "SocketException", "Error getting socket name");
215 return;
216 }
217
218 localport = NET_GetPortFromSockaddr(&sa);
219
220 (*env)->SetIntField(env, this, pdsi_localPortID, localport);
221 } else {
222 (*env)->SetIntField(env, this, pdsi_localPortID, localport);
223 }
224 }
225
226 /*
227 * Class: java_net_PlainDatagramSocketImpl
228 * Method: connect0
229 * Signature: (Ljava/net/InetAddress;I)V
230 */
231 JNIEXPORT void JNICALL
Java_java_net_PlainDatagramSocketImpl_connect0(JNIEnv * env,jobject this,jobject address,jint port)232 Java_java_net_PlainDatagramSocketImpl_connect0(JNIEnv *env, jobject this,
233 jobject address, jint port) {
234 /* The object's field */
235 jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
236 /* The fdObj'fd */
237 jint fd;
238 /* The packetAddress address, family and port */
239 SOCKETADDRESS rmtaddr;
240 int len = 0;
241
242 if (IS_NULL(fdObj)) {
243 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
244 "Socket closed");
245 return;
246 }
247 fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
248
249 if (IS_NULL(address)) {
250 JNU_ThrowNullPointerException(env, "address");
251 return;
252 }
253
254 if (NET_InetAddressToSockaddr(env, address, port, &rmtaddr, &len,
255 JNI_TRUE) != 0) {
256 return;
257 }
258
259 if (NET_Connect(fd, &rmtaddr.sa, len) == -1) {
260 NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "ConnectException",
261 "Connect failed");
262 }
263 }
264
265 /*
266 * Class: java_net_PlainDatagramSocketImpl
267 * Method: disconnect0
268 * Signature: ()V
269 */
270 JNIEXPORT void JNICALL
Java_java_net_PlainDatagramSocketImpl_disconnect0(JNIEnv * env,jobject this,jint family)271 Java_java_net_PlainDatagramSocketImpl_disconnect0(JNIEnv *env, jobject this, jint family) {
272 /* The object's field */
273 jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
274 /* The fdObj'fd */
275 jint fd;
276
277 #if defined(__linux__) || defined(_ALLBSD_SOURCE)
278 SOCKETADDRESS addr;
279 socklen_t len;
280 #if defined(__linux__)
281 int localPort = 0;
282 #endif
283 #endif
284
285 if (IS_NULL(fdObj)) {
286 return;
287 }
288 fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
289
290 #if defined(__linux__) || defined(_ALLBSD_SOURCE)
291 memset(&addr, 0, sizeof(addr));
292 if (ipv6_available()) {
293 addr.sa6.sin6_family = AF_UNSPEC;
294 len = sizeof(struct sockaddr_in6);
295 } else {
296 addr.sa4.sin_family = AF_UNSPEC;
297 len = sizeof(struct sockaddr_in);
298 }
299 NET_Connect(fd, &addr.sa, len);
300
301 #if defined(__linux__)
302 if (getsockname(fd, &addr.sa, &len) == -1)
303 return;
304
305 localPort = NET_GetPortFromSockaddr(&addr);
306 if (localPort == 0) {
307 localPort = (*env)->GetIntField(env, this, pdsi_localPortID);
308 if (addr.sa.sa_family == AF_INET6) {
309 addr.sa6.sin6_port = htons(localPort);
310 } else {
311 addr.sa4.sin_port = htons(localPort);
312 }
313
314 NET_Bind(fd, &addr, len);
315 }
316
317 #endif
318 #else
319 NET_Connect(fd, 0, 0);
320 #endif
321 }
322
323 /*
324 * Class: java_net_PlainDatagramSocketImpl
325 * Method: send0
326 * Signature: (Ljava/net/DatagramPacket;)V
327 */
328 JNIEXPORT void JNICALL
Java_java_net_PlainDatagramSocketImpl_send0(JNIEnv * env,jobject this,jobject packet)329 Java_java_net_PlainDatagramSocketImpl_send0(JNIEnv *env, jobject this,
330 jobject packet) {
331
332 char BUF[MAX_BUFFER_LEN];
333 char *fullPacket = NULL;
334 int ret, mallocedPacket = JNI_FALSE;
335 /* The object's field */
336 jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
337 jint trafficClass = (*env)->GetIntField(env, this, pdsi_trafficClassID);
338
339 jbyteArray packetBuffer;
340 jobject packetAddress;
341 jint packetBufferOffset, packetBufferLen, packetPort;
342 jboolean connected;
343
344 /* The fdObj'fd */
345 jint fd;
346
347 SOCKETADDRESS rmtaddr;
348 struct sockaddr *rmtaddrP = 0;
349 int len = 0;
350
351 if (IS_NULL(fdObj)) {
352 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
353 "Socket closed");
354 return;
355 }
356 fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
357
358 if (IS_NULL(packet)) {
359 JNU_ThrowNullPointerException(env, "packet");
360 return;
361 }
362
363 connected = (*env)->GetBooleanField(env, this, pdsi_connected);
364
365 packetBuffer = (*env)->GetObjectField(env, packet, dp_bufID);
366 packetAddress = (*env)->GetObjectField(env, packet, dp_addressID);
367 if (IS_NULL(packetBuffer) || IS_NULL(packetAddress)) {
368 JNU_ThrowNullPointerException(env, "null buffer || null address");
369 return;
370 }
371
372 packetBufferOffset = (*env)->GetIntField(env, packet, dp_offsetID);
373 packetBufferLen = (*env)->GetIntField(env, packet, dp_lengthID);
374
375 // arg to NET_Sendto() null, if connected
376 if (!connected) {
377 packetPort = (*env)->GetIntField(env, packet, dp_portID);
378 if (NET_InetAddressToSockaddr(env, packetAddress, packetPort, &rmtaddr,
379 &len, JNI_TRUE) != 0) {
380 return;
381 }
382 rmtaddrP = &rmtaddr.sa;
383 }
384
385 if (packetBufferLen > MAX_BUFFER_LEN) {
386 /* When JNI-ifying the JDK's IO routines, we turned
387 * reads and writes of byte arrays of size greater
388 * than 2048 bytes into several operations of size 2048.
389 * This saves a malloc()/memcpy()/free() for big
390 * buffers. This is OK for file IO and TCP, but that
391 * strategy violates the semantics of a datagram protocol.
392 * (one big send) != (several smaller sends). So here
393 * we *must* allocate the buffer. Note it needn't be bigger
394 * than 65,536 (0xFFFF), the max size of an IP packet.
395 * Anything bigger should be truncated anyway.
396 *
397 * We may want to use a smarter allocation scheme at some
398 * point.
399 */
400 if (packetBufferLen > MAX_PACKET_LEN) {
401 packetBufferLen = MAX_PACKET_LEN;
402 }
403 fullPacket = (char *)malloc(packetBufferLen);
404
405 if (!fullPacket) {
406 JNU_ThrowOutOfMemoryError(env, "Send buffer native heap allocation failed");
407 return;
408 } else {
409 mallocedPacket = JNI_TRUE;
410 }
411 } else {
412 fullPacket = &(BUF[0]);
413 }
414
415 (*env)->GetByteArrayRegion(env, packetBuffer, packetBufferOffset, packetBufferLen,
416 (jbyte *)fullPacket);
417 if (trafficClass != 0 && ipv6_available()) {
418 NET_SetTrafficClass(&rmtaddr, trafficClass);
419 }
420
421 /*
422 * Send the datagram.
423 *
424 * If we are connected it's possible that sendto will return
425 * ECONNREFUSED indicating that an ICMP port unreachable has
426 * received.
427 */
428 ret = NET_SendTo(fd, fullPacket, packetBufferLen, 0, rmtaddrP, len);
429
430 if (ret < 0) {
431 if (errno == ECONNREFUSED) {
432 JNU_ThrowByName(env, JNU_JAVANETPKG "PortUnreachableException",
433 "ICMP Port Unreachable");
434 } else {
435 JNU_ThrowIOExceptionWithLastError(env, "sendto failed");
436 }
437 }
438
439 if (mallocedPacket) {
440 free(fullPacket);
441 }
442 return;
443 }
444
445 /*
446 * Class: java_net_PlainDatagramSocketImpl
447 * Method: peek
448 * Signature: (Ljava/net/InetAddress;)I
449 */
450 JNIEXPORT jint JNICALL
Java_java_net_PlainDatagramSocketImpl_peek(JNIEnv * env,jobject this,jobject addressObj)451 Java_java_net_PlainDatagramSocketImpl_peek(JNIEnv *env, jobject this,
452 jobject addressObj) {
453
454 jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
455 jint timeout = (*env)->GetIntField(env, this, pdsi_timeoutID);
456 jint fd;
457 ssize_t n;
458 SOCKETADDRESS rmtaddr;
459 socklen_t slen = sizeof(SOCKETADDRESS);
460 char buf[1];
461 jint family;
462 jobject iaObj;
463 int port;
464 if (IS_NULL(fdObj)) {
465 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");
466 return -1;
467 } else {
468 fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
469 }
470 if (IS_NULL(addressObj)) {
471 JNU_ThrowNullPointerException(env, "Null address in peek()");
472 return -1;
473 }
474 if (timeout) {
475 int ret = NET_Timeout(env, fd, timeout, JVM_NanoTime(env, 0));
476 if (ret == 0) {
477 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException",
478 "Peek timed out");
479 return ret;
480 } else if (ret == -1) {
481 if (errno == EBADF) {
482 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");
483 } else if (errno == ENOMEM) {
484 JNU_ThrowOutOfMemoryError(env, "NET_Timeout native heap allocation failed");
485 } else {
486 JNU_ThrowByNameWithMessageAndLastError
487 (env, JNU_JAVANETPKG "SocketException", "Peek failed");
488 }
489 return ret;
490 }
491 }
492
493 n = NET_RecvFrom(fd, buf, 1, MSG_PEEK, &rmtaddr.sa, &slen);
494
495 if (n == -1) {
496 if (errno == ECONNREFUSED) {
497 JNU_ThrowByName(env, JNU_JAVANETPKG "PortUnreachableException",
498 "ICMP Port Unreachable");
499 } else {
500 if (errno == EBADF) {
501 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");
502 } else {
503 JNU_ThrowByNameWithMessageAndLastError
504 (env, JNU_JAVANETPKG "SocketException", "Peek failed");
505 }
506 }
507 return 0;
508 }
509
510 iaObj = NET_SockaddrToInetAddress(env, &rmtaddr, &port);
511 family = getInetAddress_family(env, iaObj) == java_net_InetAddress_IPv4 ?
512 AF_INET : AF_INET6;
513 JNU_CHECK_EXCEPTION_RETURN(env, -1);
514 if (family == AF_INET) { /* this API can't handle IPV6 addresses */
515 int address = getInetAddress_addr(env, iaObj);
516 JNU_CHECK_EXCEPTION_RETURN(env, -1);
517 setInetAddress_addr(env, addressObj, address);
518 JNU_CHECK_EXCEPTION_RETURN(env, -1);
519 }
520 return port;
521 }
522
523 JNIEXPORT jint JNICALL
Java_java_net_PlainDatagramSocketImpl_peekData(JNIEnv * env,jobject this,jobject packet)524 Java_java_net_PlainDatagramSocketImpl_peekData(JNIEnv *env, jobject this,
525 jobject packet) {
526
527 char BUF[MAX_BUFFER_LEN];
528 char *fullPacket = NULL;
529 int mallocedPacket = JNI_FALSE;
530 jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
531 jint timeout = (*env)->GetIntField(env, this, pdsi_timeoutID);
532 jbyteArray packetBuffer;
533 jint packetBufferOffset, packetBufferLen;
534 int fd;
535 int n;
536 SOCKETADDRESS rmtaddr;
537 socklen_t slen = sizeof(SOCKETADDRESS);
538 int port = -1;
539
540 if (IS_NULL(fdObj)) {
541 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
542 "Socket closed");
543 return -1;
544 }
545
546 fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
547
548 if (IS_NULL(packet)) {
549 JNU_ThrowNullPointerException(env, "packet");
550 return -1;
551 }
552
553 packetBuffer = (*env)->GetObjectField(env, packet, dp_bufID);
554 if (IS_NULL(packetBuffer)) {
555 JNU_ThrowNullPointerException(env, "packet buffer");
556 return -1;
557 }
558 packetBufferOffset = (*env)->GetIntField(env, packet, dp_offsetID);
559 packetBufferLen = (*env)->GetIntField(env, packet, dp_bufLengthID);
560 if (timeout) {
561 int ret = NET_Timeout(env, fd, timeout, JVM_NanoTime(env, 0));
562 if (ret == 0) {
563 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException",
564 "Receive timed out");
565 return -1;
566 } else if (ret == -1) {
567 if (errno == ENOMEM) {
568 JNU_ThrowOutOfMemoryError(env, "NET_Timeout native heap allocation failed");
569 #ifdef __linux__
570 } else if (errno == EBADF) {
571 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");
572 } else {
573 JNU_ThrowByNameWithMessageAndLastError
574 (env, JNU_JAVANETPKG "SocketException", "Receive failed");
575 #else
576 } else {
577 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");
578 #endif
579 }
580 return -1;
581 }
582 }
583
584 if (packetBufferLen > MAX_BUFFER_LEN) {
585
586 /* When JNI-ifying the JDK's IO routines, we turned
587 * reads and writes of byte arrays of size greater
588 * than 2048 bytes into several operations of size 2048.
589 * This saves a malloc()/memcpy()/free() for big
590 * buffers. This is OK for file IO and TCP, but that
591 * strategy violates the semantics of a datagram protocol.
592 * (one big send) != (several smaller sends). So here
593 * we *must* allocate the buffer. Note it needn't be bigger
594 * than 65,536 (0xFFFF), the max size of an IP packet.
595 * anything bigger is truncated anyway.
596 *
597 * We may want to use a smarter allocation scheme at some
598 * point.
599 */
600 if (packetBufferLen > MAX_PACKET_LEN) {
601 packetBufferLen = MAX_PACKET_LEN;
602 }
603 fullPacket = (char *)malloc(packetBufferLen);
604
605 if (!fullPacket) {
606 JNU_ThrowOutOfMemoryError(env, "Peek buffer native heap allocation failed");
607 return -1;
608 } else {
609 mallocedPacket = JNI_TRUE;
610 }
611 } else {
612 fullPacket = &(BUF[0]);
613 }
614
615 n = NET_RecvFrom(fd, fullPacket, packetBufferLen, MSG_PEEK,
616 &rmtaddr.sa, &slen);
617 /* truncate the data if the packet's length is too small */
618 if (n > packetBufferLen) {
619 n = packetBufferLen;
620 }
621 if (n == -1) {
622 (*env)->SetIntField(env, packet, dp_offsetID, 0);
623 (*env)->SetIntField(env, packet, dp_lengthID, 0);
624 if (errno == ECONNREFUSED) {
625 JNU_ThrowByName(env, JNU_JAVANETPKG "PortUnreachableException",
626 "ICMP Port Unreachable");
627 } else {
628 if (errno == EBADF) {
629 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");
630 } else {
631 JNU_ThrowByNameWithMessageAndLastError
632 (env, JNU_JAVANETPKG "SocketException", "Receive failed");
633 }
634 }
635 } else {
636 /*
637 * success - fill in received address...
638 *
639 * REMIND: Fill in an int on the packet, and create inetadd
640 * object in Java, as a performance improvement. Also
641 * construct the inetadd object lazily.
642 */
643
644 jobject packetAddress;
645
646 /*
647 * Check if there is an InetAddress already associated with this
648 * packet. If so we check if it is the same source address. We
649 * can't update any existing InetAddress because it is immutable
650 */
651 packetAddress = (*env)->GetObjectField(env, packet, dp_addressID);
652 if (packetAddress != NULL) {
653 if (!NET_SockaddrEqualsInetAddress(env, &rmtaddr, packetAddress)) {
654 /* force a new InetAddress to be created */
655 packetAddress = NULL;
656 }
657 }
658 if (!(*env)->ExceptionCheck(env)){
659 if (packetAddress == NULL ) {
660 packetAddress = NET_SockaddrToInetAddress(env, &rmtaddr, &port);
661 /* stuff the new InetAddress in the packet */
662 (*env)->SetObjectField(env, packet, dp_addressID, packetAddress);
663 } else {
664 /* only get the new port number */
665 port = NET_GetPortFromSockaddr(&rmtaddr);
666 }
667 /* and fill in the data, remote address/port and such */
668 (*env)->SetByteArrayRegion(env, packetBuffer, packetBufferOffset, n,
669 (jbyte *)fullPacket);
670 (*env)->SetIntField(env, packet, dp_portID, port);
671 (*env)->SetIntField(env, packet, dp_lengthID, n);
672 }
673 }
674
675 if (mallocedPacket) {
676 free(fullPacket);
677 }
678 return port;
679 }
680
681 /*
682 * Class: java_net_PlainDatagramSocketImpl
683 * Method: receive
684 * Signature: (Ljava/net/DatagramPacket;)V
685 */
686 JNIEXPORT void JNICALL
Java_java_net_PlainDatagramSocketImpl_receive0(JNIEnv * env,jobject this,jobject packet)687 Java_java_net_PlainDatagramSocketImpl_receive0(JNIEnv *env, jobject this,
688 jobject packet) {
689
690 char BUF[MAX_BUFFER_LEN];
691 char *fullPacket = NULL;
692 int mallocedPacket = JNI_FALSE;
693 jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
694 jint timeout = (*env)->GetIntField(env, this, pdsi_timeoutID);
695
696 jbyteArray packetBuffer;
697 jint packetBufferOffset, packetBufferLen;
698
699 int fd;
700
701 int n;
702 SOCKETADDRESS rmtaddr;
703 socklen_t slen = sizeof(SOCKETADDRESS);
704 jboolean retry;
705 #ifdef __linux__
706 jboolean connected = JNI_FALSE;
707 jobject connectedAddress = NULL;
708 jint connectedPort = 0;
709 jlong prevTime = 0;
710 #endif
711
712 if (IS_NULL(fdObj)) {
713 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
714 "Socket closed");
715 return;
716 }
717
718 fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
719
720 if (IS_NULL(packet)) {
721 JNU_ThrowNullPointerException(env, "packet");
722 return;
723 }
724
725 packetBuffer = (*env)->GetObjectField(env, packet, dp_bufID);
726 if (IS_NULL(packetBuffer)) {
727 JNU_ThrowNullPointerException(env, "packet buffer");
728 return;
729 }
730 packetBufferOffset = (*env)->GetIntField(env, packet, dp_offsetID);
731 packetBufferLen = (*env)->GetIntField(env, packet, dp_bufLengthID);
732
733 if (packetBufferLen > MAX_BUFFER_LEN) {
734
735 /* When JNI-ifying the JDK's IO routines, we turned
736 * reads and writes of byte arrays of size greater
737 * than 2048 bytes into several operations of size 2048.
738 * This saves a malloc()/memcpy()/free() for big
739 * buffers. This is OK for file IO and TCP, but that
740 * strategy violates the semantics of a datagram protocol.
741 * (one big send) != (several smaller sends). So here
742 * we *must* allocate the buffer. Note it needn't be bigger
743 * than 65,536 (0xFFFF) the max size of an IP packet,
744 * anything bigger is truncated anyway.
745 *
746 * We may want to use a smarter allocation scheme at some
747 * point.
748 */
749 if (packetBufferLen > MAX_PACKET_LEN) {
750 packetBufferLen = MAX_PACKET_LEN;
751 }
752 fullPacket = (char *)malloc(packetBufferLen);
753
754 if (!fullPacket) {
755 JNU_ThrowOutOfMemoryError(env, "Receive buffer native heap allocation failed");
756 return;
757 } else {
758 mallocedPacket = JNI_TRUE;
759 }
760 } else {
761 fullPacket = &(BUF[0]);
762 }
763
764 do {
765 retry = JNI_FALSE;
766
767 if (timeout) {
768 int ret = NET_Timeout(env, fd, timeout, JVM_NanoTime(env, 0));
769 if (ret <= 0) {
770 if (ret == 0) {
771 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException",
772 "Receive timed out");
773 } else if (ret == -1) {
774 if (errno == ENOMEM) {
775 JNU_ThrowOutOfMemoryError(env, "NET_Timeout native heap allocation failed");
776 #ifdef __linux__
777 } else if (errno == EBADF) {
778 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");
779 } else {
780 JNU_ThrowByNameWithMessageAndLastError
781 (env, JNU_JAVANETPKG "SocketException", "Receive failed");
782 #else
783 } else {
784 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");
785 #endif
786 }
787 }
788
789 if (mallocedPacket) {
790 free(fullPacket);
791 }
792
793 return;
794 }
795 }
796
797 n = NET_RecvFrom(fd, fullPacket, packetBufferLen, 0,
798 &rmtaddr.sa, &slen);
799 /* truncate the data if the packet's length is too small */
800 if (n > packetBufferLen) {
801 n = packetBufferLen;
802 }
803 if (n == -1) {
804 (*env)->SetIntField(env, packet, dp_offsetID, 0);
805 (*env)->SetIntField(env, packet, dp_lengthID, 0);
806 if (errno == ECONNREFUSED) {
807 JNU_ThrowByName(env, JNU_JAVANETPKG "PortUnreachableException",
808 "ICMP Port Unreachable");
809 } else {
810 if (errno == EBADF) {
811 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");
812 } else {
813 JNU_ThrowByNameWithMessageAndLastError
814 (env, JNU_JAVANETPKG "SocketException", "Receive failed");
815 }
816 }
817 } else {
818 int port;
819 jobject packetAddress;
820
821 /*
822 * success - fill in received address...
823 *
824 * REMIND: Fill in an int on the packet, and create inetadd
825 * object in Java, as a performance improvement. Also
826 * construct the inetadd object lazily.
827 */
828
829 /*
830 * Check if there is an InetAddress already associated with this
831 * packet. If so we check if it is the same source address. We
832 * can't update any existing InetAddress because it is immutable
833 */
834 packetAddress = (*env)->GetObjectField(env, packet, dp_addressID);
835 if (packetAddress != NULL) {
836 if (!NET_SockaddrEqualsInetAddress(env, &rmtaddr,
837 packetAddress)) {
838 /* force a new InetAddress to be created */
839 packetAddress = NULL;
840 }
841 }
842 if (packetAddress == NULL) {
843 packetAddress = NET_SockaddrToInetAddress(env, &rmtaddr, &port);
844 /* stuff the new Inetaddress in the packet */
845 (*env)->SetObjectField(env, packet, dp_addressID, packetAddress);
846 } else {
847 /* only get the new port number */
848 port = NET_GetPortFromSockaddr(&rmtaddr);
849 }
850 /* and fill in the data, remote address/port and such */
851 (*env)->SetByteArrayRegion(env, packetBuffer, packetBufferOffset, n,
852 (jbyte *)fullPacket);
853 (*env)->SetIntField(env, packet, dp_portID, port);
854 (*env)->SetIntField(env, packet, dp_lengthID, n);
855 }
856
857 } while (retry);
858
859 if (mallocedPacket) {
860 free(fullPacket);
861 }
862 }
863
864 /*
865 * Class: java_net_PlainDatagramSocketImpl
866 * Method: datagramSocketCreate
867 * Signature: ()V
868 */
869 JNIEXPORT void JNICALL
Java_java_net_PlainDatagramSocketImpl_datagramSocketCreate(JNIEnv * env,jobject this)870 Java_java_net_PlainDatagramSocketImpl_datagramSocketCreate(JNIEnv *env,
871 jobject this) {
872 jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
873 int arg, fd, t = 1;
874 char tmpbuf[1024];
875 int domain = ipv6_available() ? AF_INET6 : AF_INET;
876
877 if (IS_NULL(fdObj)) {
878 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
879 "Socket closed");
880 return;
881 }
882
883 if ((fd = socket(domain, SOCK_DGRAM, 0)) == -1) {
884 JNU_ThrowByNameWithMessageAndLastError
885 (env, JNU_JAVANETPKG "SocketException", "Error creating socket");
886 return;
887 }
888
889 /*
890 * If IPv4 is available, disable IPV6_V6ONLY to ensure dual-socket support.
891 */
892 if (domain == AF_INET6 && ipv4_available()) {
893 arg = 0;
894 if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&arg,
895 sizeof(int)) < 0) {
896 NET_ThrowNew(env, errno, "cannot set IPPROTO_IPV6");
897 close(fd);
898 return;
899 }
900 }
901
902 #ifdef __APPLE__
903 arg = (domain == AF_INET6) ? IPV6_SNDBUF_LIMIT : IPV4_SNDBUF_LIMIT;
904 if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF,
905 (char *)&arg, sizeof(arg)) < 0) {
906 getErrorString(errno, tmpbuf, sizeof(tmpbuf));
907 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", tmpbuf);
908 close(fd);
909 return;
910 }
911 #endif /* __APPLE__ */
912
913 if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (char*) &t, sizeof (int)) < 0) {
914 getErrorString(errno, tmpbuf, sizeof(tmpbuf));
915 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", tmpbuf);
916 close(fd);
917 return;
918 }
919
920 #if defined(__linux__)
921 arg = 0;
922 int level = (domain == AF_INET6) ? IPPROTO_IPV6 : IPPROTO_IP;
923 if ((setsockopt(fd, level, IP_MULTICAST_ALL, (char*)&arg, sizeof(arg)) < 0) &&
924 (errno != ENOPROTOOPT))
925 {
926 getErrorString(errno, tmpbuf, sizeof(tmpbuf));
927 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", tmpbuf);
928 close(fd);
929 return;
930 }
931 #endif
932
933 #if defined (__linux__)
934 /*
935 * On Linux for IPv6 sockets we must set the hop limit
936 * to 1 to be compatible with default TTL of 1 for IPv4 sockets.
937 */
938 if (domain == AF_INET6) {
939 int ttl = 1;
940 if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (char *) &ttl,
941 sizeof (ttl)) < 0) {
942 getErrorString(errno, tmpbuf, sizeof(tmpbuf));
943 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", tmpbuf);
944 close(fd);
945 return;
946 }
947 }
948 #endif /* __linux__ */
949
950 (*env)->SetIntField(env, fdObj, IO_fd_fdID, fd);
951 }
952
953 /*
954 * Class: java_net_PlainDatagramSocketImpl
955 * Method: datagramSocketClose
956 * Signature: ()V
957 */
958 JNIEXPORT void JNICALL
Java_java_net_PlainDatagramSocketImpl_datagramSocketClose(JNIEnv * env,jobject this)959 Java_java_net_PlainDatagramSocketImpl_datagramSocketClose(JNIEnv *env,
960 jobject this) {
961 /*
962 * REMIND: PUT A LOCK AROUND THIS CODE
963 */
964 jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
965 int fd;
966
967 if (IS_NULL(fdObj)) {
968 return;
969 }
970 fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
971 if (fd == -1) {
972 return;
973 }
974 (*env)->SetIntField(env, fdObj, IO_fd_fdID, -1);
975 NET_SocketClose(fd);
976 }
977
978
979 /*
980 * Set outgoing multicast interface designated by a NetworkInterface.
981 * Throw exception if failed.
982 */
mcast_set_if_by_if_v4(JNIEnv * env,jobject this,int fd,jobject value)983 static void mcast_set_if_by_if_v4(JNIEnv *env, jobject this, int fd, jobject value) {
984 static jfieldID ni_addrsID;
985 struct in_addr in;
986 jobjectArray addrArray;
987 jsize len;
988 jint family;
989 jobject addr;
990 int i;
991
992 if (ni_addrsID == NULL ) {
993 jclass c = (*env)->FindClass(env, "java/net/NetworkInterface");
994 CHECK_NULL(c);
995 ni_addrsID = (*env)->GetFieldID(env, c, "addrs",
996 "[Ljava/net/InetAddress;");
997 CHECK_NULL(ni_addrsID);
998 }
999
1000 addrArray = (*env)->GetObjectField(env, value, ni_addrsID);
1001 len = (*env)->GetArrayLength(env, addrArray);
1002
1003 /*
1004 * Check that there is at least one address bound to this
1005 * interface.
1006 */
1007 if (len < 1) {
1008 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
1009 "bad argument for IP_MULTICAST_IF2: No IP addresses bound to interface");
1010 return;
1011 }
1012
1013 /*
1014 * We need an ipv4 address here
1015 */
1016 in.s_addr = 0;
1017 for (i = 0; i < len; i++) {
1018 addr = (*env)->GetObjectArrayElement(env, addrArray, i);
1019 family = getInetAddress_family(env, addr);
1020 JNU_CHECK_EXCEPTION(env);
1021 if (family == java_net_InetAddress_IPv4) {
1022 in.s_addr = htonl(getInetAddress_addr(env, addr));
1023 JNU_CHECK_EXCEPTION(env);
1024 break;
1025 }
1026 }
1027
1028 if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF,
1029 (const char *)&in, sizeof(in)) < 0) {
1030 JNU_ThrowByNameWithMessageAndLastError
1031 (env, JNU_JAVANETPKG "SocketException", "Error setting socket option");
1032 }
1033 }
1034
1035 /*
1036 * Set outgoing multicast interface designated by a NetworkInterface.
1037 * Throw exception if failed.
1038 */
mcast_set_if_by_if_v6(JNIEnv * env,jobject this,int fd,jobject value)1039 static void mcast_set_if_by_if_v6(JNIEnv *env, jobject this, int fd, jobject value) {
1040 static jfieldID ni_indexID;
1041 int index;
1042
1043 if (ni_indexID == NULL) {
1044 jclass c = (*env)->FindClass(env, "java/net/NetworkInterface");
1045 CHECK_NULL(c);
1046 ni_indexID = (*env)->GetFieldID(env, c, "index", "I");
1047 CHECK_NULL(ni_indexID);
1048 }
1049 index = (*env)->GetIntField(env, value, ni_indexID);
1050
1051 if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF,
1052 (const char*)&index, sizeof(index)) < 0) {
1053 if ((errno == EINVAL || errno == EADDRNOTAVAIL) && index > 0) {
1054 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
1055 "IPV6_MULTICAST_IF failed (interface has IPv4 "
1056 "address only?)");
1057 } else {
1058 JNU_ThrowByNameWithMessageAndLastError
1059 (env, JNU_JAVANETPKG "SocketException", "Error setting socket option");
1060 }
1061 return;
1062 }
1063 }
1064
1065 /*
1066 * Set outgoing multicast interface designated by an InetAddress.
1067 * Throw exception if failed.
1068 */
mcast_set_if_by_addr_v4(JNIEnv * env,jobject this,int fd,jobject value)1069 static void mcast_set_if_by_addr_v4(JNIEnv *env, jobject this, int fd, jobject value) {
1070 struct in_addr in;
1071
1072 in.s_addr = htonl( getInetAddress_addr(env, value) );
1073 JNU_CHECK_EXCEPTION(env);
1074 if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF,
1075 (const char*)&in, sizeof(in)) < 0) {
1076 JNU_ThrowByNameWithMessageAndLastError
1077 (env, JNU_JAVANETPKG "SocketException", "Error setting socket option");
1078 }
1079 }
1080
1081 /*
1082 * Set outgoing multicast interface designated by an InetAddress.
1083 * Throw exception if failed.
1084 */
mcast_set_if_by_addr_v6(JNIEnv * env,jobject this,int fd,jobject value)1085 static void mcast_set_if_by_addr_v6(JNIEnv *env, jobject this, int fd, jobject value) {
1086 static jclass ni_class;
1087 if (ni_class == NULL) {
1088 jclass c = (*env)->FindClass(env, "java/net/NetworkInterface");
1089 CHECK_NULL(c);
1090 ni_class = (*env)->NewGlobalRef(env, c);
1091 CHECK_NULL(ni_class);
1092 }
1093
1094 value = Java_java_net_NetworkInterface_getByInetAddress0(env, ni_class, value);
1095 if (value == NULL) {
1096 if (!(*env)->ExceptionOccurred(env)) {
1097 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
1098 "bad argument for IP_MULTICAST_IF"
1099 ": address not bound to any interface");
1100 }
1101 return;
1102 }
1103
1104 mcast_set_if_by_if_v6(env, this, fd, value);
1105 }
1106
1107 /*
1108 * Sets the multicast interface.
1109 *
1110 * SocketOptions.IP_MULTICAST_IF :-
1111 * value is a InetAddress
1112 * IPv4: set outgoing multicast interface using
1113 * IPPROTO_IP/IP_MULTICAST_IF
1114 * IPv6: Get the index of the interface to which the
1115 * InetAddress is bound
1116 * Set outgoing multicast interface using
1117 * IPPROTO_IPV6/IPV6_MULTICAST_IF
1118 *
1119 * SockOptions.IF_MULTICAST_IF2 :-
1120 * value is a NetworkInterface
1121 * IPv4: Obtain IP address bound to network interface
1122 * (NetworkInterface.addres[0])
1123 * set outgoing multicast interface using
1124 * IPPROTO_IP/IP_MULTICAST_IF
1125 * IPv6: Obtain NetworkInterface.index
1126 * Set outgoing multicast interface using
1127 * IPPROTO_IPV6/IPV6_MULTICAST_IF
1128 *
1129 */
setMulticastInterface(JNIEnv * env,jobject this,int fd,jint opt,jobject value)1130 static void setMulticastInterface(JNIEnv *env, jobject this, int fd,
1131 jint opt, jobject value)
1132 {
1133 if (opt == java_net_SocketOptions_IP_MULTICAST_IF) {
1134 /*
1135 * value is an InetAddress.
1136 */
1137 #ifdef __linux__
1138 mcast_set_if_by_addr_v4(env, this, fd, value);
1139 if (ipv6_available()) {
1140 if ((*env)->ExceptionCheck(env)){
1141 (*env)->ExceptionClear(env);
1142 }
1143 mcast_set_if_by_addr_v6(env, this, fd, value);
1144 }
1145 #else /* __linux__ not defined */
1146 if (ipv6_available()) {
1147 mcast_set_if_by_addr_v6(env, this, fd, value);
1148 } else {
1149 mcast_set_if_by_addr_v4(env, this, fd, value);
1150 }
1151 #endif /* __linux__ */
1152 }
1153
1154 if (opt == java_net_SocketOptions_IP_MULTICAST_IF2) {
1155 /*
1156 * value is a NetworkInterface.
1157 */
1158 #ifdef __linux__
1159 mcast_set_if_by_if_v4(env, this, fd, value);
1160 if (ipv6_available()) {
1161 if ((*env)->ExceptionCheck(env)){
1162 (*env)->ExceptionClear(env);
1163 }
1164 mcast_set_if_by_if_v6(env, this, fd, value);
1165 }
1166 #else /* __linux__ not defined */
1167 if (ipv6_available()) {
1168 mcast_set_if_by_if_v6(env, this, fd, value);
1169 } else {
1170 mcast_set_if_by_if_v4(env, this, fd, value);
1171 }
1172 #endif /* __linux__ */
1173 }
1174 }
1175
1176 /*
1177 * Enable/disable local loopback of multicast datagrams.
1178 */
mcast_set_loop_v4(JNIEnv * env,jobject this,int fd,jobject value)1179 static void mcast_set_loop_v4(JNIEnv *env, jobject this, int fd, jobject value) {
1180 jclass cls;
1181 jfieldID fid;
1182 jboolean on;
1183 char loopback;
1184
1185 cls = (*env)->FindClass(env, "java/lang/Boolean");
1186 CHECK_NULL(cls);
1187 fid = (*env)->GetFieldID(env, cls, "value", "Z");
1188 CHECK_NULL(fid);
1189
1190 on = (*env)->GetBooleanField(env, value, fid);
1191 loopback = (!on ? 1 : 0);
1192
1193 if (NET_SetSockOpt(fd, IPPROTO_IP, IP_MULTICAST_LOOP,
1194 (const void *)&loopback, sizeof(char)) < 0) {
1195 JNU_ThrowByNameWithMessageAndLastError
1196 (env, JNU_JAVANETPKG "SocketException", "Error setting socket option");
1197 return;
1198 }
1199 }
1200
1201 /*
1202 * Enable/disable local loopback of multicast datagrams.
1203 */
mcast_set_loop_v6(JNIEnv * env,jobject this,int fd,jobject value)1204 static void mcast_set_loop_v6(JNIEnv *env, jobject this, int fd, jobject value) {
1205 jclass cls;
1206 jfieldID fid;
1207 jboolean on;
1208 int loopback;
1209
1210 cls = (*env)->FindClass(env, "java/lang/Boolean");
1211 CHECK_NULL(cls);
1212 fid = (*env)->GetFieldID(env, cls, "value", "Z");
1213 CHECK_NULL(fid);
1214
1215 on = (*env)->GetBooleanField(env, value, fid);
1216 loopback = (!on ? 1 : 0);
1217
1218 if (NET_SetSockOpt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
1219 (const void *)&loopback, sizeof(int)) < 0) {
1220 JNU_ThrowByNameWithMessageAndLastError
1221 (env, JNU_JAVANETPKG "SocketException", "Error setting socket option");
1222 return;
1223 }
1224
1225 }
1226
1227 /*
1228 * Sets the multicast loopback mode.
1229 */
setMulticastLoopbackMode(JNIEnv * env,jobject this,int fd,jint opt,jobject value)1230 static void setMulticastLoopbackMode(JNIEnv *env, jobject this, int fd,
1231 jint opt, jobject value) {
1232 #ifdef __linux__
1233 mcast_set_loop_v4(env, this, fd, value);
1234 if (ipv6_available()) {
1235 if ((*env)->ExceptionCheck(env)){
1236 (*env)->ExceptionClear(env);
1237 }
1238 mcast_set_loop_v6(env, this, fd, value);
1239 }
1240 #else /* __linux__ not defined */
1241 if (ipv6_available()) {
1242 mcast_set_loop_v6(env, this, fd, value);
1243 } else {
1244 mcast_set_loop_v4(env, this, fd, value);
1245 }
1246 #endif /* __linux__ */
1247 }
1248
1249 /*
1250 * Class: java_net_PlainDatagramSocketImpl
1251 * Method: socketSetOption0
1252 * Signature: (ILjava/lang/Object;)V
1253 */
1254 JNIEXPORT void JNICALL
Java_java_net_PlainDatagramSocketImpl_socketSetOption0(JNIEnv * env,jobject this,jint opt,jobject value)1255 Java_java_net_PlainDatagramSocketImpl_socketSetOption0
1256 (JNIEnv *env, jobject this, jint opt, jobject value)
1257 {
1258 int fd;
1259 int level, optname, optlen;
1260 int optval;
1261 optlen = sizeof(int);
1262
1263 /*
1264 * Check that socket hasn't been closed
1265 */
1266 fd = getFD(env, this);
1267 if (fd < 0) {
1268 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
1269 "Socket closed");
1270 return;
1271 }
1272
1273 /*
1274 * Check argument has been provided
1275 */
1276 if (IS_NULL(value)) {
1277 JNU_ThrowNullPointerException(env, "value argument");
1278 return;
1279 }
1280
1281 /*
1282 * Setting the multicast interface handled separately
1283 */
1284 if (opt == java_net_SocketOptions_IP_MULTICAST_IF ||
1285 opt == java_net_SocketOptions_IP_MULTICAST_IF2) {
1286
1287 setMulticastInterface(env, this, fd, opt, value);
1288 return;
1289 }
1290
1291 /*
1292 * Setting the multicast loopback mode handled separately
1293 */
1294 if (opt == java_net_SocketOptions_IP_MULTICAST_LOOP) {
1295 setMulticastLoopbackMode(env, this, fd, opt, value);
1296 return;
1297 }
1298
1299 /*
1300 * Map the Java level socket option to the platform specific
1301 * level and option name.
1302 */
1303 if (NET_MapSocketOption(opt, &level, &optname)) {
1304 JNU_ThrowByName(env, "java/net/SocketException", "Invalid option");
1305 return;
1306 }
1307
1308 switch (opt) {
1309 case java_net_SocketOptions_SO_SNDBUF :
1310 case java_net_SocketOptions_SO_RCVBUF :
1311 case java_net_SocketOptions_IP_TOS :
1312 {
1313 jclass cls;
1314 jfieldID fid;
1315
1316 cls = (*env)->FindClass(env, "java/lang/Integer");
1317 CHECK_NULL(cls);
1318 fid = (*env)->GetFieldID(env, cls, "value", "I");
1319 CHECK_NULL(fid);
1320
1321 optval = (*env)->GetIntField(env, value, fid);
1322 break;
1323 }
1324
1325 case java_net_SocketOptions_SO_REUSEADDR:
1326 case java_net_SocketOptions_SO_REUSEPORT:
1327 case java_net_SocketOptions_SO_BROADCAST:
1328 {
1329 jclass cls;
1330 jfieldID fid;
1331 jboolean on;
1332
1333 cls = (*env)->FindClass(env, "java/lang/Boolean");
1334 CHECK_NULL(cls);
1335 fid = (*env)->GetFieldID(env, cls, "value", "Z");
1336 CHECK_NULL(fid);
1337
1338 on = (*env)->GetBooleanField(env, value, fid);
1339
1340 /* SO_REUSEADDR or SO_BROADCAST */
1341 optval = (on ? 1 : 0);
1342
1343 break;
1344 }
1345
1346 default :
1347 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
1348 "Socket option not supported by PlainDatagramSocketImp");
1349 return;
1350
1351 }
1352
1353 if (NET_SetSockOpt(fd, level, optname, (const void *)&optval, optlen) < 0) {
1354 JNU_ThrowByNameWithMessageAndLastError
1355 (env, JNU_JAVANETPKG "SocketException", "Error setting socket option");
1356 return;
1357 }
1358 }
1359
1360
1361 /*
1362 * Return the multicast interface:
1363 *
1364 * SocketOptions.IP_MULTICAST_IF
1365 * IPv4: Query IPPROTO_IP/IP_MULTICAST_IF
1366 * Create InetAddress
1367 * IP_MULTICAST_IF returns struct ip_mreqn on 2.2
1368 * kernel but struct in_addr on 2.4 kernel
1369 * IPv6: Query IPPROTO_IPV6 / IPV6_MULTICAST_IF
1370 * If index == 0 return InetAddress representing
1371 * anyLocalAddress.
1372 * If index > 0 query NetworkInterface by index
1373 * and returns addrs[0]
1374 *
1375 * SocketOptions.IP_MULTICAST_IF2
1376 * IPv4: Query IPPROTO_IP/IP_MULTICAST_IF
1377 * Query NetworkInterface by IP address and
1378 * return the NetworkInterface that the address
1379 * is bound too.
1380 * IPv6: Query IPPROTO_IPV6 / IPV6_MULTICAST_IF
1381 * (except Linux .2 kernel)
1382 * Query NetworkInterface by index and
1383 * return NetworkInterface.
1384 */
getMulticastInterface(JNIEnv * env,jobject this,int fd,jint opt)1385 jobject getMulticastInterface(JNIEnv *env, jobject this, int fd, jint opt) {
1386 jboolean isIPV4 = JNI_TRUE;
1387
1388 if (ipv6_available()) {
1389 isIPV4 = JNI_FALSE;
1390 }
1391
1392 /*
1393 * IPv4 implementation
1394 */
1395 if (isIPV4) {
1396 static jclass inet4_class;
1397 static jmethodID inet4_ctrID;
1398
1399 static jclass ni_class;
1400 static jmethodID ni_ctrID;
1401 static jfieldID ni_indexID;
1402 static jfieldID ni_addrsID;
1403 static jfieldID ni_nameID;
1404
1405 jobjectArray addrArray;
1406 jobject addr;
1407 jobject ni;
1408 jobject ni_name;
1409
1410 struct in_addr in;
1411 struct in_addr *inP = ∈
1412 socklen_t len = sizeof(struct in_addr);
1413
1414 if (getsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF,
1415 (char *)inP, &len) < 0) {
1416 JNU_ThrowByNameWithMessageAndLastError
1417 (env, JNU_JAVANETPKG "SocketException", "Error getting socket option");
1418 return NULL;
1419 }
1420
1421 /*
1422 * Construct and populate an Inet4Address
1423 */
1424 if (inet4_class == NULL) {
1425 jclass c = (*env)->FindClass(env, "java/net/Inet4Address");
1426 CHECK_NULL_RETURN(c, NULL);
1427 inet4_ctrID = (*env)->GetMethodID(env, c, "<init>", "()V");
1428 CHECK_NULL_RETURN(inet4_ctrID, NULL);
1429 inet4_class = (*env)->NewGlobalRef(env, c);
1430 CHECK_NULL_RETURN(inet4_class, NULL);
1431 }
1432 addr = (*env)->NewObject(env, inet4_class, inet4_ctrID, 0);
1433 CHECK_NULL_RETURN(addr, NULL);
1434
1435 setInetAddress_addr(env, addr, ntohl(in.s_addr));
1436 JNU_CHECK_EXCEPTION_RETURN(env, NULL);
1437
1438 /*
1439 * For IP_MULTICAST_IF return InetAddress
1440 */
1441 if (opt == java_net_SocketOptions_IP_MULTICAST_IF) {
1442 return addr;
1443 }
1444
1445 /*
1446 * For IP_MULTICAST_IF2 we get the NetworkInterface for
1447 * this address and return it
1448 */
1449 if (ni_class == NULL) {
1450 jclass c = (*env)->FindClass(env, "java/net/NetworkInterface");
1451 CHECK_NULL_RETURN(c, NULL);
1452 ni_ctrID = (*env)->GetMethodID(env, c, "<init>", "()V");
1453 CHECK_NULL_RETURN(ni_ctrID, NULL);
1454 ni_indexID = (*env)->GetFieldID(env, c, "index", "I");
1455 CHECK_NULL_RETURN(ni_indexID, NULL);
1456 ni_addrsID = (*env)->GetFieldID(env, c, "addrs",
1457 "[Ljava/net/InetAddress;");
1458 CHECK_NULL_RETURN(ni_addrsID, NULL);
1459 ni_nameID = (*env)->GetFieldID(env, c,"name", "Ljava/lang/String;");
1460 CHECK_NULL_RETURN(ni_nameID, NULL);
1461 ni_class = (*env)->NewGlobalRef(env, c);
1462 CHECK_NULL_RETURN(ni_class, NULL);
1463 }
1464 ni = Java_java_net_NetworkInterface_getByInetAddress0(env, ni_class, addr);
1465 JNU_CHECK_EXCEPTION_RETURN(env, NULL);
1466 if (ni) {
1467 return ni;
1468 }
1469 return NULL;
1470 }
1471
1472
1473 /*
1474 * IPv6 implementation
1475 */
1476 if ((opt == java_net_SocketOptions_IP_MULTICAST_IF) ||
1477 (opt == java_net_SocketOptions_IP_MULTICAST_IF2)) {
1478
1479 static jclass ni_class;
1480 static jmethodID ni_ctrID;
1481 static jfieldID ni_indexID;
1482 static jfieldID ni_addrsID;
1483 static jclass ia_class;
1484 static jfieldID ni_nameID;
1485 static jmethodID ia_anyLocalAddressID;
1486
1487 int index = 0;
1488 socklen_t len = sizeof(index);
1489
1490 jobjectArray addrArray;
1491 jobject addr;
1492 jobject ni;
1493 jobject ni_name;
1494
1495 if (getsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF,
1496 (char*)&index, &len) < 0) {
1497 JNU_ThrowByNameWithMessageAndLastError
1498 (env, JNU_JAVANETPKG "SocketException", "Error getting socket option");
1499 return NULL;
1500 }
1501
1502 if (ni_class == NULL) {
1503 jclass c = (*env)->FindClass(env, "java/net/NetworkInterface");
1504 CHECK_NULL_RETURN(c, NULL);
1505 ni_ctrID = (*env)->GetMethodID(env, c, "<init>", "()V");
1506 CHECK_NULL_RETURN(ni_ctrID, NULL);
1507 ni_indexID = (*env)->GetFieldID(env, c, "index", "I");
1508 CHECK_NULL_RETURN(ni_indexID, NULL);
1509 ni_addrsID = (*env)->GetFieldID(env, c, "addrs",
1510 "[Ljava/net/InetAddress;");
1511 CHECK_NULL_RETURN(ni_addrsID, NULL);
1512
1513 ia_class = (*env)->FindClass(env, "java/net/InetAddress");
1514 CHECK_NULL_RETURN(ia_class, NULL);
1515 ia_class = (*env)->NewGlobalRef(env, ia_class);
1516 CHECK_NULL_RETURN(ia_class, NULL);
1517 ia_anyLocalAddressID = (*env)->GetStaticMethodID(env,
1518 ia_class,
1519 "anyLocalAddress",
1520 "()Ljava/net/InetAddress;");
1521 CHECK_NULL_RETURN(ia_anyLocalAddressID, NULL);
1522 ni_nameID = (*env)->GetFieldID(env, c,"name", "Ljava/lang/String;");
1523 CHECK_NULL_RETURN(ni_nameID, NULL);
1524 ni_class = (*env)->NewGlobalRef(env, c);
1525 CHECK_NULL_RETURN(ni_class, NULL);
1526 }
1527
1528 /*
1529 * If multicast to a specific interface then return the
1530 * interface (for IF2) or the any address on that interface
1531 * (for IF).
1532 */
1533 if (index > 0) {
1534 ni = Java_java_net_NetworkInterface_getByIndex0(env, ni_class,
1535 index);
1536 if (ni == NULL) {
1537 char errmsg[255];
1538 sprintf(errmsg,
1539 "IPV6_MULTICAST_IF returned index to unrecognized interface: %d",
1540 index);
1541 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", errmsg);
1542 return NULL;
1543 }
1544
1545 /*
1546 * For IP_MULTICAST_IF2 return the NetworkInterface
1547 */
1548 if (opt == java_net_SocketOptions_IP_MULTICAST_IF2) {
1549 return ni;
1550 }
1551
1552 /*
1553 * For IP_MULTICAST_IF return addrs[0]
1554 */
1555 addrArray = (*env)->GetObjectField(env, ni, ni_addrsID);
1556 if ((*env)->GetArrayLength(env, addrArray) < 1) {
1557 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
1558 "IPV6_MULTICAST_IF returned interface without IP bindings");
1559 return NULL;
1560 }
1561
1562 addr = (*env)->GetObjectArrayElement(env, addrArray, 0);
1563 return addr;
1564 }
1565
1566 /*
1567 * Multicast to any address - return anyLocalAddress
1568 * or a NetworkInterface with addrs[0] set to anyLocalAddress
1569 */
1570
1571 addr = (*env)->CallStaticObjectMethod(env, ia_class, ia_anyLocalAddressID,
1572 NULL);
1573 if (opt == java_net_SocketOptions_IP_MULTICAST_IF) {
1574 return addr;
1575 }
1576 }
1577 return NULL;
1578 }
1579
1580
1581
1582 /*
1583 * Returns relevant info as a jint.
1584 *
1585 * Class: java_net_PlainDatagramSocketImpl
1586 * Method: socketGetOption
1587 * Signature: (I)Ljava/lang/Object;
1588 */
1589 JNIEXPORT jobject JNICALL
Java_java_net_PlainDatagramSocketImpl_socketGetOption(JNIEnv * env,jobject this,jint opt)1590 Java_java_net_PlainDatagramSocketImpl_socketGetOption
1591 (JNIEnv *env, jobject this, jint opt)
1592 {
1593 int fd;
1594 int level, optname, optlen;
1595 union {
1596 int i;
1597 char c;
1598 } optval;
1599
1600 fd = getFD(env, this);
1601 if (fd < 0) {
1602 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
1603 "socket closed");
1604 return NULL;
1605 }
1606
1607 /*
1608 * Handle IP_MULTICAST_IF separately
1609 */
1610 if (opt == java_net_SocketOptions_IP_MULTICAST_IF ||
1611 opt == java_net_SocketOptions_IP_MULTICAST_IF2) {
1612 return getMulticastInterface(env, this, fd, opt);
1613
1614 }
1615
1616 /*
1617 * SO_BINDADDR implemented using getsockname
1618 */
1619 if (opt == java_net_SocketOptions_SO_BINDADDR) {
1620 /* find out local IP address */
1621 SOCKETADDRESS sa;
1622 socklen_t len = sizeof(SOCKETADDRESS);
1623 int port;
1624 jobject iaObj;
1625
1626 if (getsockname(fd, &sa.sa, &len) == -1) {
1627 JNU_ThrowByNameWithMessageAndLastError
1628 (env, JNU_JAVANETPKG "SocketException", "Error getting socket name");
1629 return NULL;
1630 }
1631 iaObj = NET_SockaddrToInetAddress(env, &sa, &port);
1632
1633 return iaObj;
1634 }
1635
1636 /*
1637 * Map the Java level socket option to the platform specific
1638 * level and option name.
1639 */
1640 if (NET_MapSocketOption(opt, &level, &optname)) {
1641 JNU_ThrowByName(env, "java/net/SocketException", "Invalid option");
1642 return NULL;
1643 }
1644
1645 if (opt == java_net_SocketOptions_IP_MULTICAST_LOOP &&
1646 level == IPPROTO_IP) {
1647 optlen = sizeof(optval.c);
1648 } else {
1649 optlen = sizeof(optval.i);
1650 }
1651
1652 if (NET_GetSockOpt(fd, level, optname, (void *)&optval, &optlen) < 0) {
1653 JNU_ThrowByNameWithMessageAndLastError
1654 (env, JNU_JAVANETPKG "SocketException", "Error getting socket option");
1655 return NULL;
1656 }
1657
1658 switch (opt) {
1659 case java_net_SocketOptions_IP_MULTICAST_LOOP:
1660 /* getLoopbackMode() returns true if IP_MULTICAST_LOOP disabled */
1661 if (level == IPPROTO_IP) {
1662 return createBoolean(env, (int)!optval.c);
1663 } else {
1664 return createBoolean(env, !optval.i);
1665 }
1666
1667 case java_net_SocketOptions_SO_BROADCAST:
1668 case java_net_SocketOptions_SO_REUSEADDR:
1669 return createBoolean(env, optval.i);
1670
1671 case java_net_SocketOptions_SO_REUSEPORT:
1672 return createBoolean(env, optval.i);
1673
1674 case java_net_SocketOptions_SO_SNDBUF:
1675 case java_net_SocketOptions_SO_RCVBUF:
1676 case java_net_SocketOptions_IP_TOS:
1677 return createInteger(env, optval.i);
1678
1679 }
1680
1681 /* should never reach here */
1682 return NULL;
1683 }
1684
1685 /*
1686 * Multicast-related calls
1687 */
1688
1689 JNIEXPORT void JNICALL
Java_java_net_PlainDatagramSocketImpl_setTTL(JNIEnv * env,jobject this,jbyte ttl)1690 Java_java_net_PlainDatagramSocketImpl_setTTL(JNIEnv *env, jobject this,
1691 jbyte ttl) {
1692 jint ittl = ttl;
1693 if (ittl < 0) {
1694 ittl += 0x100;
1695 }
1696 Java_java_net_PlainDatagramSocketImpl_setTimeToLive(env, this, ittl);
1697 }
1698
1699 /*
1700 * Set TTL for a socket. Throw exception if failed.
1701 */
setTTL(JNIEnv * env,int fd,jint ttl)1702 static void setTTL(JNIEnv *env, int fd, jint ttl) {
1703 char ittl = (char)ttl;
1704 if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, (char*)&ittl,
1705 sizeof(ittl)) < 0) {
1706 JNU_ThrowByNameWithMessageAndLastError
1707 (env, JNU_JAVANETPKG "SocketException", "Error setting socket option");
1708 }
1709 }
1710
1711 /*
1712 * Set hops limit for a socket. Throw exception if failed.
1713 */
setHopLimit(JNIEnv * env,int fd,jint ttl)1714 static void setHopLimit(JNIEnv *env, int fd, jint ttl) {
1715 int ittl = (int)ttl;
1716 if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
1717 (char*)&ittl, sizeof(ittl)) < 0) {
1718 JNU_ThrowByNameWithMessageAndLastError
1719 (env, JNU_JAVANETPKG "SocketException", "Error setting socket option");
1720 }
1721 }
1722
1723 /*
1724 * Class: java_net_PlainDatagramSocketImpl
1725 * Method: setTTL
1726 * Signature: (B)V
1727 */
1728 JNIEXPORT void JNICALL
Java_java_net_PlainDatagramSocketImpl_setTimeToLive(JNIEnv * env,jobject this,jint ttl)1729 Java_java_net_PlainDatagramSocketImpl_setTimeToLive(JNIEnv *env, jobject this,
1730 jint ttl) {
1731
1732 jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
1733 int fd;
1734 /* it is important to cast this to a char, otherwise setsockopt gets confused */
1735
1736 if (IS_NULL(fdObj)) {
1737 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
1738 "Socket closed");
1739 return;
1740 } else {
1741 fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
1742 }
1743 /* setsockopt to be correct TTL */
1744 #ifdef __linux__
1745 setTTL(env, fd, ttl);
1746 JNU_CHECK_EXCEPTION(env);
1747 if (ipv6_available()) {
1748 setHopLimit(env, fd, ttl);
1749 }
1750 #else /* __linux__ not defined */
1751 if (ipv6_available()) {
1752 setHopLimit(env, fd, ttl);
1753 } else {
1754 setTTL(env, fd, ttl);
1755 }
1756 #endif /* __linux__ */
1757 }
1758
1759 /*
1760 * Class: java_net_PlainDatagramSocketImpl
1761 * Method: getTTL
1762 * Signature: ()B
1763 */
1764 JNIEXPORT jbyte JNICALL
Java_java_net_PlainDatagramSocketImpl_getTTL(JNIEnv * env,jobject this)1765 Java_java_net_PlainDatagramSocketImpl_getTTL(JNIEnv *env, jobject this) {
1766 return (jbyte)Java_java_net_PlainDatagramSocketImpl_getTimeToLive(env, this);
1767 }
1768
1769
1770 /*
1771 * Class: java_net_PlainDatagramSocketImpl
1772 * Method: getTTL
1773 * Signature: ()B
1774 */
1775 JNIEXPORT jint JNICALL
Java_java_net_PlainDatagramSocketImpl_getTimeToLive(JNIEnv * env,jobject this)1776 Java_java_net_PlainDatagramSocketImpl_getTimeToLive(JNIEnv *env, jobject this) {
1777
1778 jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
1779 jint fd = -1;
1780
1781 if (IS_NULL(fdObj)) {
1782 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
1783 "Socket closed");
1784 return -1;
1785 } else {
1786 fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
1787 }
1788 /* getsockopt of TTL */
1789 if (ipv6_available()) {
1790 int ttl = 0;
1791 socklen_t len = sizeof(ttl);
1792
1793 if (getsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
1794 (char*)&ttl, &len) < 0) {
1795 JNU_ThrowByNameWithMessageAndLastError
1796 (env, JNU_JAVANETPKG "SocketException", "Error getting socket option");
1797 return -1;
1798 }
1799 return (jint)ttl;
1800 } else {
1801 u_char ttl = 0;
1802 socklen_t len = sizeof(ttl);
1803 if (getsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL,
1804 (char*)&ttl, &len) < 0) {
1805 JNU_ThrowByNameWithMessageAndLastError
1806 (env, JNU_JAVANETPKG "SocketException", "Error getting socket option");
1807 return -1;
1808 }
1809 return (jint)ttl;
1810 }
1811 }
1812
1813
1814 /*
1815 * mcast_join_leave: Join or leave a multicast group.
1816 *
1817 * For IPv4 sockets use IP_ADD_MEMBERSHIP/IP_DROP_MEMBERSHIP socket option
1818 * to join/leave multicast group.
1819 *
1820 * For IPv6 sockets use IPV6_ADD_MEMBERSHIP/IPV6_DROP_MEMBERSHIP socket option
1821 * to join/leave multicast group. If multicast group is an IPv4 address then
1822 * an IPv4-mapped address is used.
1823 *
1824 * On Linux with IPv6 if we wish to join/leave an IPv4 multicast group then
1825 * we must use the IPv4 socket options. This is because the IPv6 socket options
1826 * don't support IPv4-mapped addresses. This is true as per 2.2.19 and 2.4.7
1827 * kernel releases. In the future it's possible that IP_ADD_MEMBERSHIP
1828 * will be updated to return ENOPROTOOPT if uses with an IPv6 socket. Thus to
1829 * cater for this we first try with the IPv4 socket options and if they fail we
1830 * use the IPv6 socket options. This seems a reasonable failsafe solution.
1831 */
mcast_join_leave(JNIEnv * env,jobject this,jobject iaObj,jobject niObj,jboolean join)1832 static void mcast_join_leave(JNIEnv *env, jobject this,
1833 jobject iaObj, jobject niObj,
1834 jboolean join) {
1835
1836 jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
1837 jint fd;
1838 jint family;
1839 jint ipv6_join_leave;
1840
1841 if (IS_NULL(fdObj)) {
1842 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
1843 "Socket closed");
1844 return;
1845 } else {
1846 fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
1847 }
1848 if (IS_NULL(iaObj)) {
1849 JNU_ThrowNullPointerException(env, "iaObj");
1850 return;
1851 }
1852
1853 /*
1854 * Determine if this is an IPv4 or IPv6 join/leave.
1855 */
1856 ipv6_join_leave = ipv6_available();
1857
1858 #ifdef __linux__
1859 family = getInetAddress_family(env, iaObj);
1860 JNU_CHECK_EXCEPTION(env);
1861 if (family == java_net_InetAddress_IPv4) {
1862 ipv6_join_leave = JNI_FALSE;
1863 }
1864 #endif
1865
1866 /*
1867 * For IPv4 join use IP_ADD_MEMBERSHIP/IP_DROP_MEMBERSHIP socket option
1868 *
1869 * On Linux if IPv4 or IPv6 use IP_ADD_MEMBERSHIP/IP_DROP_MEMBERSHIP
1870 */
1871 if (!ipv6_join_leave) {
1872 #ifdef __linux__
1873 struct ip_mreqn mname;
1874 #else
1875 struct ip_mreq mname;
1876 #endif
1877 int mname_len;
1878
1879 /*
1880 * joinGroup(InetAddress, NetworkInterface) implementation :-
1881 *
1882 * Linux/IPv6: use ip_mreqn structure populated with multicast
1883 * address and interface index.
1884 *
1885 * IPv4: use ip_mreq structure populated with multicast
1886 * address and first address obtained from
1887 * NetworkInterface
1888 */
1889 if (niObj != NULL) {
1890 #if defined(__linux__)
1891 if (ipv6_available()) {
1892 static jfieldID ni_indexID;
1893
1894 if (ni_indexID == NULL) {
1895 jclass c = (*env)->FindClass(env, "java/net/NetworkInterface");
1896 CHECK_NULL(c);
1897 ni_indexID = (*env)->GetFieldID(env, c, "index", "I");
1898 CHECK_NULL(ni_indexID);
1899 }
1900
1901 mname.imr_multiaddr.s_addr = htonl(getInetAddress_addr(env, iaObj));
1902 JNU_CHECK_EXCEPTION(env);
1903 mname.imr_address.s_addr = 0;
1904 mname.imr_ifindex = (*env)->GetIntField(env, niObj, ni_indexID);
1905 mname_len = sizeof(struct ip_mreqn);
1906 } else
1907 #endif
1908 {
1909 jobjectArray addrArray = (*env)->GetObjectField(env, niObj, ni_addrsID);
1910 jobject addr;
1911
1912 if ((*env)->GetArrayLength(env, addrArray) < 1) {
1913 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
1914 "bad argument for IP_ADD_MEMBERSHIP: "
1915 "No IP addresses bound to interface");
1916 return;
1917 }
1918 addr = (*env)->GetObjectArrayElement(env, addrArray, 0);
1919
1920 mname.imr_multiaddr.s_addr = htonl(getInetAddress_addr(env, iaObj));
1921 JNU_CHECK_EXCEPTION(env);
1922 #ifdef __linux__
1923 mname.imr_address.s_addr = htonl(getInetAddress_addr(env, addr));
1924 JNU_CHECK_EXCEPTION(env);
1925 mname.imr_ifindex = 0;
1926 #else
1927 mname.imr_interface.s_addr = htonl(getInetAddress_addr(env, addr));
1928 JNU_CHECK_EXCEPTION(env);
1929 #endif
1930 mname_len = sizeof(struct ip_mreq);
1931 }
1932 }
1933
1934
1935 /*
1936 * joinGroup(InetAddress) implementation :-
1937 *
1938 * Linux/IPv6: use ip_mreqn structure populated with multicast
1939 * address and interface index. index obtained
1940 * from cached value or IPV6_MULTICAST_IF.
1941 *
1942 * IPv4: use ip_mreq structure populated with multicast
1943 * address and local address obtained from
1944 * IP_MULTICAST_IF. On Linux IP_MULTICAST_IF
1945 * returns different structure depending on
1946 * kernel.
1947 */
1948
1949 if (niObj == NULL) {
1950
1951 #if defined(__linux__)
1952 if (ipv6_available()) {
1953
1954 int index;
1955 socklen_t len = sizeof(index);
1956
1957 if (getsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF,
1958 (char*)&index, &len) < 0) {
1959 NET_ThrowCurrent(env, "getsockopt IPV6_MULTICAST_IF failed");
1960 return;
1961 }
1962
1963 mname.imr_multiaddr.s_addr = htonl(getInetAddress_addr(env, iaObj));
1964 JNU_CHECK_EXCEPTION(env);
1965 mname.imr_address.s_addr = 0 ;
1966 mname.imr_ifindex = index;
1967 mname_len = sizeof(struct ip_mreqn);
1968 } else
1969 #endif
1970 {
1971 struct in_addr in;
1972 struct in_addr *inP = ∈
1973 socklen_t len = sizeof(struct in_addr);
1974
1975 if (getsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, (char *)inP, &len) < 0) {
1976 NET_ThrowCurrent(env, "getsockopt IP_MULTICAST_IF failed");
1977 return;
1978 }
1979
1980 #ifdef __linux__
1981 mname.imr_address.s_addr = in.s_addr;
1982 mname.imr_ifindex = 0;
1983 #else
1984 mname.imr_interface.s_addr = in.s_addr;
1985 #endif
1986 mname.imr_multiaddr.s_addr = htonl(getInetAddress_addr(env, iaObj));
1987 JNU_CHECK_EXCEPTION(env);
1988 mname_len = sizeof(struct ip_mreq);
1989 }
1990 }
1991
1992
1993 /*
1994 * Join the multicast group.
1995 */
1996 if (setsockopt(fd, IPPROTO_IP, (join ? IP_ADD_MEMBERSHIP:IP_DROP_MEMBERSHIP),
1997 (char *) &mname, mname_len) < 0) {
1998
1999 /*
2000 * If IP_ADD_MEMBERSHIP returns ENOPROTOOPT on Linux and we've got
2001 * IPv6 enabled then it's possible that the kernel has been fixed
2002 * so we switch to IPV6_ADD_MEMBERSHIP socket option.
2003 * As of 2.4.7 kernel IPV6_ADD_MEMBERSHIP can't handle IPv4-mapped
2004 * addresses so we have to use IP_ADD_MEMBERSHIP for IPv4 multicast
2005 * groups. However if the socket is an IPv6 socket then setsockopt
2006 * should return ENOPROTOOPT. We assume this will be fixed in Linux
2007 * at some stage.
2008 */
2009 #if defined(__linux__)
2010 if (errno == ENOPROTOOPT) {
2011 if (ipv6_available()) {
2012 ipv6_join_leave = JNI_TRUE;
2013 errno = 0;
2014 } else {
2015 errno = ENOPROTOOPT; /* errno can be changed by ipv6_available */
2016 }
2017 }
2018 #endif
2019 if (errno) {
2020 if (join) {
2021 NET_ThrowCurrent(env, "setsockopt IP_ADD_MEMBERSHIP failed");
2022 } else {
2023 if (errno == ENOENT)
2024 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
2025 "Not a member of the multicast group");
2026 else
2027 NET_ThrowCurrent(env, "setsockopt IP_DROP_MEMBERSHIP failed");
2028 }
2029 return;
2030 }
2031 }
2032
2033 /*
2034 * If we haven't switched to IPv6 socket option then we're done.
2035 */
2036 if (!ipv6_join_leave) {
2037 return;
2038 }
2039 }
2040
2041
2042 /*
2043 * IPv6 join. If it's an IPv4 multicast group then we use an IPv4-mapped
2044 * address.
2045 */
2046 {
2047 struct ipv6_mreq mname6;
2048 jbyteArray ipaddress;
2049 jbyte caddr[16];
2050 jint family;
2051 jint address;
2052 family = getInetAddress_family(env, iaObj) == java_net_InetAddress_IPv4 ?
2053 AF_INET : AF_INET6;
2054 JNU_CHECK_EXCEPTION(env);
2055 if (family == AF_INET) { /* will convert to IPv4-mapped address */
2056 memset((char *) caddr, 0, 16);
2057 address = getInetAddress_addr(env, iaObj);
2058 JNU_CHECK_EXCEPTION(env);
2059 caddr[10] = 0xff;
2060 caddr[11] = 0xff;
2061
2062 caddr[12] = ((address >> 24) & 0xff);
2063 caddr[13] = ((address >> 16) & 0xff);
2064 caddr[14] = ((address >> 8) & 0xff);
2065 caddr[15] = (address & 0xff);
2066 } else {
2067 getInet6Address_ipaddress(env, iaObj, (char*)caddr);
2068 }
2069
2070 memcpy((void *)&(mname6.ipv6mr_multiaddr), caddr, sizeof(struct in6_addr));
2071 if (IS_NULL(niObj)) {
2072 int index;
2073 socklen_t len = sizeof(index);
2074
2075 if (getsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF,
2076 (char*)&index, &len) < 0) {
2077 NET_ThrowCurrent(env, "getsockopt IPV6_MULTICAST_IF failed");
2078 return;
2079 }
2080 mname6.ipv6mr_interface = index;
2081 } else {
2082 jint idx = (*env)->GetIntField(env, niObj, ni_indexID);
2083 mname6.ipv6mr_interface = idx;
2084 }
2085
2086 #if defined(_ALLBSD_SOURCE)
2087 #define ADD_MEMBERSHIP IPV6_JOIN_GROUP
2088 #define DRP_MEMBERSHIP IPV6_LEAVE_GROUP
2089 #define S_ADD_MEMBERSHIP "IPV6_JOIN_GROUP"
2090 #define S_DRP_MEMBERSHIP "IPV6_LEAVE_GROUP"
2091 #else
2092 #define ADD_MEMBERSHIP IPV6_ADD_MEMBERSHIP
2093 #define DRP_MEMBERSHIP IPV6_DROP_MEMBERSHIP
2094 #define S_ADD_MEMBERSHIP "IPV6_ADD_MEMBERSHIP"
2095 #define S_DRP_MEMBERSHIP "IPV6_DROP_MEMBERSHIP"
2096 #endif
2097
2098 /* Join the multicast group */
2099 if (setsockopt(fd, IPPROTO_IPV6, (join ? ADD_MEMBERSHIP : DRP_MEMBERSHIP),
2100 (char *) &mname6, sizeof (mname6)) < 0) {
2101
2102 if (join) {
2103 NET_ThrowCurrent(env, "setsockopt " S_ADD_MEMBERSHIP " failed");
2104 } else {
2105 if (errno == ENOENT) {
2106 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
2107 "Not a member of the multicast group");
2108 } else {
2109 NET_ThrowCurrent(env, "setsockopt " S_DRP_MEMBERSHIP " failed");
2110 }
2111 }
2112 }
2113 }
2114 }
2115
2116 /*
2117 * Class: java_net_PlainDatagramSocketImpl
2118 * Method: join
2119 * Signature: (Ljava/net/InetAddress;)V
2120 */
2121 JNIEXPORT void JNICALL
Java_java_net_PlainDatagramSocketImpl_join(JNIEnv * env,jobject this,jobject iaObj,jobject niObj)2122 Java_java_net_PlainDatagramSocketImpl_join(JNIEnv *env, jobject this,
2123 jobject iaObj, jobject niObj)
2124 {
2125 mcast_join_leave(env, this, iaObj, niObj, JNI_TRUE);
2126 }
2127
2128 /*
2129 * Class: java_net_PlainDatagramSocketImpl
2130 * Method: leave
2131 * Signature: (Ljava/net/InetAddress;)V
2132 */
2133 JNIEXPORT void JNICALL
Java_java_net_PlainDatagramSocketImpl_leave(JNIEnv * env,jobject this,jobject iaObj,jobject niObj)2134 Java_java_net_PlainDatagramSocketImpl_leave(JNIEnv *env, jobject this,
2135 jobject iaObj, jobject niObj)
2136 {
2137 mcast_join_leave(env, this, iaObj, niObj, JNI_FALSE);
2138 }
2139
2140 /*
2141 * Class: java_net_PlainDatagramSocketImpl
2142 * Method: dataAvailable
2143 * Signature: ()I
2144 */
2145 JNIEXPORT jint JNICALL
Java_java_net_PlainDatagramSocketImpl_dataAvailable(JNIEnv * env,jobject this)2146 Java_java_net_PlainDatagramSocketImpl_dataAvailable(JNIEnv *env, jobject this)
2147 {
2148 int fd, retval;
2149
2150 jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
2151
2152 if (IS_NULL(fdObj)) {
2153 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
2154 "Socket closed");
2155 return -1;
2156 }
2157 fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
2158
2159 if (ioctl(fd, FIONREAD, &retval) < 0) {
2160 return -1;
2161 }
2162 return retval;
2163 }
2164