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