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