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