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