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