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