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