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 #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 
  72 static jfieldID IO_fd_fdID;
  73 
  74 static jfieldID pdsi_fdID;
  75 static jfieldID pdsi_timeoutID;
  76 static jfieldID pdsi_trafficClassID;
  77 static jfieldID pdsi_localPortID;
  78 static jfieldID pdsi_connected;
  79 static jfieldID pdsi_connectedAddress;
  80 static jfieldID pdsi_connectedPort;
  81 
  82 extern void setDefaultScopeID(JNIEnv *env, struct sockaddr *him);
  83 extern int getDefaultScopeID(JNIEnv *env);
  84 
  85 
  86 /*
  87  * Returns a java.lang.Integer based on 'i'
  88  */
  89 static 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  */
 108 static 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  */
 128 static 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  */
 141 JNIEXPORT void JNICALL
 142 Java_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  */
 174 JNIEXPORT void JNICALL
 175 Java_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_InetAddressToSockaddr0(env, iaObj, localport, &sa, &len,
 200                                    JNI_TRUE, 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  */
 241 JNIEXPORT void JNICALL
 242 Java_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  */
 282 JNIEXPORT void JNICALL
 283 Java_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  */
 340 JNIEXPORT void JNICALL
 341 Java_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  */
 463 JNIEXPORT jint JNICALL
 464 Java_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     JNU_CHECK_EXCEPTION_RETURN(env, -1);
 535     if (family == AF_INET) { /* this API can't handle IPV6 addresses */
 536         int address = getInetAddress_addr(env, iaObj);
 537         JNU_CHECK_EXCEPTION_RETURN(env, -1);
 538         setInetAddress_addr(env, addressObj, address);
 539         JNU_CHECK_EXCEPTION_RETURN(env, -1);
 540     }
 541     return port;
 542 }
 543 
 544 JNIEXPORT jint JNICALL
 545 Java_java_net_PlainDatagramSocketImpl_peekData(JNIEnv *env, jobject this,
 546                                            jobject packet) {
 547 
 548     char BUF[MAX_BUFFER_LEN];
 549     char *fullPacket = NULL;
 550     int mallocedPacket = JNI_FALSE;
 551     jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
 552     jint timeout = (*env)->GetIntField(env, this, pdsi_timeoutID);
 553     jbyteArray packetBuffer;
 554     jint packetBufferOffset, packetBufferLen;
 555     int fd;
 556     int n;
 557     SOCKETADDRESS rmtaddr;
 558     socklen_t slen = sizeof(SOCKETADDRESS);
 559     int port = -1;
 560 
 561     if (IS_NULL(fdObj)) {
 562         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
 563                         "Socket closed");
 564         return -1;
 565     }
 566 
 567     fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
 568 
 569     if (IS_NULL(packet)) {
 570         JNU_ThrowNullPointerException(env, "packet");
 571         return -1;
 572     }
 573 
 574     packetBuffer = (*env)->GetObjectField(env, packet, dp_bufID);
 575     if (IS_NULL(packetBuffer)) {
 576         JNU_ThrowNullPointerException(env, "packet buffer");
 577         return -1;
 578     }
 579     packetBufferOffset = (*env)->GetIntField(env, packet, dp_offsetID);
 580     packetBufferLen = (*env)->GetIntField(env, packet, dp_bufLengthID);
 581     if (timeout) {
 582         int ret = NET_Timeout(env, fd, timeout, JVM_NanoTime(env, 0));
 583         if (ret == 0) {
 584             JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException",
 585                             "Receive timed out");
 586             return -1;
 587         } else if (ret == -1) {
 588             if (errno == ENOMEM) {
 589                 JNU_ThrowOutOfMemoryError(env, "NET_Timeout native heap allocation failed");
 590 #ifdef __linux__
 591             } else if (errno == EBADF) {
 592                 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");
 593             } else {
 594                 JNU_ThrowByNameWithMessageAndLastError
 595                     (env, JNU_JAVANETPKG "SocketException", "Receive failed");
 596 #else
 597             } else {
 598                 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");
 599 #endif
 600             }
 601             return -1;
 602         }
 603     }
 604 
 605     if (packetBufferLen > MAX_BUFFER_LEN) {
 606 
 607         /* When JNI-ifying the JDK's IO routines, we turned
 608          * reads and writes of byte arrays of size greater
 609          * than 2048 bytes into several operations of size 2048.
 610          * This saves a malloc()/memcpy()/free() for big
 611          * buffers.  This is OK for file IO and TCP, but that
 612          * strategy violates the semantics of a datagram protocol.
 613          * (one big send) != (several smaller sends).  So here
 614          * we *must* allocate the buffer.  Note it needn't be bigger
 615          * than 65,536 (0xFFFF), the max size of an IP packet.
 616          * anything bigger is truncated anyway.
 617          *
 618          * We may want to use a smarter allocation scheme at some
 619          * point.
 620          */
 621         if (packetBufferLen > MAX_PACKET_LEN) {
 622             packetBufferLen = MAX_PACKET_LEN;
 623         }
 624         fullPacket = (char *)malloc(packetBufferLen);
 625 
 626         if (!fullPacket) {
 627             JNU_ThrowOutOfMemoryError(env, "Peek buffer native heap allocation failed");
 628             return -1;
 629         } else {
 630             mallocedPacket = JNI_TRUE;
 631         }
 632     } else {
 633         fullPacket = &(BUF[0]);
 634     }
 635 
 636     n = NET_RecvFrom(fd, fullPacket, packetBufferLen, MSG_PEEK,
 637                      &rmtaddr.sa, &slen);
 638     /* truncate the data if the packet's length is too small */
 639     if (n > packetBufferLen) {
 640         n = packetBufferLen;
 641     }
 642     if (n == -1) {
 643 
 644 #ifdef __solaris__
 645         if (errno == ECONNREFUSED) {
 646             int orig_errno = errno;
 647             (void) recv(fd, fullPacket, 1, 0);
 648             errno = orig_errno;
 649         }
 650 #endif
 651         (*env)->SetIntField(env, packet, dp_offsetID, 0);
 652         (*env)->SetIntField(env, packet, dp_lengthID, 0);
 653         if (errno == ECONNREFUSED) {
 654             JNU_ThrowByName(env, JNU_JAVANETPKG "PortUnreachableException",
 655                             "ICMP Port Unreachable");
 656         } else {
 657             if (errno == EBADF) {
 658                  JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");
 659             } else {
 660                  JNU_ThrowByNameWithMessageAndLastError
 661                      (env, JNU_JAVANETPKG "SocketException", "Receive failed");
 662             }
 663         }
 664     } else {
 665         /*
 666          * success - fill in received address...
 667          *
 668          * REMIND: Fill in an int on the packet, and create inetadd
 669          * object in Java, as a performance improvement. Also
 670          * construct the inetadd object lazily.
 671          */
 672 
 673         jobject packetAddress;
 674 
 675         /*
 676          * Check if there is an InetAddress already associated with this
 677          * packet. If so we check if it is the same source address. We
 678          * can't update any existing InetAddress because it is immutable
 679          */
 680         packetAddress = (*env)->GetObjectField(env, packet, dp_addressID);
 681         if (packetAddress != NULL) {
 682             if (!NET_SockaddrEqualsInetAddress(env, &rmtaddr, packetAddress)) {
 683                 /* force a new InetAddress to be created */
 684                 packetAddress = NULL;
 685             }
 686         }
 687         if (!(*env)->ExceptionCheck(env)){
 688             if (packetAddress == NULL ) {
 689                 packetAddress = NET_SockaddrToInetAddress(env, &rmtaddr, &port);
 690                 /* stuff the new InetAddress in the packet */
 691                 (*env)->SetObjectField(env, packet, dp_addressID, packetAddress);
 692             } else {
 693                 /* only get the new port number */
 694                 port = NET_GetPortFromSockaddr(&rmtaddr);
 695             }
 696             /* and fill in the data, remote address/port and such */
 697             (*env)->SetByteArrayRegion(env, packetBuffer, packetBufferOffset, n,
 698                                     (jbyte *)fullPacket);
 699             (*env)->SetIntField(env, packet, dp_portID, port);
 700             (*env)->SetIntField(env, packet, dp_lengthID, n);
 701         }
 702     }
 703 
 704     if (mallocedPacket) {
 705         free(fullPacket);
 706     }
 707     return port;
 708 }
 709 
 710 /*
 711  * Class:     java_net_PlainDatagramSocketImpl
 712  * Method:    receive
 713  * Signature: (Ljava/net/DatagramPacket;)V
 714  */
 715 JNIEXPORT void JNICALL
 716 Java_java_net_PlainDatagramSocketImpl_receive0(JNIEnv *env, jobject this,
 717                                               jobject packet) {
 718 
 719     char BUF[MAX_BUFFER_LEN];
 720     char *fullPacket = NULL;
 721     int mallocedPacket = JNI_FALSE;
 722     jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
 723     jint timeout = (*env)->GetIntField(env, this, pdsi_timeoutID);
 724 
 725     jbyteArray packetBuffer;
 726     jint packetBufferOffset, packetBufferLen;
 727 
 728     int fd;
 729 
 730     int n;
 731     SOCKETADDRESS rmtaddr;
 732     socklen_t slen = sizeof(SOCKETADDRESS);
 733     jboolean retry;
 734 #ifdef __linux__
 735     jboolean connected = JNI_FALSE;
 736     jobject connectedAddress = NULL;
 737     jint connectedPort = 0;
 738     jlong prevTime = 0;
 739 #endif
 740 
 741     if (IS_NULL(fdObj)) {
 742         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
 743                         "Socket closed");
 744         return;
 745     }
 746 
 747     fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
 748 
 749     if (IS_NULL(packet)) {
 750         JNU_ThrowNullPointerException(env, "packet");
 751         return;
 752     }
 753 
 754     packetBuffer = (*env)->GetObjectField(env, packet, dp_bufID);
 755     if (IS_NULL(packetBuffer)) {
 756         JNU_ThrowNullPointerException(env, "packet buffer");
 757         return;
 758     }
 759     packetBufferOffset = (*env)->GetIntField(env, packet, dp_offsetID);
 760     packetBufferLen = (*env)->GetIntField(env, packet, dp_bufLengthID);
 761 
 762     if (packetBufferLen > MAX_BUFFER_LEN) {
 763 
 764         /* When JNI-ifying the JDK's IO routines, we turned
 765          * reads and writes of byte arrays of size greater
 766          * than 2048 bytes into several operations of size 2048.
 767          * This saves a malloc()/memcpy()/free() for big
 768          * buffers.  This is OK for file IO and TCP, but that
 769          * strategy violates the semantics of a datagram protocol.
 770          * (one big send) != (several smaller sends).  So here
 771          * we *must* allocate the buffer.  Note it needn't be bigger
 772          * than 65,536 (0xFFFF) the max size of an IP packet,
 773          * anything bigger is truncated anyway.
 774          *
 775          * We may want to use a smarter allocation scheme at some
 776          * point.
 777          */
 778         if (packetBufferLen > MAX_PACKET_LEN) {
 779             packetBufferLen = MAX_PACKET_LEN;
 780         }
 781         fullPacket = (char *)malloc(packetBufferLen);
 782 
 783         if (!fullPacket) {
 784             JNU_ThrowOutOfMemoryError(env, "Receive buffer native heap allocation failed");
 785             return;
 786         } else {
 787             mallocedPacket = JNI_TRUE;
 788         }
 789     } else {
 790         fullPacket = &(BUF[0]);
 791     }
 792 
 793     do {
 794         retry = JNI_FALSE;
 795 
 796         if (timeout) {
 797             int ret = NET_Timeout(env, fd, timeout, JVM_NanoTime(env, 0));
 798             if (ret <= 0) {
 799                 if (ret == 0) {
 800                     JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException",
 801                                     "Receive timed out");
 802                 } else if (ret == -1) {
 803                     if (errno == ENOMEM) {
 804                         JNU_ThrowOutOfMemoryError(env, "NET_Timeout native heap allocation failed");
 805 #ifdef __linux__
 806                     } else if (errno == EBADF) {
 807                          JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");
 808                     } else {
 809                         JNU_ThrowByNameWithMessageAndLastError
 810                             (env, JNU_JAVANETPKG "SocketException", "Receive failed");
 811 #else
 812                     } else {
 813                         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");
 814 #endif
 815                     }
 816                 }
 817 
 818                 if (mallocedPacket) {
 819                     free(fullPacket);
 820                 }
 821 
 822                 return;
 823             }
 824         }
 825 
 826         n = NET_RecvFrom(fd, fullPacket, packetBufferLen, 0,
 827                          &rmtaddr.sa, &slen);
 828         /* truncate the data if the packet's length is too small */
 829         if (n > packetBufferLen) {
 830             n = packetBufferLen;
 831         }
 832         if (n == -1) {
 833             (*env)->SetIntField(env, packet, dp_offsetID, 0);
 834             (*env)->SetIntField(env, packet, dp_lengthID, 0);
 835             if (errno == ECONNREFUSED) {
 836                 JNU_ThrowByName(env, JNU_JAVANETPKG "PortUnreachableException",
 837                                 "ICMP Port Unreachable");
 838             } else {
 839                 if (errno == EBADF) {
 840                      JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");
 841                  } else {
 842                      JNU_ThrowByNameWithMessageAndLastError
 843                          (env, JNU_JAVANETPKG "SocketException", "Receive failed");
 844                  }
 845             }
 846         } else {
 847             int port;
 848             jobject packetAddress;
 849 
 850             /*
 851              * success - fill in received address...
 852              *
 853              * REMIND: Fill in an int on the packet, and create inetadd
 854              * object in Java, as a performance improvement. Also
 855              * construct the inetadd object lazily.
 856              */
 857 
 858             /*
 859              * Check if there is an InetAddress already associated with this
 860              * packet. If so we check if it is the same source address. We
 861              * can't update any existing InetAddress because it is immutable
 862              */
 863             packetAddress = (*env)->GetObjectField(env, packet, dp_addressID);
 864             if (packetAddress != NULL) {
 865                 if (!NET_SockaddrEqualsInetAddress(env, &rmtaddr,
 866                                                    packetAddress)) {
 867                     /* force a new InetAddress to be created */
 868                     packetAddress = NULL;
 869                 }
 870             }
 871             if (packetAddress == NULL) {
 872                 packetAddress = NET_SockaddrToInetAddress(env, &rmtaddr, &port);
 873                 /* stuff the new Inetaddress in the packet */
 874                 (*env)->SetObjectField(env, packet, dp_addressID, packetAddress);
 875             } else {
 876                 /* only get the new port number */
 877                 port = NET_GetPortFromSockaddr(&rmtaddr);
 878             }
 879             /* and fill in the data, remote address/port and such */
 880             (*env)->SetByteArrayRegion(env, packetBuffer, packetBufferOffset, n,
 881                                        (jbyte *)fullPacket);
 882             (*env)->SetIntField(env, packet, dp_portID, port);
 883             (*env)->SetIntField(env, packet, dp_lengthID, n);
 884         }
 885 
 886     } while (retry);
 887 
 888     if (mallocedPacket) {
 889         free(fullPacket);
 890     }
 891 }
 892 
 893 /*
 894  * Class:     java_net_PlainDatagramSocketImpl
 895  * Method:    datagramSocketCreate
 896  * Signature: ()V
 897  */
 898 JNIEXPORT void JNICALL
 899 Java_java_net_PlainDatagramSocketImpl_datagramSocketCreate(JNIEnv *env,
 900                                                            jobject this) {
 901     jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
 902     int arg, fd, t = 1;
 903     char tmpbuf[1024];
 904     int domain = ipv6_available() ? AF_INET6 : AF_INET;
 905 
 906     if (IS_NULL(fdObj)) {
 907         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
 908                         "Socket closed");
 909         return;
 910     }
 911 
 912     if ((fd = socket(domain, SOCK_DGRAM, 0)) == -1) {
 913         JNU_ThrowByNameWithMessageAndLastError
 914             (env, JNU_JAVANETPKG "SocketException", "Error creating socket");
 915         return;
 916     }
 917 
 918     /*
 919      * If IPv4 is available, disable IPV6_V6ONLY to ensure dual-socket support.
 920      */
 921     if (domain == AF_INET6 && ipv4_available()) {
 922         arg = 0;
 923         if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&arg,
 924                        sizeof(int)) < 0) {
 925             NET_ThrowNew(env, errno, "cannot set IPPROTO_IPV6");
 926             close(fd);
 927             return;
 928         }
 929     }
 930 
 931 #ifdef __APPLE__
 932     arg = 65507;
 933     if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF,
 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     if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF,
 941                    (char *)&arg, sizeof(arg)) < 0) {
 942         getErrorString(errno, tmpbuf, sizeof(tmpbuf));
 943         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", tmpbuf);
 944         close(fd);
 945         return;
 946     }
 947 #endif /* __APPLE__ */
 948 
 949     if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (char*) &t, sizeof (int)) < 0) {
 950         getErrorString(errno, tmpbuf, sizeof(tmpbuf));
 951         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", tmpbuf);
 952         close(fd);
 953         return;
 954     }
 955 
 956 #if defined(__linux__)
 957      arg = 0;
 958      int level = (domain == AF_INET6) ? IPPROTO_IPV6 : IPPROTO_IP;
 959      if ((setsockopt(fd, level, IP_MULTICAST_ALL, (char*)&arg, sizeof(arg)) < 0) &&
 960            (errno != ENOPROTOOPT))
 961     {
 962         getErrorString(errno, tmpbuf, sizeof(tmpbuf));
 963         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", tmpbuf);
 964          close(fd);
 965          return;
 966      }
 967 #endif
 968 
 969 #if defined (__linux__)
 970     /*
 971      * On Linux for IPv6 sockets we must set the hop limit
 972      * to 1 to be compatible with default TTL of 1 for IPv4 sockets.
 973      */
 974     if (domain == AF_INET6) {
 975         int ttl = 1;
 976         if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (char *) &ttl,
 977                 sizeof (ttl)) < 0) {
 978             getErrorString(errno, tmpbuf, sizeof(tmpbuf));
 979             JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", tmpbuf);
 980             close(fd);
 981             return;
 982         }
 983     }
 984 #endif /* __linux__ */
 985 
 986     (*env)->SetIntField(env, fdObj, IO_fd_fdID, fd);
 987 }
 988 
 989 /*
 990  * Class:     java_net_PlainDatagramSocketImpl
 991  * Method:    datagramSocketClose
 992  * Signature: ()V
 993  */
 994 JNIEXPORT void JNICALL
 995 Java_java_net_PlainDatagramSocketImpl_datagramSocketClose(JNIEnv *env,
 996                                                           jobject this) {
 997     /*
 998      * REMIND: PUT A LOCK AROUND THIS CODE
 999      */
1000     jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
1001     int fd;
1002 
1003     if (IS_NULL(fdObj)) {
1004         return;
1005     }
1006     fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
1007     if (fd == -1) {
1008         return;
1009     }
1010     (*env)->SetIntField(env, fdObj, IO_fd_fdID, -1);
1011     NET_SocketClose(fd);
1012 }
1013 
1014 
1015 /*
1016  * Set outgoing multicast interface designated by a NetworkInterface.
1017  * Throw exception if failed.
1018  */
1019 static void mcast_set_if_by_if_v4(JNIEnv *env, jobject this, int fd, jobject value) {
1020     static jfieldID ni_addrsID;
1021     struct in_addr in;
1022     jobjectArray addrArray;
1023     jsize len;
1024     jint family;
1025     jobject addr;
1026     int i;
1027 
1028     if (ni_addrsID == NULL ) {
1029         jclass c = (*env)->FindClass(env, "java/net/NetworkInterface");
1030         CHECK_NULL(c);
1031         ni_addrsID = (*env)->GetFieldID(env, c, "addrs",
1032                                         "[Ljava/net/InetAddress;");
1033         CHECK_NULL(ni_addrsID);
1034     }
1035 
1036     addrArray = (*env)->GetObjectField(env, value, ni_addrsID);
1037     len = (*env)->GetArrayLength(env, addrArray);
1038 
1039     /*
1040      * Check that there is at least one address bound to this
1041      * interface.
1042      */
1043     if (len < 1) {
1044         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
1045             "bad argument for IP_MULTICAST_IF2: No IP addresses bound to interface");
1046         return;
1047     }
1048 
1049     /*
1050      * We need an ipv4 address here
1051      */
1052     in.s_addr = 0;
1053     for (i = 0; i < len; i++) {
1054         addr = (*env)->GetObjectArrayElement(env, addrArray, i);
1055         family = getInetAddress_family(env, addr);
1056         JNU_CHECK_EXCEPTION(env);
1057         if (family == java_net_InetAddress_IPv4) {
1058             in.s_addr = htonl(getInetAddress_addr(env, addr));
1059             JNU_CHECK_EXCEPTION(env);
1060             break;
1061         }
1062     }
1063 
1064     if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF,
1065                    (const char *)&in, sizeof(in)) < 0) {
1066         JNU_ThrowByNameWithMessageAndLastError
1067             (env, JNU_JAVANETPKG "SocketException", "Error setting socket option");
1068     }
1069 }
1070 
1071 /*
1072  * Set outgoing multicast interface designated by a NetworkInterface.
1073  * Throw exception if failed.
1074  */
1075 static void mcast_set_if_by_if_v6(JNIEnv *env, jobject this, int fd, jobject value) {
1076     static jfieldID ni_indexID;
1077     int index;
1078 
1079     if (ni_indexID == NULL) {
1080         jclass c = (*env)->FindClass(env, "java/net/NetworkInterface");
1081         CHECK_NULL(c);
1082         ni_indexID = (*env)->GetFieldID(env, c, "index", "I");
1083         CHECK_NULL(ni_indexID);
1084     }
1085     index = (*env)->GetIntField(env, value, ni_indexID);
1086 
1087     if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF,
1088                    (const char*)&index, sizeof(index)) < 0) {
1089         if ((errno == EINVAL || errno == EADDRNOTAVAIL) && index > 0) {
1090             JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
1091                 "IPV6_MULTICAST_IF failed (interface has IPv4 "
1092                 "address only?)");
1093         } else {
1094             JNU_ThrowByNameWithMessageAndLastError
1095                 (env, JNU_JAVANETPKG "SocketException", "Error setting socket option");
1096         }
1097         return;
1098     }
1099 }
1100 
1101 /*
1102  * Set outgoing multicast interface designated by an InetAddress.
1103  * Throw exception if failed.
1104  */
1105 static void mcast_set_if_by_addr_v4(JNIEnv *env, jobject this, int fd, jobject value) {
1106     struct in_addr in;
1107 
1108     in.s_addr = htonl( getInetAddress_addr(env, value) );
1109     JNU_CHECK_EXCEPTION(env);
1110     if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF,
1111                    (const char*)&in, sizeof(in)) < 0) {
1112         JNU_ThrowByNameWithMessageAndLastError
1113             (env, JNU_JAVANETPKG "SocketException", "Error setting socket option");
1114     }
1115 }
1116 
1117 /*
1118  * Set outgoing multicast interface designated by an InetAddress.
1119  * Throw exception if failed.
1120  */
1121 static void mcast_set_if_by_addr_v6(JNIEnv *env, jobject this, int fd, jobject value) {
1122     static jclass ni_class;
1123     if (ni_class == NULL) {
1124         jclass c = (*env)->FindClass(env, "java/net/NetworkInterface");
1125         CHECK_NULL(c);
1126         ni_class = (*env)->NewGlobalRef(env, c);
1127         CHECK_NULL(ni_class);
1128     }
1129 
1130     value = Java_java_net_NetworkInterface_getByInetAddress0(env, ni_class, value);
1131     if (value == NULL) {
1132         if (!(*env)->ExceptionOccurred(env)) {
1133             JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
1134                  "bad argument for IP_MULTICAST_IF"
1135                  ": address not bound to any interface");
1136         }
1137         return;
1138     }
1139 
1140     mcast_set_if_by_if_v6(env, this, fd, value);
1141 }
1142 
1143 /*
1144  * Sets the multicast interface.
1145  *
1146  * SocketOptions.IP_MULTICAST_IF :-
1147  *      value is a InetAddress
1148  *      IPv4:   set outgoing multicast interface using
1149  *              IPPROTO_IP/IP_MULTICAST_IF
1150  *      IPv6:   Get the index of the interface to which the
1151  *              InetAddress is bound
1152  *              Set outgoing multicast interface using
1153  *              IPPROTO_IPV6/IPV6_MULTICAST_IF
1154  *
1155  * SockOptions.IF_MULTICAST_IF2 :-
1156  *      value is a NetworkInterface
1157  *      IPv4:   Obtain IP address bound to network interface
1158  *              (NetworkInterface.addres[0])
1159  *              set outgoing multicast interface using
1160  *              IPPROTO_IP/IP_MULTICAST_IF
1161  *      IPv6:   Obtain NetworkInterface.index
1162  *              Set outgoing multicast interface using
1163  *              IPPROTO_IPV6/IPV6_MULTICAST_IF
1164  *
1165  */
1166 static void setMulticastInterface(JNIEnv *env, jobject this, int fd,
1167                                   jint opt, jobject value)
1168 {
1169     if (opt == java_net_SocketOptions_IP_MULTICAST_IF) {
1170         /*
1171          * value is an InetAddress.
1172          */
1173 #ifdef __linux__
1174         mcast_set_if_by_addr_v4(env, this, fd, value);
1175         if (ipv6_available()) {
1176             if ((*env)->ExceptionCheck(env)){
1177                 (*env)->ExceptionClear(env);
1178             }
1179             mcast_set_if_by_addr_v6(env, this, fd, value);
1180         }
1181 #else  /* __linux__ not defined */
1182         if (ipv6_available()) {
1183             mcast_set_if_by_addr_v6(env, this, fd, value);
1184         } else {
1185             mcast_set_if_by_addr_v4(env, this, fd, value);
1186         }
1187 #endif  /* __linux__ */
1188     }
1189 
1190     if (opt == java_net_SocketOptions_IP_MULTICAST_IF2) {
1191         /*
1192          * value is a NetworkInterface.
1193          */
1194 #ifdef __linux__
1195         mcast_set_if_by_if_v4(env, this, fd, value);
1196         if (ipv6_available()) {
1197             if ((*env)->ExceptionCheck(env)){
1198                 (*env)->ExceptionClear(env);
1199             }
1200             mcast_set_if_by_if_v6(env, this, fd, value);
1201         }
1202 #else  /* __linux__ not defined */
1203         if (ipv6_available()) {
1204             mcast_set_if_by_if_v6(env, this, fd, value);
1205         } else {
1206             mcast_set_if_by_if_v4(env, this, fd, value);
1207         }
1208 #endif  /* __linux__ */
1209     }
1210 }
1211 
1212 /*
1213  * Enable/disable local loopback of multicast datagrams.
1214  */
1215 static void mcast_set_loop_v4(JNIEnv *env, jobject this, int fd, jobject value) {
1216     jclass cls;
1217     jfieldID fid;
1218     jboolean on;
1219     char loopback;
1220 
1221     cls = (*env)->FindClass(env, "java/lang/Boolean");
1222     CHECK_NULL(cls);
1223     fid =  (*env)->GetFieldID(env, cls, "value", "Z");
1224     CHECK_NULL(fid);
1225 
1226     on = (*env)->GetBooleanField(env, value, fid);
1227     loopback = (!on ? 1 : 0);
1228 
1229     if (NET_SetSockOpt(fd, IPPROTO_IP, IP_MULTICAST_LOOP,
1230                        (const void *)&loopback, sizeof(char)) < 0) {
1231         JNU_ThrowByNameWithMessageAndLastError
1232             (env, JNU_JAVANETPKG "SocketException", "Error setting socket option");
1233         return;
1234     }
1235 }
1236 
1237 /*
1238  * Enable/disable local loopback of multicast datagrams.
1239  */
1240 static void mcast_set_loop_v6(JNIEnv *env, jobject this, int fd, jobject value) {
1241     jclass cls;
1242     jfieldID fid;
1243     jboolean on;
1244     int loopback;
1245 
1246     cls = (*env)->FindClass(env, "java/lang/Boolean");
1247     CHECK_NULL(cls);
1248     fid =  (*env)->GetFieldID(env, cls, "value", "Z");
1249     CHECK_NULL(fid);
1250 
1251     on = (*env)->GetBooleanField(env, value, fid);
1252     loopback = (!on ? 1 : 0);
1253 
1254     if (NET_SetSockOpt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
1255                        (const void *)&loopback, sizeof(int)) < 0) {
1256         JNU_ThrowByNameWithMessageAndLastError
1257             (env, JNU_JAVANETPKG "SocketException", "Error setting socket option");
1258         return;
1259     }
1260 
1261 }
1262 
1263 /*
1264  * Sets the multicast loopback mode.
1265  */
1266 static void setMulticastLoopbackMode(JNIEnv *env, jobject this, int fd,
1267                                      jint opt, jobject value) {
1268 #ifdef __linux__
1269     mcast_set_loop_v4(env, this, fd, value);
1270     if (ipv6_available()) {
1271         if ((*env)->ExceptionCheck(env)){
1272             (*env)->ExceptionClear(env);
1273         }
1274         mcast_set_loop_v6(env, this, fd, value);
1275     }
1276 #else  /* __linux__ not defined */
1277     if (ipv6_available()) {
1278         mcast_set_loop_v6(env, this, fd, value);
1279     } else {
1280         mcast_set_loop_v4(env, this, fd, value);
1281     }
1282 #endif  /* __linux__ */
1283 }
1284 
1285 /*
1286  * Class:     java_net_PlainDatagramSocketImpl
1287  * Method:    socketSetOption0
1288  * Signature: (ILjava/lang/Object;)V
1289  */
1290 JNIEXPORT void JNICALL
1291 Java_java_net_PlainDatagramSocketImpl_socketSetOption0
1292   (JNIEnv *env, jobject this, jint opt, jobject value)
1293 {
1294     int fd;
1295     int level, optname, optlen;
1296     int optval;
1297     optlen = sizeof(int);
1298 
1299     /*
1300      * Check that socket hasn't been closed
1301      */
1302     fd = getFD(env, this);
1303     if (fd < 0) {
1304         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
1305                         "Socket closed");
1306         return;
1307     }
1308 
1309     /*
1310      * Check argument has been provided
1311      */
1312     if (IS_NULL(value)) {
1313         JNU_ThrowNullPointerException(env, "value argument");
1314         return;
1315     }
1316 
1317     /*
1318      * Setting the multicast interface handled separately
1319      */
1320     if (opt == java_net_SocketOptions_IP_MULTICAST_IF ||
1321         opt == java_net_SocketOptions_IP_MULTICAST_IF2) {
1322 
1323         setMulticastInterface(env, this, fd, opt, value);
1324         return;
1325     }
1326 
1327     /*
1328      * Setting the multicast loopback mode handled separately
1329      */
1330     if (opt == java_net_SocketOptions_IP_MULTICAST_LOOP) {
1331         setMulticastLoopbackMode(env, this, fd, opt, value);
1332         return;
1333     }
1334 
1335     /*
1336      * Map the Java level socket option to the platform specific
1337      * level and option name.
1338      */
1339     if (NET_MapSocketOption(opt, &level, &optname)) {
1340         JNU_ThrowByName(env, "java/net/SocketException", "Invalid option");
1341         return;
1342     }
1343 
1344     switch (opt) {
1345         case java_net_SocketOptions_SO_SNDBUF :
1346         case java_net_SocketOptions_SO_RCVBUF :
1347         case java_net_SocketOptions_IP_TOS :
1348             {
1349                 jclass cls;
1350                 jfieldID fid;
1351 
1352                 cls = (*env)->FindClass(env, "java/lang/Integer");
1353                 CHECK_NULL(cls);
1354                 fid =  (*env)->GetFieldID(env, cls, "value", "I");
1355                 CHECK_NULL(fid);
1356 
1357                 optval = (*env)->GetIntField(env, value, fid);
1358                 break;
1359             }
1360 
1361         case java_net_SocketOptions_SO_REUSEADDR:
1362         case java_net_SocketOptions_SO_REUSEPORT:
1363         case java_net_SocketOptions_SO_BROADCAST:
1364             {
1365                 jclass cls;
1366                 jfieldID fid;
1367                 jboolean on;
1368 
1369                 cls = (*env)->FindClass(env, "java/lang/Boolean");
1370                 CHECK_NULL(cls);
1371                 fid =  (*env)->GetFieldID(env, cls, "value", "Z");
1372                 CHECK_NULL(fid);
1373 
1374                 on = (*env)->GetBooleanField(env, value, fid);
1375 
1376                 /* SO_REUSEADDR or SO_BROADCAST */
1377                 optval = (on ? 1 : 0);
1378 
1379                 break;
1380             }
1381 
1382         default :
1383             JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
1384                 "Socket option not supported by PlainDatagramSocketImp");
1385             return;
1386 
1387     }
1388 
1389     if (NET_SetSockOpt(fd, level, optname, (const void *)&optval, optlen) < 0) {
1390         JNU_ThrowByNameWithMessageAndLastError
1391             (env, JNU_JAVANETPKG "SocketException", "Error setting socket option");
1392         return;
1393     }
1394 }
1395 
1396 
1397 /*
1398  * Return the multicast interface:
1399  *
1400  * SocketOptions.IP_MULTICAST_IF
1401  *      IPv4:   Query IPPROTO_IP/IP_MULTICAST_IF
1402  *              Create InetAddress
1403  *              IP_MULTICAST_IF returns struct ip_mreqn on 2.2
1404  *              kernel but struct in_addr on 2.4 kernel
1405  *      IPv6:   Query IPPROTO_IPV6 / IPV6_MULTICAST_IF
1406  *              If index == 0 return InetAddress representing
1407  *              anyLocalAddress.
1408  *              If index > 0 query NetworkInterface by index
1409  *              and returns addrs[0]
1410  *
1411  * SocketOptions.IP_MULTICAST_IF2
1412  *      IPv4:   Query IPPROTO_IP/IP_MULTICAST_IF
1413  *              Query NetworkInterface by IP address and
1414  *              return the NetworkInterface that the address
1415  *              is bound too.
1416  *      IPv6:   Query IPPROTO_IPV6 / IPV6_MULTICAST_IF
1417  *              (except Linux .2 kernel)
1418  *              Query NetworkInterface by index and
1419  *              return NetworkInterface.
1420  */
1421 jobject getMulticastInterface(JNIEnv *env, jobject this, int fd, jint opt) {
1422     jboolean isIPV4 = JNI_TRUE;
1423 
1424     if (ipv6_available()) {
1425         isIPV4 = JNI_FALSE;
1426     }
1427 
1428     /*
1429      * IPv4 implementation
1430      */
1431     if (isIPV4) {
1432         static jclass inet4_class;
1433         static jmethodID inet4_ctrID;
1434 
1435         static jclass ni_class;
1436         static jmethodID ni_ctrID;
1437         static jfieldID ni_indexID;
1438         static jfieldID ni_addrsID;
1439         static jfieldID ni_nameID;
1440 
1441         jobjectArray addrArray;
1442         jobject addr;
1443         jobject ni;
1444         jobject ni_name;
1445 
1446         struct in_addr in;
1447         struct in_addr *inP = &in;
1448         socklen_t len = sizeof(struct in_addr);
1449 
1450         if (getsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF,
1451                        (char *)inP, &len) < 0) {
1452             JNU_ThrowByNameWithMessageAndLastError
1453                 (env, JNU_JAVANETPKG "SocketException", "Error getting socket option");
1454             return NULL;
1455         }
1456 
1457         /*
1458          * Construct and populate an Inet4Address
1459          */
1460         if (inet4_class == NULL) {
1461             jclass c = (*env)->FindClass(env, "java/net/Inet4Address");
1462             CHECK_NULL_RETURN(c, NULL);
1463             inet4_ctrID = (*env)->GetMethodID(env, c, "<init>", "()V");
1464             CHECK_NULL_RETURN(inet4_ctrID, NULL);
1465             inet4_class = (*env)->NewGlobalRef(env, c);
1466             CHECK_NULL_RETURN(inet4_class, NULL);
1467         }
1468         addr = (*env)->NewObject(env, inet4_class, inet4_ctrID, 0);
1469         CHECK_NULL_RETURN(addr, NULL);
1470 
1471         setInetAddress_addr(env, addr, ntohl(in.s_addr));
1472         JNU_CHECK_EXCEPTION_RETURN(env, NULL);
1473 
1474         /*
1475          * For IP_MULTICAST_IF return InetAddress
1476          */
1477         if (opt == java_net_SocketOptions_IP_MULTICAST_IF) {
1478             return addr;
1479         }
1480 
1481         /*
1482          * For IP_MULTICAST_IF2 we get the NetworkInterface for
1483          * this address and return it
1484          */
1485         if (ni_class == NULL) {
1486             jclass c = (*env)->FindClass(env, "java/net/NetworkInterface");
1487             CHECK_NULL_RETURN(c, NULL);
1488             ni_ctrID = (*env)->GetMethodID(env, c, "<init>", "()V");
1489             CHECK_NULL_RETURN(ni_ctrID, NULL);
1490             ni_indexID = (*env)->GetFieldID(env, c, "index", "I");
1491             CHECK_NULL_RETURN(ni_indexID, NULL);
1492             ni_addrsID = (*env)->GetFieldID(env, c, "addrs",
1493                                             "[Ljava/net/InetAddress;");
1494             CHECK_NULL_RETURN(ni_addrsID, NULL);
1495             ni_nameID = (*env)->GetFieldID(env, c,"name", "Ljava/lang/String;");
1496             CHECK_NULL_RETURN(ni_nameID, NULL);
1497             ni_class = (*env)->NewGlobalRef(env, c);
1498             CHECK_NULL_RETURN(ni_class, NULL);
1499         }
1500         ni = Java_java_net_NetworkInterface_getByInetAddress0(env, ni_class, addr);
1501         JNU_CHECK_EXCEPTION_RETURN(env, NULL);
1502         if (ni) {
1503             return ni;
1504         }
1505 
1506         /*
1507          * The address doesn't appear to be bound at any known
1508          * NetworkInterface. Therefore we construct a NetworkInterface
1509          * with this address.
1510          */
1511         ni = (*env)->NewObject(env, ni_class, ni_ctrID, 0);
1512         CHECK_NULL_RETURN(ni, NULL);
1513 
1514         (*env)->SetIntField(env, ni, ni_indexID, -1);
1515         addrArray = (*env)->NewObjectArray(env, 1, inet4_class, NULL);
1516         CHECK_NULL_RETURN(addrArray, NULL);
1517         (*env)->SetObjectArrayElement(env, addrArray, 0, addr);
1518         (*env)->SetObjectField(env, ni, ni_addrsID, addrArray);
1519         ni_name = (*env)->NewStringUTF(env, "");
1520         if (ni_name != NULL) {
1521             (*env)->SetObjectField(env, ni, ni_nameID, ni_name);
1522         }
1523         return ni;
1524     }
1525 
1526 
1527     /*
1528      * IPv6 implementation
1529      */
1530     if ((opt == java_net_SocketOptions_IP_MULTICAST_IF) ||
1531         (opt == java_net_SocketOptions_IP_MULTICAST_IF2)) {
1532 
1533         static jclass ni_class;
1534         static jmethodID ni_ctrID;
1535         static jfieldID ni_indexID;
1536         static jfieldID ni_addrsID;
1537         static jclass ia_class;
1538         static jfieldID ni_nameID;
1539         static jmethodID ia_anyLocalAddressID;
1540 
1541         int index = 0;
1542         socklen_t len = sizeof(index);
1543 
1544         jobjectArray addrArray;
1545         jobject addr;
1546         jobject ni;
1547         jobject ni_name;
1548 
1549         if (getsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF,
1550                        (char*)&index, &len) < 0) {
1551             JNU_ThrowByNameWithMessageAndLastError
1552                 (env, JNU_JAVANETPKG "SocketException", "Error getting socket option");
1553             return NULL;
1554         }
1555 
1556         if (ni_class == NULL) {
1557             jclass c = (*env)->FindClass(env, "java/net/NetworkInterface");
1558             CHECK_NULL_RETURN(c, NULL);
1559             ni_ctrID = (*env)->GetMethodID(env, c, "<init>", "()V");
1560             CHECK_NULL_RETURN(ni_ctrID, NULL);
1561             ni_indexID = (*env)->GetFieldID(env, c, "index", "I");
1562             CHECK_NULL_RETURN(ni_indexID, NULL);
1563             ni_addrsID = (*env)->GetFieldID(env, c, "addrs",
1564                                             "[Ljava/net/InetAddress;");
1565             CHECK_NULL_RETURN(ni_addrsID, NULL);
1566 
1567             ia_class = (*env)->FindClass(env, "java/net/InetAddress");
1568             CHECK_NULL_RETURN(ia_class, NULL);
1569             ia_class = (*env)->NewGlobalRef(env, ia_class);
1570             CHECK_NULL_RETURN(ia_class, NULL);
1571             ia_anyLocalAddressID = (*env)->GetStaticMethodID(env,
1572                                                              ia_class,
1573                                                              "anyLocalAddress",
1574                                                              "()Ljava/net/InetAddress;");
1575             CHECK_NULL_RETURN(ia_anyLocalAddressID, NULL);
1576             ni_nameID = (*env)->GetFieldID(env, c,"name", "Ljava/lang/String;");
1577             CHECK_NULL_RETURN(ni_nameID, NULL);
1578             ni_class = (*env)->NewGlobalRef(env, c);
1579             CHECK_NULL_RETURN(ni_class, NULL);
1580         }
1581 
1582         /*
1583          * If multicast to a specific interface then return the
1584          * interface (for IF2) or the any address on that interface
1585          * (for IF).
1586          */
1587         if (index > 0) {
1588             ni = Java_java_net_NetworkInterface_getByIndex0(env, ni_class,
1589                                                                    index);
1590             if (ni == NULL) {
1591                 char errmsg[255];
1592                 sprintf(errmsg,
1593                         "IPV6_MULTICAST_IF returned index to unrecognized interface: %d",
1594                         index);
1595                 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", errmsg);
1596                 return NULL;
1597             }
1598 
1599             /*
1600              * For IP_MULTICAST_IF2 return the NetworkInterface
1601              */
1602             if (opt == java_net_SocketOptions_IP_MULTICAST_IF2) {
1603                 return ni;
1604             }
1605 
1606             /*
1607              * For IP_MULTICAST_IF return addrs[0]
1608              */
1609             addrArray = (*env)->GetObjectField(env, ni, ni_addrsID);
1610             if ((*env)->GetArrayLength(env, addrArray) < 1) {
1611                 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
1612                     "IPV6_MULTICAST_IF returned interface without IP bindings");
1613                 return NULL;
1614             }
1615 
1616             addr = (*env)->GetObjectArrayElement(env, addrArray, 0);
1617             return addr;
1618         }
1619 
1620         /*
1621          * Multicast to any address - return anyLocalAddress
1622          * or a NetworkInterface with addrs[0] set to anyLocalAddress
1623          */
1624 
1625         addr = (*env)->CallStaticObjectMethod(env, ia_class, ia_anyLocalAddressID,
1626                                               NULL);
1627         if (opt == java_net_SocketOptions_IP_MULTICAST_IF) {
1628             return addr;
1629         }
1630 
1631         ni = (*env)->NewObject(env, ni_class, ni_ctrID, 0);
1632         CHECK_NULL_RETURN(ni, NULL);
1633         (*env)->SetIntField(env, ni, ni_indexID, -1);
1634         addrArray = (*env)->NewObjectArray(env, 1, ia_class, NULL);
1635         CHECK_NULL_RETURN(addrArray, NULL);
1636         (*env)->SetObjectArrayElement(env, addrArray, 0, addr);
1637         (*env)->SetObjectField(env, ni, ni_addrsID, addrArray);
1638         ni_name = (*env)->NewStringUTF(env, "");
1639         if (ni_name != NULL) {
1640             (*env)->SetObjectField(env, ni, ni_nameID, ni_name);
1641         }
1642         return ni;
1643     }
1644     return NULL;
1645 }
1646 
1647 
1648 
1649 /*
1650  * Returns relevant info as a jint.
1651  *
1652  * Class:     java_net_PlainDatagramSocketImpl
1653  * Method:    socketGetOption
1654  * Signature: (I)Ljava/lang/Object;
1655  */
1656 JNIEXPORT jobject JNICALL
1657 Java_java_net_PlainDatagramSocketImpl_socketGetOption
1658   (JNIEnv *env, jobject this, jint opt)
1659 {
1660     int fd;
1661     int level, optname, optlen;
1662     union {
1663         int i;
1664         char c;
1665     } optval;
1666 
1667     fd = getFD(env, this);
1668     if (fd < 0) {
1669         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
1670                         "socket closed");
1671         return NULL;
1672     }
1673 
1674     /*
1675      * Handle IP_MULTICAST_IF separately
1676      */
1677     if (opt == java_net_SocketOptions_IP_MULTICAST_IF ||
1678         opt == java_net_SocketOptions_IP_MULTICAST_IF2) {
1679         return getMulticastInterface(env, this, fd, opt);
1680 
1681     }
1682 
1683     /*
1684      * SO_BINDADDR implemented using getsockname
1685      */
1686     if (opt == java_net_SocketOptions_SO_BINDADDR) {
1687         /* find out local IP address */
1688         SOCKETADDRESS sa;
1689         socklen_t len = sizeof(SOCKETADDRESS);
1690         int port;
1691         jobject iaObj;
1692 
1693         if (getsockname(fd, &sa.sa, &len) == -1) {
1694             JNU_ThrowByNameWithMessageAndLastError
1695                 (env, JNU_JAVANETPKG "SocketException", "Error getting socket name");
1696             return NULL;
1697         }
1698         iaObj = NET_SockaddrToInetAddress(env, &sa, &port);
1699 
1700         return iaObj;
1701     }
1702 
1703     /*
1704      * Map the Java level socket option to the platform specific
1705      * level and option name.
1706      */
1707     if (NET_MapSocketOption(opt, &level, &optname)) {
1708         JNU_ThrowByName(env, "java/net/SocketException", "Invalid option");
1709         return NULL;
1710     }
1711 
1712     if (opt == java_net_SocketOptions_IP_MULTICAST_LOOP &&
1713         level == IPPROTO_IP) {
1714         optlen = sizeof(optval.c);
1715     } else {
1716         optlen = sizeof(optval.i);
1717     }
1718 
1719     if (NET_GetSockOpt(fd, level, optname, (void *)&optval, &optlen) < 0) {
1720         JNU_ThrowByNameWithMessageAndLastError
1721             (env, JNU_JAVANETPKG "SocketException", "Error getting socket option");
1722         return NULL;
1723     }
1724 
1725     switch (opt) {
1726         case java_net_SocketOptions_IP_MULTICAST_LOOP:
1727             /* getLoopbackMode() returns true if IP_MULTICAST_LOOP disabled */
1728             if (level == IPPROTO_IP) {
1729                 return createBoolean(env, (int)!optval.c);
1730             } else {
1731                 return createBoolean(env, !optval.i);
1732             }
1733 
1734         case java_net_SocketOptions_SO_BROADCAST:
1735         case java_net_SocketOptions_SO_REUSEADDR:
1736             return createBoolean(env, optval.i);
1737 
1738         case java_net_SocketOptions_SO_REUSEPORT:
1739             return createBoolean(env, optval.i);
1740 
1741         case java_net_SocketOptions_SO_SNDBUF:
1742         case java_net_SocketOptions_SO_RCVBUF:
1743         case java_net_SocketOptions_IP_TOS:
1744             return createInteger(env, optval.i);
1745 
1746     }
1747 
1748     /* should never reach here */
1749     return NULL;
1750 }
1751 
1752 /*
1753  * Multicast-related calls
1754  */
1755 
1756 JNIEXPORT void JNICALL
1757 Java_java_net_PlainDatagramSocketImpl_setTTL(JNIEnv *env, jobject this,
1758                                              jbyte ttl) {
1759     jint ittl = ttl;
1760     if (ittl < 0) {
1761         ittl += 0x100;
1762     }
1763     Java_java_net_PlainDatagramSocketImpl_setTimeToLive(env, this, ittl);
1764 }
1765 
1766 /*
1767  * Set TTL for a socket. Throw exception if failed.
1768  */
1769 static void setTTL(JNIEnv *env, int fd, jint ttl) {
1770     char ittl = (char)ttl;
1771     if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, (char*)&ittl,
1772                    sizeof(ittl)) < 0) {
1773         JNU_ThrowByNameWithMessageAndLastError
1774             (env, JNU_JAVANETPKG "SocketException", "Error setting socket option");
1775     }
1776 }
1777 
1778 /*
1779  * Set hops limit for a socket. Throw exception if failed.
1780  */
1781 static void setHopLimit(JNIEnv *env, int fd, jint ttl) {
1782     int ittl = (int)ttl;
1783     if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
1784                    (char*)&ittl, sizeof(ittl)) < 0) {
1785         JNU_ThrowByNameWithMessageAndLastError
1786             (env, JNU_JAVANETPKG "SocketException", "Error setting socket option");
1787     }
1788 }
1789 
1790 /*
1791  * Class:     java_net_PlainDatagramSocketImpl
1792  * Method:    setTTL
1793  * Signature: (B)V
1794  */
1795 JNIEXPORT void JNICALL
1796 Java_java_net_PlainDatagramSocketImpl_setTimeToLive(JNIEnv *env, jobject this,
1797                                                     jint ttl) {
1798 
1799     jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
1800     int fd;
1801     /* it is important to cast this to a char, otherwise setsockopt gets confused */
1802 
1803     if (IS_NULL(fdObj)) {
1804         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
1805                         "Socket closed");
1806         return;
1807     } else {
1808         fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
1809     }
1810     /* setsockopt to be correct TTL */
1811 #ifdef __linux__
1812     setTTL(env, fd, ttl);
1813     JNU_CHECK_EXCEPTION(env);
1814     if (ipv6_available()) {
1815         setHopLimit(env, fd, ttl);
1816     }
1817 #else  /*  __linux__ not defined */
1818     if (ipv6_available()) {
1819         setHopLimit(env, fd, ttl);
1820     } else {
1821         setTTL(env, fd, ttl);
1822     }
1823 #endif  /* __linux__ */
1824 }
1825 
1826 /*
1827  * Class:     java_net_PlainDatagramSocketImpl
1828  * Method:    getTTL
1829  * Signature: ()B
1830  */
1831 JNIEXPORT jbyte JNICALL
1832 Java_java_net_PlainDatagramSocketImpl_getTTL(JNIEnv *env, jobject this) {
1833     return (jbyte)Java_java_net_PlainDatagramSocketImpl_getTimeToLive(env, this);
1834 }
1835 
1836 
1837 /*
1838  * Class:     java_net_PlainDatagramSocketImpl
1839  * Method:    getTTL
1840  * Signature: ()B
1841  */
1842 JNIEXPORT jint JNICALL
1843 Java_java_net_PlainDatagramSocketImpl_getTimeToLive(JNIEnv *env, jobject this) {
1844 
1845     jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
1846     jint fd = -1;
1847 
1848     if (IS_NULL(fdObj)) {
1849         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
1850                         "Socket closed");
1851         return -1;
1852     } else {
1853         fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
1854     }
1855     /* getsockopt of TTL */
1856     if (ipv6_available()) {
1857         int ttl = 0;
1858         socklen_t len = sizeof(ttl);
1859 
1860         if (getsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
1861                        (char*)&ttl, &len) < 0) {
1862             JNU_ThrowByNameWithMessageAndLastError
1863                 (env, JNU_JAVANETPKG "SocketException", "Error getting socket option");
1864             return -1;
1865         }
1866         return (jint)ttl;
1867     } else {
1868         u_char ttl = 0;
1869         socklen_t len = sizeof(ttl);
1870         if (getsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL,
1871                        (char*)&ttl, &len) < 0) {
1872             JNU_ThrowByNameWithMessageAndLastError
1873                 (env, JNU_JAVANETPKG "SocketException", "Error getting socket option");
1874             return -1;
1875         }
1876         return (jint)ttl;
1877     }
1878 }
1879 
1880 
1881 /*
1882  * mcast_join_leave: Join or leave a multicast group.
1883  *
1884  * For IPv4 sockets use IP_ADD_MEMBERSHIP/IP_DROP_MEMBERSHIP socket option
1885  * to join/leave multicast group.
1886  *
1887  * For IPv6 sockets use IPV6_ADD_MEMBERSHIP/IPV6_DROP_MEMBERSHIP socket option
1888  * to join/leave multicast group. If multicast group is an IPv4 address then
1889  * an IPv4-mapped address is used.
1890  *
1891  * On Linux with IPv6 if we wish to join/leave an IPv4 multicast group then
1892  * we must use the IPv4 socket options. This is because the IPv6 socket options
1893  * don't support IPv4-mapped addresses. This is true as per 2.2.19 and 2.4.7
1894  * kernel releases. In the future it's possible that IP_ADD_MEMBERSHIP
1895  * will be updated to return ENOPROTOOPT if uses with an IPv6 socket (Solaris
1896  * already does this). Thus to cater for this we first try with the IPv4
1897  * socket options and if they fail we use the IPv6 socket options. This
1898  * seems a reasonable failsafe solution.
1899  */
1900 static void mcast_join_leave(JNIEnv *env, jobject this,
1901                              jobject iaObj, jobject niObj,
1902                              jboolean join) {
1903 
1904     jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
1905     jint fd;
1906     jint family;
1907     jint ipv6_join_leave;
1908 
1909     if (IS_NULL(fdObj)) {
1910         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
1911                         "Socket closed");
1912         return;
1913     } else {
1914         fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
1915     }
1916     if (IS_NULL(iaObj)) {
1917         JNU_ThrowNullPointerException(env, "iaObj");
1918         return;
1919     }
1920 
1921     /*
1922      * Determine if this is an IPv4 or IPv6 join/leave.
1923      */
1924     ipv6_join_leave = ipv6_available();
1925 
1926 #ifdef __linux__
1927     family = getInetAddress_family(env, iaObj);
1928     JNU_CHECK_EXCEPTION(env);
1929     if (family == java_net_InetAddress_IPv4) {
1930         ipv6_join_leave = JNI_FALSE;
1931     }
1932 #endif
1933 
1934     /*
1935      * For IPv4 join use IP_ADD_MEMBERSHIP/IP_DROP_MEMBERSHIP socket option
1936      *
1937      * On Linux if IPv4 or IPv6 use IP_ADD_MEMBERSHIP/IP_DROP_MEMBERSHIP
1938      */
1939     if (!ipv6_join_leave) {
1940 #ifdef __linux__
1941         struct ip_mreqn mname;
1942 #else
1943         struct ip_mreq mname;
1944 #endif
1945         int mname_len;
1946 
1947         /*
1948          * joinGroup(InetAddress, NetworkInterface) implementation :-
1949          *
1950          * Linux/IPv6:  use ip_mreqn structure populated with multicast
1951          *              address and interface index.
1952          *
1953          * IPv4:        use ip_mreq structure populated with multicast
1954          *              address and first address obtained from
1955          *              NetworkInterface
1956          */
1957         if (niObj != NULL) {
1958 #if defined(__linux__)
1959             if (ipv6_available()) {
1960                 static jfieldID ni_indexID;
1961 
1962                 if (ni_indexID == NULL) {
1963                     jclass c = (*env)->FindClass(env, "java/net/NetworkInterface");
1964                     CHECK_NULL(c);
1965                     ni_indexID = (*env)->GetFieldID(env, c, "index", "I");
1966                     CHECK_NULL(ni_indexID);
1967                 }
1968 
1969                 mname.imr_multiaddr.s_addr = htonl(getInetAddress_addr(env, iaObj));
1970                 JNU_CHECK_EXCEPTION(env);
1971                 mname.imr_address.s_addr = 0;
1972                 mname.imr_ifindex =  (*env)->GetIntField(env, niObj, ni_indexID);
1973                 mname_len = sizeof(struct ip_mreqn);
1974             } else
1975 #endif
1976             {
1977                 jobjectArray addrArray = (*env)->GetObjectField(env, niObj, ni_addrsID);
1978                 jobject addr;
1979 
1980                 if ((*env)->GetArrayLength(env, addrArray) < 1) {
1981                     JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
1982                         "bad argument for IP_ADD_MEMBERSHIP: "
1983                         "No IP addresses bound to interface");
1984                     return;
1985                 }
1986                 addr = (*env)->GetObjectArrayElement(env, addrArray, 0);
1987 
1988                 mname.imr_multiaddr.s_addr = htonl(getInetAddress_addr(env, iaObj));
1989                 JNU_CHECK_EXCEPTION(env);
1990 #ifdef __linux__
1991                 mname.imr_address.s_addr = htonl(getInetAddress_addr(env, addr));
1992                 JNU_CHECK_EXCEPTION(env);
1993                 mname.imr_ifindex = 0;
1994 #else
1995                 mname.imr_interface.s_addr = htonl(getInetAddress_addr(env, addr));
1996                 JNU_CHECK_EXCEPTION(env);
1997 #endif
1998                 mname_len = sizeof(struct ip_mreq);
1999             }
2000         }
2001 
2002 
2003         /*
2004          * joinGroup(InetAddress) implementation :-
2005          *
2006          * Linux/IPv6:  use ip_mreqn structure populated with multicast
2007          *              address and interface index. index obtained
2008          *              from cached value or IPV6_MULTICAST_IF.
2009          *
2010          * IPv4:        use ip_mreq structure populated with multicast
2011          *              address and local address obtained from
2012          *              IP_MULTICAST_IF. On Linux IP_MULTICAST_IF
2013          *              returns different structure depending on
2014          *              kernel.
2015          */
2016 
2017         if (niObj == NULL) {
2018 
2019 #if defined(__linux__)
2020             if (ipv6_available()) {
2021 
2022                 int index;
2023                 socklen_t len = sizeof(index);
2024 
2025                 if (getsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF,
2026                                (char*)&index, &len) < 0) {
2027                     NET_ThrowCurrent(env, "getsockopt IPV6_MULTICAST_IF failed");
2028                     return;
2029                 }
2030 
2031                 mname.imr_multiaddr.s_addr = htonl(getInetAddress_addr(env, iaObj));
2032                 JNU_CHECK_EXCEPTION(env);
2033                 mname.imr_address.s_addr = 0 ;
2034                 mname.imr_ifindex = index;
2035                 mname_len = sizeof(struct ip_mreqn);
2036             } else
2037 #endif
2038             {
2039                 struct in_addr in;
2040                 struct in_addr *inP = &in;
2041                 socklen_t len = sizeof(struct in_addr);
2042 
2043                 if (getsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, (char *)inP, &len) < 0) {
2044                     NET_ThrowCurrent(env, "getsockopt IP_MULTICAST_IF failed");
2045                     return;
2046                 }
2047 
2048 #ifdef __linux__
2049                 mname.imr_address.s_addr = in.s_addr;
2050                 mname.imr_ifindex = 0;
2051 #else
2052                 mname.imr_interface.s_addr = in.s_addr;
2053 #endif
2054                 mname.imr_multiaddr.s_addr = htonl(getInetAddress_addr(env, iaObj));
2055                 JNU_CHECK_EXCEPTION(env);
2056                 mname_len = sizeof(struct ip_mreq);
2057             }
2058         }
2059 
2060 
2061         /*
2062          * Join the multicast group.
2063          */
2064         if (setsockopt(fd, IPPROTO_IP, (join ? IP_ADD_MEMBERSHIP:IP_DROP_MEMBERSHIP),
2065                        (char *) &mname, mname_len) < 0) {
2066 
2067             /*
2068              * If IP_ADD_MEMBERSHIP returns ENOPROTOOPT on Linux and we've got
2069              * IPv6 enabled then it's possible that the kernel has been fixed
2070              * so we switch to IPV6_ADD_MEMBERSHIP socket option.
2071              * As of 2.4.7 kernel IPV6_ADD_MEMBERSHIP can't handle IPv4-mapped
2072              * addresses so we have to use IP_ADD_MEMBERSHIP for IPv4 multicast
2073              * groups. However if the socket is an IPv6 socket then setsockopt
2074              * should return ENOPROTOOPT. We assume this will be fixed in Linux
2075              * at some stage.
2076              */
2077 #if defined(__linux__)
2078             if (errno == ENOPROTOOPT) {
2079                 if (ipv6_available()) {
2080                     ipv6_join_leave = JNI_TRUE;
2081                     errno = 0;
2082                 } else  {
2083                     errno = ENOPROTOOPT;    /* errno can be changed by ipv6_available */
2084                 }
2085             }
2086 #endif
2087             if (errno) {
2088                 if (join) {
2089                     NET_ThrowCurrent(env, "setsockopt IP_ADD_MEMBERSHIP failed");
2090                 } else {
2091                     if (errno == ENOENT)
2092                         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
2093                             "Not a member of the multicast group");
2094                     else
2095                         NET_ThrowCurrent(env, "setsockopt IP_DROP_MEMBERSHIP failed");
2096                 }
2097                 return;
2098             }
2099         }
2100 
2101         /*
2102          * If we haven't switched to IPv6 socket option then we're done.
2103          */
2104         if (!ipv6_join_leave) {
2105             return;
2106         }
2107     }
2108 
2109 
2110     /*
2111      * IPv6 join. If it's an IPv4 multicast group then we use an IPv4-mapped
2112      * address.
2113      */
2114     {
2115         struct ipv6_mreq mname6;
2116         jbyteArray ipaddress;
2117         jbyte caddr[16];
2118         jint family;
2119         jint address;
2120         family = getInetAddress_family(env, iaObj) == java_net_InetAddress_IPv4 ?
2121             AF_INET : AF_INET6;
2122         JNU_CHECK_EXCEPTION(env);
2123         if (family == AF_INET) { /* will convert to IPv4-mapped address */
2124             memset((char *) caddr, 0, 16);
2125             address = getInetAddress_addr(env, iaObj);
2126             JNU_CHECK_EXCEPTION(env);
2127             caddr[10] = 0xff;
2128             caddr[11] = 0xff;
2129 
2130             caddr[12] = ((address >> 24) & 0xff);
2131             caddr[13] = ((address >> 16) & 0xff);
2132             caddr[14] = ((address >> 8) & 0xff);
2133             caddr[15] = (address & 0xff);
2134         } else {
2135             getInet6Address_ipaddress(env, iaObj, (char*)caddr);
2136         }
2137 
2138         memcpy((void *)&(mname6.ipv6mr_multiaddr), caddr, sizeof(struct in6_addr));
2139         if (IS_NULL(niObj)) {
2140             int index;
2141             socklen_t len = sizeof(index);
2142 
2143             if (getsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF,
2144                            (char*)&index, &len) < 0) {
2145                 NET_ThrowCurrent(env, "getsockopt IPV6_MULTICAST_IF failed");
2146                 return;
2147             }
2148 
2149 #ifdef __linux__
2150             /*
2151              * On 2.4.8+ if we join a group with the interface set to 0
2152              * then the kernel records the interface it decides. This causes
2153              * subsequent leave groups to fail as there is no match. Thus we
2154              * pick the interface if there is a matching route.
2155              */
2156             if (index == 0) {
2157                 int rt_index = getDefaultIPv6Interface(&(mname6.ipv6mr_multiaddr));
2158                 if (rt_index > 0) {
2159                     index = rt_index;
2160                 }
2161             }
2162 #endif
2163 #ifdef MACOSX
2164             if (family == AF_INET6 && index == 0) {
2165                 index = getDefaultScopeID(env);
2166             }
2167 #endif
2168             mname6.ipv6mr_interface = index;
2169         } else {
2170             jint idx = (*env)->GetIntField(env, niObj, ni_indexID);
2171             mname6.ipv6mr_interface = idx;
2172         }
2173 
2174 #if defined(_ALLBSD_SOURCE)
2175 #define ADD_MEMBERSHIP          IPV6_JOIN_GROUP
2176 #define DRP_MEMBERSHIP          IPV6_LEAVE_GROUP
2177 #define S_ADD_MEMBERSHIP        "IPV6_JOIN_GROUP"
2178 #define S_DRP_MEMBERSHIP        "IPV6_LEAVE_GROUP"
2179 #else
2180 #define ADD_MEMBERSHIP          IPV6_ADD_MEMBERSHIP
2181 #define DRP_MEMBERSHIP          IPV6_DROP_MEMBERSHIP
2182 #define S_ADD_MEMBERSHIP        "IPV6_ADD_MEMBERSHIP"
2183 #define S_DRP_MEMBERSHIP        "IPV6_DROP_MEMBERSHIP"
2184 #endif
2185 
2186         /* Join the multicast group */
2187         if (setsockopt(fd, IPPROTO_IPV6, (join ? ADD_MEMBERSHIP : DRP_MEMBERSHIP),
2188                        (char *) &mname6, sizeof (mname6)) < 0) {
2189 
2190             if (join) {
2191                 NET_ThrowCurrent(env, "setsockopt " S_ADD_MEMBERSHIP " failed");
2192             } else {
2193                 if (errno == ENOENT) {
2194                    JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
2195                         "Not a member of the multicast group");
2196                 } else {
2197                     NET_ThrowCurrent(env, "setsockopt " S_DRP_MEMBERSHIP " failed");
2198                 }
2199             }
2200         }
2201     }
2202 }
2203 
2204 /*
2205  * Class:     java_net_PlainDatagramSocketImpl
2206  * Method:    join
2207  * Signature: (Ljava/net/InetAddress;)V
2208  */
2209 JNIEXPORT void JNICALL
2210 Java_java_net_PlainDatagramSocketImpl_join(JNIEnv *env, jobject this,
2211                                            jobject iaObj, jobject niObj)
2212 {
2213     mcast_join_leave(env, this, iaObj, niObj, JNI_TRUE);
2214 }
2215 
2216 /*
2217  * Class:     java_net_PlainDatagramSocketImpl
2218  * Method:    leave
2219  * Signature: (Ljava/net/InetAddress;)V
2220  */
2221 JNIEXPORT void JNICALL
2222 Java_java_net_PlainDatagramSocketImpl_leave(JNIEnv *env, jobject this,
2223                                             jobject iaObj, jobject niObj)
2224 {
2225     mcast_join_leave(env, this, iaObj, niObj, JNI_FALSE);
2226 }
2227 
2228 /*
2229  * Class:     java_net_PlainDatagramSocketImpl
2230  * Method:    dataAvailable
2231  * Signature: ()I
2232  */
2233 JNIEXPORT jint JNICALL
2234 Java_java_net_PlainDatagramSocketImpl_dataAvailable(JNIEnv *env, jobject this)
2235 {
2236     int fd, retval;
2237 
2238     jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
2239 
2240     if (IS_NULL(fdObj)) {
2241         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
2242                         "Socket closed");
2243         return -1;
2244     }
2245     fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
2246 
2247     if (ioctl(fd, FIONREAD, &retval) < 0) {
2248         return -1;
2249     }
2250     return retval;
2251 }