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 = &in;
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 = &in;
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