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_InetAddressToSockaddr(env, iaObj, localport, &sa, &len,
 200                                   JNI_TRUE) != 0) {
 201       return;
 202     }
 203     setDefaultScopeID(env, &sa.sa);
 204 
 205     if (NET_Bind(fd, &sa, len) < 0)  {
 206         if (errno == EADDRINUSE || errno == EADDRNOTAVAIL ||
 207             errno == EPERM || errno == EACCES) {
 208             NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "BindException",
 209                             "Bind failed");
 210         } else {
 211             JNU_ThrowByNameWithMessageAndLastError
 212                 (env, JNU_JAVANETPKG "SocketException", "Bind failed");
 213         }
 214         return;
 215     }
 216 
 217     /* initialize the local port */
 218     if (localport == 0) {
 219         /* Now that we're a connected socket, let's extract the port number
 220          * that the system chose for us and store it in the Socket object.
 221          */
 222         if (getsockname(fd, &sa.sa, &slen) == -1) {
 223             JNU_ThrowByNameWithMessageAndLastError
 224                 (env, JNU_JAVANETPKG "SocketException", "Error getting socket name");
 225             return;
 226         }
 227 
 228         localport = NET_GetPortFromSockaddr(&sa);
 229 
 230         (*env)->SetIntField(env, this, pdsi_localPortID, localport);
 231     } else {
 232         (*env)->SetIntField(env, this, pdsi_localPortID, localport);
 233     }
 234 }
 235 
 236 /*
 237  * Class:     java_net_PlainDatagramSocketImpl
 238  * Method:    connect0
 239  * Signature: (Ljava/net/InetAddress;I)V
 240  */
 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 static int socket0(int domain, int type, int protocol) {
 894     if (domain == AF_INET) {
 895         errno = EAFNOSUPPORT;
 896         return -1;
 897     }
 898     return socket(domain, type, protocol);
 899 }
 900 
 901 /*
 902  * Class:     java_net_PlainDatagramSocketImpl
 903  * Method:    datagramSocketCreate
 904  * Signature: ()V
 905  */
 906 JNIEXPORT void JNICALL
 907 Java_java_net_PlainDatagramSocketImpl_datagramSocketCreate(JNIEnv *env,
 908                                                            jobject this) {
 909     jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
 910     int arg, fd, t = 1;
 911     char tmpbuf[1024];
 912     int domain = ipv6_available() ? AF_INET6 : AF_INET;
 913 
 914     if (IS_NULL(fdObj)) {
 915         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
 916                         "Socket closed");
 917         return;
 918     }
 919 
 920     if ((fd = socket0(domain, SOCK_DGRAM, 0)) == -1) {
 921         JNU_ThrowByNameWithMessageAndLastError
 922             (env, JNU_JAVANETPKG "SocketException", "Error creating socket");
 923         return;
 924     }
 925 
 926     /*
 927      * If IPv4 is available, disable IPV6_V6ONLY to ensure dual-socket support.
 928      */
 929     if (domain == AF_INET6 && ipv4_available()) {
 930         arg = 0;
 931         if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&arg,
 932                        sizeof(int)) < 0) {
 933             NET_ThrowNew(env, errno, "cannot set IPPROTO_IPV6");
 934             close(fd);
 935             return;
 936         }
 937     }
 938 
 939 #ifdef __APPLE__
 940     arg = 65507;
 941     if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF,
 942                    (char *)&arg, sizeof(arg)) < 0) {
 943         getErrorString(errno, tmpbuf, sizeof(tmpbuf));
 944         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", tmpbuf);
 945         close(fd);
 946         return;
 947     }
 948     if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF,
 949                    (char *)&arg, sizeof(arg)) < 0) {
 950         getErrorString(errno, tmpbuf, sizeof(tmpbuf));
 951         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", tmpbuf);
 952         close(fd);
 953         return;
 954     }
 955 #endif /* __APPLE__ */
 956 
 957     if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (char*) &t, sizeof (int)) < 0) {
 958         getErrorString(errno, tmpbuf, sizeof(tmpbuf));
 959         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", tmpbuf);
 960         close(fd);
 961         return;
 962     }
 963 
 964 #if defined(__linux__)
 965      arg = 0;
 966      int level = (domain == AF_INET6) ? IPPROTO_IPV6 : IPPROTO_IP;
 967      if ((setsockopt(fd, level, IP_MULTICAST_ALL, (char*)&arg, sizeof(arg)) < 0) &&
 968            (errno != ENOPROTOOPT))
 969     {
 970         getErrorString(errno, tmpbuf, sizeof(tmpbuf));
 971         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", tmpbuf);
 972          close(fd);
 973          return;
 974      }
 975 #endif
 976 
 977 #if defined (__linux__)
 978     /*
 979      * On Linux for IPv6 sockets we must set the hop limit
 980      * to 1 to be compatible with default TTL of 1 for IPv4 sockets.
 981      */
 982     if (domain == AF_INET6) {
 983         int ttl = 1;
 984         if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (char *) &ttl,
 985                 sizeof (ttl)) < 0) {
 986             getErrorString(errno, tmpbuf, sizeof(tmpbuf));
 987             JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", tmpbuf);
 988             close(fd);
 989             return;
 990         }
 991     }
 992 #endif /* __linux__ */
 993 
 994     (*env)->SetIntField(env, fdObj, IO_fd_fdID, fd);
 995 }
 996 
 997 /*
 998  * Class:     java_net_PlainDatagramSocketImpl
 999  * Method:    datagramSocketClose
1000  * Signature: ()V
1001  */
1002 JNIEXPORT void JNICALL
1003 Java_java_net_PlainDatagramSocketImpl_datagramSocketClose(JNIEnv *env,
1004                                                           jobject this) {
1005     /*
1006      * REMIND: PUT A LOCK AROUND THIS CODE
1007      */
1008     jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
1009     int fd;
1010 
1011     if (IS_NULL(fdObj)) {
1012         return;
1013     }
1014     fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
1015     if (fd == -1) {
1016         return;
1017     }
1018     (*env)->SetIntField(env, fdObj, IO_fd_fdID, -1);
1019     NET_SocketClose(fd);
1020 }
1021 
1022 
1023 /*
1024  * Set outgoing multicast interface designated by a NetworkInterface.
1025  * Throw exception if failed.
1026  */
1027 static void mcast_set_if_by_if_v4(JNIEnv *env, jobject this, int fd, jobject value) {
1028     static jfieldID ni_addrsID;
1029     struct in_addr in;
1030     jobjectArray addrArray;
1031     jsize len;
1032     jint family;
1033     jobject addr;
1034     int i;
1035 
1036     if (ni_addrsID == NULL ) {
1037         jclass c = (*env)->FindClass(env, "java/net/NetworkInterface");
1038         CHECK_NULL(c);
1039         ni_addrsID = (*env)->GetFieldID(env, c, "addrs",
1040                                         "[Ljava/net/InetAddress;");
1041         CHECK_NULL(ni_addrsID);
1042     }
1043 
1044     addrArray = (*env)->GetObjectField(env, value, ni_addrsID);
1045     len = (*env)->GetArrayLength(env, addrArray);
1046 
1047     /*
1048      * Check that there is at least one address bound to this
1049      * interface.
1050      */
1051     if (len < 1) {
1052         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
1053             "bad argument for IP_MULTICAST_IF2: No IP addresses bound to interface");
1054         return;
1055     }
1056 
1057     /*
1058      * We need an ipv4 address here
1059      */
1060     in.s_addr = 0;
1061     for (i = 0; i < len; i++) {
1062         addr = (*env)->GetObjectArrayElement(env, addrArray, i);
1063         family = getInetAddress_family(env, addr);
1064         JNU_CHECK_EXCEPTION(env);
1065         if (family == java_net_InetAddress_IPv4) {
1066             in.s_addr = htonl(getInetAddress_addr(env, addr));
1067             JNU_CHECK_EXCEPTION(env);
1068             break;
1069         }
1070     }
1071 
1072     if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF,
1073                    (const char *)&in, sizeof(in)) < 0) {
1074         JNU_ThrowByNameWithMessageAndLastError
1075             (env, JNU_JAVANETPKG "SocketException", "Error setting socket option");
1076     }
1077 }
1078 
1079 /*
1080  * Set outgoing multicast interface designated by a NetworkInterface.
1081  * Throw exception if failed.
1082  */
1083 static void mcast_set_if_by_if_v6(JNIEnv *env, jobject this, int fd, jobject value) {
1084     static jfieldID ni_indexID;
1085     int index;
1086 
1087     if (ni_indexID == NULL) {
1088         jclass c = (*env)->FindClass(env, "java/net/NetworkInterface");
1089         CHECK_NULL(c);
1090         ni_indexID = (*env)->GetFieldID(env, c, "index", "I");
1091         CHECK_NULL(ni_indexID);
1092     }
1093     index = (*env)->GetIntField(env, value, ni_indexID);
1094 
1095     if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF,
1096                    (const char*)&index, sizeof(index)) < 0) {
1097         if ((errno == EINVAL || errno == EADDRNOTAVAIL) && index > 0) {
1098             JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
1099                 "IPV6_MULTICAST_IF failed (interface has IPv4 "
1100                 "address only?)");
1101         } else {
1102             JNU_ThrowByNameWithMessageAndLastError
1103                 (env, JNU_JAVANETPKG "SocketException", "Error setting socket option");
1104         }
1105         return;
1106     }
1107 }
1108 
1109 /*
1110  * Set outgoing multicast interface designated by an InetAddress.
1111  * Throw exception if failed.
1112  */
1113 static void mcast_set_if_by_addr_v4(JNIEnv *env, jobject this, int fd, jobject value) {
1114     struct in_addr in;
1115 
1116     in.s_addr = htonl( getInetAddress_addr(env, value) );
1117     JNU_CHECK_EXCEPTION(env);
1118     if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF,
1119                    (const char*)&in, sizeof(in)) < 0) {
1120         JNU_ThrowByNameWithMessageAndLastError
1121             (env, JNU_JAVANETPKG "SocketException", "Error setting socket option");
1122     }
1123 }
1124 
1125 /*
1126  * Set outgoing multicast interface designated by an InetAddress.
1127  * Throw exception if failed.
1128  */
1129 static void mcast_set_if_by_addr_v6(JNIEnv *env, jobject this, int fd, jobject value) {
1130     static jclass ni_class;
1131     if (ni_class == NULL) {
1132         jclass c = (*env)->FindClass(env, "java/net/NetworkInterface");
1133         CHECK_NULL(c);
1134         ni_class = (*env)->NewGlobalRef(env, c);
1135         CHECK_NULL(ni_class);
1136     }
1137 
1138     value = Java_java_net_NetworkInterface_getByInetAddress0(env, ni_class, value);
1139     if (value == NULL) {
1140         if (!(*env)->ExceptionOccurred(env)) {
1141             JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
1142                  "bad argument for IP_MULTICAST_IF"
1143                  ": address not bound to any interface");
1144         }
1145         return;
1146     }
1147 
1148     mcast_set_if_by_if_v6(env, this, fd, value);
1149 }
1150 
1151 /*
1152  * Sets the multicast interface.
1153  *
1154  * SocketOptions.IP_MULTICAST_IF :-
1155  *      value is a InetAddress
1156  *      IPv4:   set outgoing multicast interface using
1157  *              IPPROTO_IP/IP_MULTICAST_IF
1158  *      IPv6:   Get the index of the interface to which the
1159  *              InetAddress is bound
1160  *              Set outgoing multicast interface using
1161  *              IPPROTO_IPV6/IPV6_MULTICAST_IF
1162  *
1163  * SockOptions.IF_MULTICAST_IF2 :-
1164  *      value is a NetworkInterface
1165  *      IPv4:   Obtain IP address bound to network interface
1166  *              (NetworkInterface.addres[0])
1167  *              set outgoing multicast interface using
1168  *              IPPROTO_IP/IP_MULTICAST_IF
1169  *      IPv6:   Obtain NetworkInterface.index
1170  *              Set outgoing multicast interface using
1171  *              IPPROTO_IPV6/IPV6_MULTICAST_IF
1172  *
1173  */
1174 static void setMulticastInterface(JNIEnv *env, jobject this, int fd,
1175                                   jint opt, jobject value)
1176 {
1177     if (opt == java_net_SocketOptions_IP_MULTICAST_IF) {
1178         /*
1179          * value is an InetAddress.
1180          */
1181 #ifdef __linux__
1182         mcast_set_if_by_addr_v4(env, this, fd, value);
1183         if (ipv6_available()) {
1184             if ((*env)->ExceptionCheck(env)){
1185                 (*env)->ExceptionClear(env);
1186             }
1187             mcast_set_if_by_addr_v6(env, this, fd, value);
1188         }
1189 #else  /* __linux__ not defined */
1190         if (ipv6_available()) {
1191             mcast_set_if_by_addr_v6(env, this, fd, value);
1192         } else {
1193             mcast_set_if_by_addr_v4(env, this, fd, value);
1194         }
1195 #endif  /* __linux__ */
1196     }
1197 
1198     if (opt == java_net_SocketOptions_IP_MULTICAST_IF2) {
1199         /*
1200          * value is a NetworkInterface.
1201          */
1202 #ifdef __linux__
1203         mcast_set_if_by_if_v4(env, this, fd, value);
1204         if (ipv6_available()) {
1205             if ((*env)->ExceptionCheck(env)){
1206                 (*env)->ExceptionClear(env);
1207             }
1208             mcast_set_if_by_if_v6(env, this, fd, value);
1209         }
1210 #else  /* __linux__ not defined */
1211         if (ipv6_available()) {
1212             mcast_set_if_by_if_v6(env, this, fd, value);
1213         } else {
1214             mcast_set_if_by_if_v4(env, this, fd, value);
1215         }
1216 #endif  /* __linux__ */
1217     }
1218 }
1219 
1220 /*
1221  * Enable/disable local loopback of multicast datagrams.
1222  */
1223 static void mcast_set_loop_v4(JNIEnv *env, jobject this, int fd, jobject value) {
1224     jclass cls;
1225     jfieldID fid;
1226     jboolean on;
1227     char loopback;
1228 
1229     cls = (*env)->FindClass(env, "java/lang/Boolean");
1230     CHECK_NULL(cls);
1231     fid =  (*env)->GetFieldID(env, cls, "value", "Z");
1232     CHECK_NULL(fid);
1233 
1234     on = (*env)->GetBooleanField(env, value, fid);
1235     loopback = (!on ? 1 : 0);
1236 
1237     if (NET_SetSockOpt(fd, IPPROTO_IP, IP_MULTICAST_LOOP,
1238                        (const void *)&loopback, sizeof(char)) < 0) {
1239         JNU_ThrowByNameWithMessageAndLastError
1240             (env, JNU_JAVANETPKG "SocketException", "Error setting socket option");
1241         return;
1242     }
1243 }
1244 
1245 /*
1246  * Enable/disable local loopback of multicast datagrams.
1247  */
1248 static void mcast_set_loop_v6(JNIEnv *env, jobject this, int fd, jobject value) {
1249     jclass cls;
1250     jfieldID fid;
1251     jboolean on;
1252     int loopback;
1253 
1254     cls = (*env)->FindClass(env, "java/lang/Boolean");
1255     CHECK_NULL(cls);
1256     fid =  (*env)->GetFieldID(env, cls, "value", "Z");
1257     CHECK_NULL(fid);
1258 
1259     on = (*env)->GetBooleanField(env, value, fid);
1260     loopback = (!on ? 1 : 0);
1261 
1262     if (NET_SetSockOpt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
1263                        (const void *)&loopback, sizeof(int)) < 0) {
1264         JNU_ThrowByNameWithMessageAndLastError
1265             (env, JNU_JAVANETPKG "SocketException", "Error setting socket option");
1266         return;
1267     }
1268 
1269 }
1270 
1271 /*
1272  * Sets the multicast loopback mode.
1273  */
1274 static void setMulticastLoopbackMode(JNIEnv *env, jobject this, int fd,
1275                                      jint opt, jobject value) {
1276 #ifdef __linux__
1277     mcast_set_loop_v4(env, this, fd, value);
1278     if (ipv6_available()) {
1279         if ((*env)->ExceptionCheck(env)){
1280             (*env)->ExceptionClear(env);
1281         }
1282         mcast_set_loop_v6(env, this, fd, value);
1283     }
1284 #else  /* __linux__ not defined */
1285     if (ipv6_available()) {
1286         mcast_set_loop_v6(env, this, fd, value);
1287     } else {
1288         mcast_set_loop_v4(env, this, fd, value);
1289     }
1290 #endif  /* __linux__ */
1291 }
1292 
1293 /*
1294  * Class:     java_net_PlainDatagramSocketImpl
1295  * Method:    socketSetOption0
1296  * Signature: (ILjava/lang/Object;)V
1297  */
1298 JNIEXPORT void JNICALL
1299 Java_java_net_PlainDatagramSocketImpl_socketSetOption0
1300   (JNIEnv *env, jobject this, jint opt, jobject value)
1301 {
1302     int fd;
1303     int level, optname, optlen;
1304     int optval;
1305     optlen = sizeof(int);
1306 
1307     /*
1308      * Check that socket hasn't been closed
1309      */
1310     fd = getFD(env, this);
1311     if (fd < 0) {
1312         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
1313                         "Socket closed");
1314         return;
1315     }
1316 
1317     /*
1318      * Check argument has been provided
1319      */
1320     if (IS_NULL(value)) {
1321         JNU_ThrowNullPointerException(env, "value argument");
1322         return;
1323     }
1324 
1325     /*
1326      * Setting the multicast interface handled separately
1327      */
1328     if (opt == java_net_SocketOptions_IP_MULTICAST_IF ||
1329         opt == java_net_SocketOptions_IP_MULTICAST_IF2) {
1330 
1331         setMulticastInterface(env, this, fd, opt, value);
1332         return;
1333     }
1334 
1335     /*
1336      * Setting the multicast loopback mode handled separately
1337      */
1338     if (opt == java_net_SocketOptions_IP_MULTICAST_LOOP) {
1339         setMulticastLoopbackMode(env, this, fd, opt, value);
1340         return;
1341     }
1342 
1343     /*
1344      * Map the Java level socket option to the platform specific
1345      * level and option name.
1346      */
1347     if (NET_MapSocketOption(opt, &level, &optname)) {
1348         JNU_ThrowByName(env, "java/net/SocketException", "Invalid option");
1349         return;
1350     }
1351 
1352     switch (opt) {
1353         case java_net_SocketOptions_SO_SNDBUF :
1354         case java_net_SocketOptions_SO_RCVBUF :
1355         case java_net_SocketOptions_IP_TOS :
1356             {
1357                 jclass cls;
1358                 jfieldID fid;
1359 
1360                 cls = (*env)->FindClass(env, "java/lang/Integer");
1361                 CHECK_NULL(cls);
1362                 fid =  (*env)->GetFieldID(env, cls, "value", "I");
1363                 CHECK_NULL(fid);
1364 
1365                 optval = (*env)->GetIntField(env, value, fid);
1366                 break;
1367             }
1368 
1369         case java_net_SocketOptions_SO_REUSEADDR:
1370         case java_net_SocketOptions_SO_REUSEPORT:
1371         case java_net_SocketOptions_SO_BROADCAST:
1372             {
1373                 jclass cls;
1374                 jfieldID fid;
1375                 jboolean on;
1376 
1377                 cls = (*env)->FindClass(env, "java/lang/Boolean");
1378                 CHECK_NULL(cls);
1379                 fid =  (*env)->GetFieldID(env, cls, "value", "Z");
1380                 CHECK_NULL(fid);
1381 
1382                 on = (*env)->GetBooleanField(env, value, fid);
1383 
1384                 /* SO_REUSEADDR or SO_BROADCAST */
1385                 optval = (on ? 1 : 0);
1386 
1387                 break;
1388             }
1389 
1390         default :
1391             JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
1392                 "Socket option not supported by PlainDatagramSocketImp");
1393             return;
1394 
1395     }
1396 
1397     if (NET_SetSockOpt(fd, level, optname, (const void *)&optval, optlen) < 0) {
1398         JNU_ThrowByNameWithMessageAndLastError
1399             (env, JNU_JAVANETPKG "SocketException", "Error setting socket option");
1400         return;
1401     }
1402 }
1403 
1404 
1405 /*
1406  * Return the multicast interface:
1407  *
1408  * SocketOptions.IP_MULTICAST_IF
1409  *      IPv4:   Query IPPROTO_IP/IP_MULTICAST_IF
1410  *              Create InetAddress
1411  *              IP_MULTICAST_IF returns struct ip_mreqn on 2.2
1412  *              kernel but struct in_addr on 2.4 kernel
1413  *      IPv6:   Query IPPROTO_IPV6 / IPV6_MULTICAST_IF
1414  *              If index == 0 return InetAddress representing
1415  *              anyLocalAddress.
1416  *              If index > 0 query NetworkInterface by index
1417  *              and returns addrs[0]
1418  *
1419  * SocketOptions.IP_MULTICAST_IF2
1420  *      IPv4:   Query IPPROTO_IP/IP_MULTICAST_IF
1421  *              Query NetworkInterface by IP address and
1422  *              return the NetworkInterface that the address
1423  *              is bound too.
1424  *      IPv6:   Query IPPROTO_IPV6 / IPV6_MULTICAST_IF
1425  *              (except Linux .2 kernel)
1426  *              Query NetworkInterface by index and
1427  *              return NetworkInterface.
1428  */
1429 jobject getMulticastInterface(JNIEnv *env, jobject this, int fd, jint opt) {
1430     jboolean isIPV4 = JNI_TRUE;
1431 
1432     if (ipv6_available()) {
1433         isIPV4 = JNI_FALSE;
1434     }
1435 
1436     /*
1437      * IPv4 implementation
1438      */
1439     if (isIPV4) {
1440         static jclass inet4_class;
1441         static jmethodID inet4_ctrID;
1442 
1443         static jclass ni_class;
1444         static jmethodID ni_ctrID;
1445         static jfieldID ni_indexID;
1446         static jfieldID ni_addrsID;
1447         static jfieldID ni_nameID;
1448 
1449         jobjectArray addrArray;
1450         jobject addr;
1451         jobject ni;
1452         jobject ni_name;
1453 
1454         struct in_addr in;
1455         struct in_addr *inP = &in;
1456         socklen_t len = sizeof(struct in_addr);
1457 
1458         if (getsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF,
1459                        (char *)inP, &len) < 0) {
1460             JNU_ThrowByNameWithMessageAndLastError
1461                 (env, JNU_JAVANETPKG "SocketException", "Error getting socket option");
1462             return NULL;
1463         }
1464 
1465         /*
1466          * Construct and populate an Inet4Address
1467          */
1468         if (inet4_class == NULL) {
1469             jclass c = (*env)->FindClass(env, "java/net/Inet4Address");
1470             CHECK_NULL_RETURN(c, NULL);
1471             inet4_ctrID = (*env)->GetMethodID(env, c, "<init>", "()V");
1472             CHECK_NULL_RETURN(inet4_ctrID, NULL);
1473             inet4_class = (*env)->NewGlobalRef(env, c);
1474             CHECK_NULL_RETURN(inet4_class, NULL);
1475         }
1476         addr = (*env)->NewObject(env, inet4_class, inet4_ctrID, 0);
1477         CHECK_NULL_RETURN(addr, NULL);
1478 
1479         setInetAddress_addr(env, addr, ntohl(in.s_addr));
1480         JNU_CHECK_EXCEPTION_RETURN(env, NULL);
1481 
1482         /*
1483          * For IP_MULTICAST_IF return InetAddress
1484          */
1485         if (opt == java_net_SocketOptions_IP_MULTICAST_IF) {
1486             return addr;
1487         }
1488 
1489         /*
1490          * For IP_MULTICAST_IF2 we get the NetworkInterface for
1491          * this address and return it
1492          */
1493         if (ni_class == NULL) {
1494             jclass c = (*env)->FindClass(env, "java/net/NetworkInterface");
1495             CHECK_NULL_RETURN(c, NULL);
1496             ni_ctrID = (*env)->GetMethodID(env, c, "<init>", "()V");
1497             CHECK_NULL_RETURN(ni_ctrID, NULL);
1498             ni_indexID = (*env)->GetFieldID(env, c, "index", "I");
1499             CHECK_NULL_RETURN(ni_indexID, NULL);
1500             ni_addrsID = (*env)->GetFieldID(env, c, "addrs",
1501                                             "[Ljava/net/InetAddress;");
1502             CHECK_NULL_RETURN(ni_addrsID, NULL);
1503             ni_nameID = (*env)->GetFieldID(env, c,"name", "Ljava/lang/String;");
1504             CHECK_NULL_RETURN(ni_nameID, NULL);
1505             ni_class = (*env)->NewGlobalRef(env, c);
1506             CHECK_NULL_RETURN(ni_class, NULL);
1507         }
1508         ni = Java_java_net_NetworkInterface_getByInetAddress0(env, ni_class, addr);
1509         JNU_CHECK_EXCEPTION_RETURN(env, NULL);
1510         if (ni) {
1511             return ni;
1512         }
1513 
1514         /*
1515          * The address doesn't appear to be bound at any known
1516          * NetworkInterface. Therefore we construct a NetworkInterface
1517          * with this address.
1518          */
1519         ni = (*env)->NewObject(env, ni_class, ni_ctrID, 0);
1520         CHECK_NULL_RETURN(ni, NULL);
1521 
1522         (*env)->SetIntField(env, ni, ni_indexID, -1);
1523         addrArray = (*env)->NewObjectArray(env, 1, inet4_class, NULL);
1524         CHECK_NULL_RETURN(addrArray, NULL);
1525         (*env)->SetObjectArrayElement(env, addrArray, 0, addr);
1526         (*env)->SetObjectField(env, ni, ni_addrsID, addrArray);
1527         ni_name = (*env)->NewStringUTF(env, "");
1528         if (ni_name != NULL) {
1529             (*env)->SetObjectField(env, ni, ni_nameID, ni_name);
1530         }
1531         return ni;
1532     }
1533 
1534 
1535     /*
1536      * IPv6 implementation
1537      */
1538     if ((opt == java_net_SocketOptions_IP_MULTICAST_IF) ||
1539         (opt == java_net_SocketOptions_IP_MULTICAST_IF2)) {
1540 
1541         static jclass ni_class;
1542         static jmethodID ni_ctrID;
1543         static jfieldID ni_indexID;
1544         static jfieldID ni_addrsID;
1545         static jclass ia_class;
1546         static jfieldID ni_nameID;
1547         static jmethodID ia_anyLocalAddressID;
1548 
1549         int index = 0;
1550         socklen_t len = sizeof(index);
1551 
1552         jobjectArray addrArray;
1553         jobject addr;
1554         jobject ni;
1555         jobject ni_name;
1556 
1557         if (getsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF,
1558                        (char*)&index, &len) < 0) {
1559             JNU_ThrowByNameWithMessageAndLastError
1560                 (env, JNU_JAVANETPKG "SocketException", "Error getting socket option");
1561             return NULL;
1562         }
1563 
1564         if (ni_class == NULL) {
1565             jclass c = (*env)->FindClass(env, "java/net/NetworkInterface");
1566             CHECK_NULL_RETURN(c, NULL);
1567             ni_ctrID = (*env)->GetMethodID(env, c, "<init>", "()V");
1568             CHECK_NULL_RETURN(ni_ctrID, NULL);
1569             ni_indexID = (*env)->GetFieldID(env, c, "index", "I");
1570             CHECK_NULL_RETURN(ni_indexID, NULL);
1571             ni_addrsID = (*env)->GetFieldID(env, c, "addrs",
1572                                             "[Ljava/net/InetAddress;");
1573             CHECK_NULL_RETURN(ni_addrsID, NULL);
1574 
1575             ia_class = (*env)->FindClass(env, "java/net/InetAddress");
1576             CHECK_NULL_RETURN(ia_class, NULL);
1577             ia_class = (*env)->NewGlobalRef(env, ia_class);
1578             CHECK_NULL_RETURN(ia_class, NULL);
1579             ia_anyLocalAddressID = (*env)->GetStaticMethodID(env,
1580                                                              ia_class,
1581                                                              "anyLocalAddress",
1582                                                              "()Ljava/net/InetAddress;");
1583             CHECK_NULL_RETURN(ia_anyLocalAddressID, NULL);
1584             ni_nameID = (*env)->GetFieldID(env, c,"name", "Ljava/lang/String;");
1585             CHECK_NULL_RETURN(ni_nameID, NULL);
1586             ni_class = (*env)->NewGlobalRef(env, c);
1587             CHECK_NULL_RETURN(ni_class, NULL);
1588         }
1589 
1590         /*
1591          * If multicast to a specific interface then return the
1592          * interface (for IF2) or the any address on that interface
1593          * (for IF).
1594          */
1595         if (index > 0) {
1596             ni = Java_java_net_NetworkInterface_getByIndex0(env, ni_class,
1597                                                                    index);
1598             if (ni == NULL) {
1599                 char errmsg[255];
1600                 sprintf(errmsg,
1601                         "IPV6_MULTICAST_IF returned index to unrecognized interface: %d",
1602                         index);
1603                 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", errmsg);
1604                 return NULL;
1605             }
1606 
1607             /*
1608              * For IP_MULTICAST_IF2 return the NetworkInterface
1609              */
1610             if (opt == java_net_SocketOptions_IP_MULTICAST_IF2) {
1611                 return ni;
1612             }
1613 
1614             /*
1615              * For IP_MULTICAST_IF return addrs[0]
1616              */
1617             addrArray = (*env)->GetObjectField(env, ni, ni_addrsID);
1618             if ((*env)->GetArrayLength(env, addrArray) < 1) {
1619                 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
1620                     "IPV6_MULTICAST_IF returned interface without IP bindings");
1621                 return NULL;
1622             }
1623 
1624             addr = (*env)->GetObjectArrayElement(env, addrArray, 0);
1625             return addr;
1626         }
1627 
1628         /*
1629          * Multicast to any address - return anyLocalAddress
1630          * or a NetworkInterface with addrs[0] set to anyLocalAddress
1631          */
1632 
1633         addr = (*env)->CallStaticObjectMethod(env, ia_class, ia_anyLocalAddressID,
1634                                               NULL);
1635         if (opt == java_net_SocketOptions_IP_MULTICAST_IF) {
1636             return addr;
1637         }
1638 
1639         ni = (*env)->NewObject(env, ni_class, ni_ctrID, 0);
1640         CHECK_NULL_RETURN(ni, NULL);
1641         (*env)->SetIntField(env, ni, ni_indexID, -1);
1642         addrArray = (*env)->NewObjectArray(env, 1, ia_class, NULL);
1643         CHECK_NULL_RETURN(addrArray, NULL);
1644         (*env)->SetObjectArrayElement(env, addrArray, 0, addr);
1645         (*env)->SetObjectField(env, ni, ni_addrsID, addrArray);
1646         ni_name = (*env)->NewStringUTF(env, "");
1647         if (ni_name != NULL) {
1648             (*env)->SetObjectField(env, ni, ni_nameID, ni_name);
1649         }
1650         return ni;
1651     }
1652     return NULL;
1653 }
1654 
1655 
1656 
1657 /*
1658  * Returns relevant info as a jint.
1659  *
1660  * Class:     java_net_PlainDatagramSocketImpl
1661  * Method:    socketGetOption
1662  * Signature: (I)Ljava/lang/Object;
1663  */
1664 JNIEXPORT jobject JNICALL
1665 Java_java_net_PlainDatagramSocketImpl_socketGetOption
1666   (JNIEnv *env, jobject this, jint opt)
1667 {
1668     int fd;
1669     int level, optname, optlen;
1670     union {
1671         int i;
1672         char c;
1673     } optval;
1674 
1675     fd = getFD(env, this);
1676     if (fd < 0) {
1677         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
1678                         "socket closed");
1679         return NULL;
1680     }
1681 
1682     /*
1683      * Handle IP_MULTICAST_IF separately
1684      */
1685     if (opt == java_net_SocketOptions_IP_MULTICAST_IF ||
1686         opt == java_net_SocketOptions_IP_MULTICAST_IF2) {
1687         return getMulticastInterface(env, this, fd, opt);
1688 
1689     }
1690 
1691     /*
1692      * SO_BINDADDR implemented using getsockname
1693      */
1694     if (opt == java_net_SocketOptions_SO_BINDADDR) {
1695         /* find out local IP address */
1696         SOCKETADDRESS sa;
1697         socklen_t len = sizeof(SOCKETADDRESS);
1698         int port;
1699         jobject iaObj;
1700 
1701         if (getsockname(fd, &sa.sa, &len) == -1) {
1702             JNU_ThrowByNameWithMessageAndLastError
1703                 (env, JNU_JAVANETPKG "SocketException", "Error getting socket name");
1704             return NULL;
1705         }
1706         iaObj = NET_SockaddrToInetAddress(env, &sa, &port);
1707 
1708         return iaObj;
1709     }
1710 
1711     /*
1712      * Map the Java level socket option to the platform specific
1713      * level and option name.
1714      */
1715     if (NET_MapSocketOption(opt, &level, &optname)) {
1716         JNU_ThrowByName(env, "java/net/SocketException", "Invalid option");
1717         return NULL;
1718     }
1719 
1720     if (opt == java_net_SocketOptions_IP_MULTICAST_LOOP &&
1721         level == IPPROTO_IP) {
1722         optlen = sizeof(optval.c);
1723     } else {
1724         optlen = sizeof(optval.i);
1725     }
1726 
1727     if (NET_GetSockOpt(fd, level, optname, (void *)&optval, &optlen) < 0) {
1728         JNU_ThrowByNameWithMessageAndLastError
1729             (env, JNU_JAVANETPKG "SocketException", "Error getting socket option");
1730         return NULL;
1731     }
1732 
1733     switch (opt) {
1734         case java_net_SocketOptions_IP_MULTICAST_LOOP:
1735             /* getLoopbackMode() returns true if IP_MULTICAST_LOOP disabled */
1736             if (level == IPPROTO_IP) {
1737                 return createBoolean(env, (int)!optval.c);
1738             } else {
1739                 return createBoolean(env, !optval.i);
1740             }
1741 
1742         case java_net_SocketOptions_SO_BROADCAST:
1743         case java_net_SocketOptions_SO_REUSEADDR:
1744             return createBoolean(env, optval.i);
1745 
1746         case java_net_SocketOptions_SO_REUSEPORT:
1747             return createBoolean(env, optval.i);
1748 
1749         case java_net_SocketOptions_SO_SNDBUF:
1750         case java_net_SocketOptions_SO_RCVBUF:
1751         case java_net_SocketOptions_IP_TOS:
1752             return createInteger(env, optval.i);
1753 
1754     }
1755 
1756     /* should never reach here */
1757     return NULL;
1758 }
1759 
1760 /*
1761  * Multicast-related calls
1762  */
1763 
1764 JNIEXPORT void JNICALL
1765 Java_java_net_PlainDatagramSocketImpl_setTTL(JNIEnv *env, jobject this,
1766                                              jbyte ttl) {
1767     jint ittl = ttl;
1768     if (ittl < 0) {
1769         ittl += 0x100;
1770     }
1771     Java_java_net_PlainDatagramSocketImpl_setTimeToLive(env, this, ittl);
1772 }
1773 
1774 /*
1775  * Set TTL for a socket. Throw exception if failed.
1776  */
1777 static void setTTL(JNIEnv *env, int fd, jint ttl) {
1778     char ittl = (char)ttl;
1779     if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, (char*)&ittl,
1780                    sizeof(ittl)) < 0) {
1781         JNU_ThrowByNameWithMessageAndLastError
1782             (env, JNU_JAVANETPKG "SocketException", "Error setting socket option");
1783     }
1784 }
1785 
1786 /*
1787  * Set hops limit for a socket. Throw exception if failed.
1788  */
1789 static void setHopLimit(JNIEnv *env, int fd, jint ttl) {
1790     int ittl = (int)ttl;
1791     if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
1792                    (char*)&ittl, sizeof(ittl)) < 0) {
1793         JNU_ThrowByNameWithMessageAndLastError
1794             (env, JNU_JAVANETPKG "SocketException", "Error setting socket option");
1795     }
1796 }
1797 
1798 /*
1799  * Class:     java_net_PlainDatagramSocketImpl
1800  * Method:    setTTL
1801  * Signature: (B)V
1802  */
1803 JNIEXPORT void JNICALL
1804 Java_java_net_PlainDatagramSocketImpl_setTimeToLive(JNIEnv *env, jobject this,
1805                                                     jint ttl) {
1806 
1807     jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
1808     int fd;
1809     /* it is important to cast this to a char, otherwise setsockopt gets confused */
1810 
1811     if (IS_NULL(fdObj)) {
1812         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
1813                         "Socket closed");
1814         return;
1815     } else {
1816         fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
1817     }
1818     /* setsockopt to be correct TTL */
1819 #ifdef __linux__
1820     setTTL(env, fd, ttl);
1821     JNU_CHECK_EXCEPTION(env);
1822     if (ipv6_available()) {
1823         setHopLimit(env, fd, ttl);
1824     }
1825 #else  /*  __linux__ not defined */
1826     if (ipv6_available()) {
1827         setHopLimit(env, fd, ttl);
1828     } else {
1829         setTTL(env, fd, ttl);
1830     }
1831 #endif  /* __linux__ */
1832 }
1833 
1834 /*
1835  * Class:     java_net_PlainDatagramSocketImpl
1836  * Method:    getTTL
1837  * Signature: ()B
1838  */
1839 JNIEXPORT jbyte JNICALL
1840 Java_java_net_PlainDatagramSocketImpl_getTTL(JNIEnv *env, jobject this) {
1841     return (jbyte)Java_java_net_PlainDatagramSocketImpl_getTimeToLive(env, this);
1842 }
1843 
1844 
1845 /*
1846  * Class:     java_net_PlainDatagramSocketImpl
1847  * Method:    getTTL
1848  * Signature: ()B
1849  */
1850 JNIEXPORT jint JNICALL
1851 Java_java_net_PlainDatagramSocketImpl_getTimeToLive(JNIEnv *env, jobject this) {
1852 
1853     jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
1854     jint fd = -1;
1855 
1856     if (IS_NULL(fdObj)) {
1857         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
1858                         "Socket closed");
1859         return -1;
1860     } else {
1861         fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
1862     }
1863     /* getsockopt of TTL */
1864     if (ipv6_available()) {
1865         int ttl = 0;
1866         socklen_t len = sizeof(ttl);
1867 
1868         if (getsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
1869                        (char*)&ttl, &len) < 0) {
1870             JNU_ThrowByNameWithMessageAndLastError
1871                 (env, JNU_JAVANETPKG "SocketException", "Error getting socket option");
1872             return -1;
1873         }
1874         return (jint)ttl;
1875     } else {
1876         u_char ttl = 0;
1877         socklen_t len = sizeof(ttl);
1878         if (getsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL,
1879                        (char*)&ttl, &len) < 0) {
1880             JNU_ThrowByNameWithMessageAndLastError
1881                 (env, JNU_JAVANETPKG "SocketException", "Error getting socket option");
1882             return -1;
1883         }
1884         return (jint)ttl;
1885     }
1886 }
1887 
1888 
1889 /*
1890  * mcast_join_leave: Join or leave a multicast group.
1891  *
1892  * For IPv4 sockets use IP_ADD_MEMBERSHIP/IP_DROP_MEMBERSHIP socket option
1893  * to join/leave multicast group.
1894  *
1895  * For IPv6 sockets use IPV6_ADD_MEMBERSHIP/IPV6_DROP_MEMBERSHIP socket option
1896  * to join/leave multicast group. If multicast group is an IPv4 address then
1897  * an IPv4-mapped address is used.
1898  *
1899  * On Linux with IPv6 if we wish to join/leave an IPv4 multicast group then
1900  * we must use the IPv4 socket options. This is because the IPv6 socket options
1901  * don't support IPv4-mapped addresses. This is true as per 2.2.19 and 2.4.7
1902  * kernel releases. In the future it's possible that IP_ADD_MEMBERSHIP
1903  * will be updated to return ENOPROTOOPT if uses with an IPv6 socket (Solaris
1904  * already does this). Thus to cater for this we first try with the IPv4
1905  * socket options and if they fail we use the IPv6 socket options. This
1906  * seems a reasonable failsafe solution.
1907  */
1908 static void mcast_join_leave(JNIEnv *env, jobject this,
1909                              jobject iaObj, jobject niObj,
1910                              jboolean join) {
1911 
1912     jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
1913     jint fd;
1914     jint family;
1915     jint ipv6_join_leave;
1916 
1917     if (IS_NULL(fdObj)) {
1918         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
1919                         "Socket closed");
1920         return;
1921     } else {
1922         fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
1923     }
1924     if (IS_NULL(iaObj)) {
1925         JNU_ThrowNullPointerException(env, "iaObj");
1926         return;
1927     }
1928 
1929     /*
1930      * Determine if this is an IPv4 or IPv6 join/leave.
1931      */
1932     ipv6_join_leave = ipv6_available();
1933 
1934 #ifdef __linux__
1935     family = getInetAddress_family(env, iaObj);
1936     JNU_CHECK_EXCEPTION(env);
1937     if (family == java_net_InetAddress_IPv4) {
1938         ipv6_join_leave = JNI_FALSE;
1939     }
1940 #endif
1941 
1942     /*
1943      * For IPv4 join use IP_ADD_MEMBERSHIP/IP_DROP_MEMBERSHIP socket option
1944      *
1945      * On Linux if IPv4 or IPv6 use IP_ADD_MEMBERSHIP/IP_DROP_MEMBERSHIP
1946      */
1947     if (!ipv6_join_leave) {
1948 #ifdef __linux__
1949         struct ip_mreqn mname;
1950 #else
1951         struct ip_mreq mname;
1952 #endif
1953         int mname_len;
1954 
1955         /*
1956          * joinGroup(InetAddress, NetworkInterface) implementation :-
1957          *
1958          * Linux/IPv6:  use ip_mreqn structure populated with multicast
1959          *              address and interface index.
1960          *
1961          * IPv4:        use ip_mreq structure populated with multicast
1962          *              address and first address obtained from
1963          *              NetworkInterface
1964          */
1965         if (niObj != NULL) {
1966 #if defined(__linux__)
1967             if (ipv6_available()) {
1968                 static jfieldID ni_indexID;
1969 
1970                 if (ni_indexID == NULL) {
1971                     jclass c = (*env)->FindClass(env, "java/net/NetworkInterface");
1972                     CHECK_NULL(c);
1973                     ni_indexID = (*env)->GetFieldID(env, c, "index", "I");
1974                     CHECK_NULL(ni_indexID);
1975                 }
1976 
1977                 mname.imr_multiaddr.s_addr = htonl(getInetAddress_addr(env, iaObj));
1978                 JNU_CHECK_EXCEPTION(env);
1979                 mname.imr_address.s_addr = 0;
1980                 mname.imr_ifindex =  (*env)->GetIntField(env, niObj, ni_indexID);
1981                 mname_len = sizeof(struct ip_mreqn);
1982             } else
1983 #endif
1984             {
1985                 jobjectArray addrArray = (*env)->GetObjectField(env, niObj, ni_addrsID);
1986                 jobject addr;
1987 
1988                 if ((*env)->GetArrayLength(env, addrArray) < 1) {
1989                     JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
1990                         "bad argument for IP_ADD_MEMBERSHIP: "
1991                         "No IP addresses bound to interface");
1992                     return;
1993                 }
1994                 addr = (*env)->GetObjectArrayElement(env, addrArray, 0);
1995 
1996                 mname.imr_multiaddr.s_addr = htonl(getInetAddress_addr(env, iaObj));
1997                 JNU_CHECK_EXCEPTION(env);
1998 #ifdef __linux__
1999                 mname.imr_address.s_addr = htonl(getInetAddress_addr(env, addr));
2000                 JNU_CHECK_EXCEPTION(env);
2001                 mname.imr_ifindex = 0;
2002 #else
2003                 mname.imr_interface.s_addr = htonl(getInetAddress_addr(env, addr));
2004                 JNU_CHECK_EXCEPTION(env);
2005 #endif
2006                 mname_len = sizeof(struct ip_mreq);
2007             }
2008         }
2009 
2010 
2011         /*
2012          * joinGroup(InetAddress) implementation :-
2013          *
2014          * Linux/IPv6:  use ip_mreqn structure populated with multicast
2015          *              address and interface index. index obtained
2016          *              from cached value or IPV6_MULTICAST_IF.
2017          *
2018          * IPv4:        use ip_mreq structure populated with multicast
2019          *              address and local address obtained from
2020          *              IP_MULTICAST_IF. On Linux IP_MULTICAST_IF
2021          *              returns different structure depending on
2022          *              kernel.
2023          */
2024 
2025         if (niObj == NULL) {
2026 
2027 #if defined(__linux__)
2028             if (ipv6_available()) {
2029 
2030                 int index;
2031                 socklen_t len = sizeof(index);
2032 
2033                 if (getsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF,
2034                                (char*)&index, &len) < 0) {
2035                     NET_ThrowCurrent(env, "getsockopt IPV6_MULTICAST_IF failed");
2036                     return;
2037                 }
2038 
2039                 mname.imr_multiaddr.s_addr = htonl(getInetAddress_addr(env, iaObj));
2040                 JNU_CHECK_EXCEPTION(env);
2041                 mname.imr_address.s_addr = 0 ;
2042                 mname.imr_ifindex = index;
2043                 mname_len = sizeof(struct ip_mreqn);
2044             } else
2045 #endif
2046             {
2047                 struct in_addr in;
2048                 struct in_addr *inP = &in;
2049                 socklen_t len = sizeof(struct in_addr);
2050 
2051                 if (getsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, (char *)inP, &len) < 0) {
2052                     NET_ThrowCurrent(env, "getsockopt IP_MULTICAST_IF failed");
2053                     return;
2054                 }
2055 
2056 #ifdef __linux__
2057                 mname.imr_address.s_addr = in.s_addr;
2058                 mname.imr_ifindex = 0;
2059 #else
2060                 mname.imr_interface.s_addr = in.s_addr;
2061 #endif
2062                 mname.imr_multiaddr.s_addr = htonl(getInetAddress_addr(env, iaObj));
2063                 JNU_CHECK_EXCEPTION(env);
2064                 mname_len = sizeof(struct ip_mreq);
2065             }
2066         }
2067 
2068 
2069         /*
2070          * Join the multicast group.
2071          */
2072         if (setsockopt(fd, IPPROTO_IP, (join ? IP_ADD_MEMBERSHIP:IP_DROP_MEMBERSHIP),
2073                        (char *) &mname, mname_len) < 0) {
2074 
2075             /*
2076              * If IP_ADD_MEMBERSHIP returns ENOPROTOOPT on Linux and we've got
2077              * IPv6 enabled then it's possible that the kernel has been fixed
2078              * so we switch to IPV6_ADD_MEMBERSHIP socket option.
2079              * As of 2.4.7 kernel IPV6_ADD_MEMBERSHIP can't handle IPv4-mapped
2080              * addresses so we have to use IP_ADD_MEMBERSHIP for IPv4 multicast
2081              * groups. However if the socket is an IPv6 socket then setsockopt
2082              * should return ENOPROTOOPT. We assume this will be fixed in Linux
2083              * at some stage.
2084              */
2085 #if defined(__linux__)
2086             if (errno == ENOPROTOOPT) {
2087                 if (ipv6_available()) {
2088                     ipv6_join_leave = JNI_TRUE;
2089                     errno = 0;
2090                 } else  {
2091                     errno = ENOPROTOOPT;    /* errno can be changed by ipv6_available */
2092                 }
2093             }
2094 #endif
2095             if (errno) {
2096                 if (join) {
2097                     NET_ThrowCurrent(env, "setsockopt IP_ADD_MEMBERSHIP failed");
2098                 } else {
2099                     if (errno == ENOENT)
2100                         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
2101                             "Not a member of the multicast group");
2102                     else
2103                         NET_ThrowCurrent(env, "setsockopt IP_DROP_MEMBERSHIP failed");
2104                 }
2105                 return;
2106             }
2107         }
2108 
2109         /*
2110          * If we haven't switched to IPv6 socket option then we're done.
2111          */
2112         if (!ipv6_join_leave) {
2113             return;
2114         }
2115     }
2116 
2117 
2118     /*
2119      * IPv6 join. If it's an IPv4 multicast group then we use an IPv4-mapped
2120      * address.
2121      */
2122     {
2123         struct ipv6_mreq mname6;
2124         jbyteArray ipaddress;
2125         jbyte caddr[16];
2126         jint family;
2127         jint address;
2128         family = getInetAddress_family(env, iaObj) == java_net_InetAddress_IPv4 ?
2129             AF_INET : AF_INET6;
2130         JNU_CHECK_EXCEPTION(env);
2131         if (family == AF_INET) { /* will convert to IPv4-mapped address */
2132             memset((char *) caddr, 0, 16);
2133             address = getInetAddress_addr(env, iaObj);
2134             JNU_CHECK_EXCEPTION(env);
2135             caddr[10] = 0xff;
2136             caddr[11] = 0xff;
2137 
2138             caddr[12] = ((address >> 24) & 0xff);
2139             caddr[13] = ((address >> 16) & 0xff);
2140             caddr[14] = ((address >> 8) & 0xff);
2141             caddr[15] = (address & 0xff);
2142         } else {
2143             getInet6Address_ipaddress(env, iaObj, (char*)caddr);
2144         }
2145 
2146         memcpy((void *)&(mname6.ipv6mr_multiaddr), caddr, sizeof(struct in6_addr));
2147         if (IS_NULL(niObj)) {
2148             int index;
2149             socklen_t len = sizeof(index);
2150 
2151             if (getsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF,
2152                            (char*)&index, &len) < 0) {
2153                 NET_ThrowCurrent(env, "getsockopt IPV6_MULTICAST_IF failed");
2154                 return;
2155             }
2156 
2157 #ifdef __linux__
2158             /*
2159              * On 2.4.8+ if we join a group with the interface set to 0
2160              * then the kernel records the interface it decides. This causes
2161              * subsequent leave groups to fail as there is no match. Thus we
2162              * pick the interface if there is a matching route.
2163              */
2164             if (index == 0) {
2165                 int rt_index = getDefaultIPv6Interface(&(mname6.ipv6mr_multiaddr));
2166                 if (rt_index > 0) {
2167                     index = rt_index;
2168                 }
2169             }
2170 #endif
2171 #ifdef MACOSX
2172             if (family == AF_INET6 && index == 0) {
2173                 index = getDefaultScopeID(env);
2174             }
2175 #endif
2176             mname6.ipv6mr_interface = index;
2177         } else {
2178             jint idx = (*env)->GetIntField(env, niObj, ni_indexID);
2179             mname6.ipv6mr_interface = idx;
2180         }
2181 
2182 #if defined(_ALLBSD_SOURCE)
2183 #define ADD_MEMBERSHIP          IPV6_JOIN_GROUP
2184 #define DRP_MEMBERSHIP          IPV6_LEAVE_GROUP
2185 #define S_ADD_MEMBERSHIP        "IPV6_JOIN_GROUP"
2186 #define S_DRP_MEMBERSHIP        "IPV6_LEAVE_GROUP"
2187 #else
2188 #define ADD_MEMBERSHIP          IPV6_ADD_MEMBERSHIP
2189 #define DRP_MEMBERSHIP          IPV6_DROP_MEMBERSHIP
2190 #define S_ADD_MEMBERSHIP        "IPV6_ADD_MEMBERSHIP"
2191 #define S_DRP_MEMBERSHIP        "IPV6_DROP_MEMBERSHIP"
2192 #endif
2193 
2194         /* Join the multicast group */
2195         if (setsockopt(fd, IPPROTO_IPV6, (join ? ADD_MEMBERSHIP : DRP_MEMBERSHIP),
2196                        (char *) &mname6, sizeof (mname6)) < 0) {
2197 
2198             if (join) {
2199                 NET_ThrowCurrent(env, "setsockopt " S_ADD_MEMBERSHIP " failed");
2200             } else {
2201                 if (errno == ENOENT) {
2202                    JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
2203                         "Not a member of the multicast group");
2204                 } else {
2205                     NET_ThrowCurrent(env, "setsockopt " S_DRP_MEMBERSHIP " failed");
2206                 }
2207             }
2208         }
2209     }
2210 }
2211 
2212 /*
2213  * Class:     java_net_PlainDatagramSocketImpl
2214  * Method:    join
2215  * Signature: (Ljava/net/InetAddress;)V
2216  */
2217 JNIEXPORT void JNICALL
2218 Java_java_net_PlainDatagramSocketImpl_join(JNIEnv *env, jobject this,
2219                                            jobject iaObj, jobject niObj)
2220 {
2221     mcast_join_leave(env, this, iaObj, niObj, JNI_TRUE);
2222 }
2223 
2224 /*
2225  * Class:     java_net_PlainDatagramSocketImpl
2226  * Method:    leave
2227  * Signature: (Ljava/net/InetAddress;)V
2228  */
2229 JNIEXPORT void JNICALL
2230 Java_java_net_PlainDatagramSocketImpl_leave(JNIEnv *env, jobject this,
2231                                             jobject iaObj, jobject niObj)
2232 {
2233     mcast_join_leave(env, this, iaObj, niObj, JNI_FALSE);
2234 }
2235 
2236 /*
2237  * Class:     java_net_PlainDatagramSocketImpl
2238  * Method:    dataAvailable
2239  * Signature: ()I
2240  */
2241 JNIEXPORT jint JNICALL
2242 Java_java_net_PlainDatagramSocketImpl_dataAvailable(JNIEnv *env, jobject this)
2243 {
2244     int fd, retval;
2245 
2246     jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
2247 
2248     if (IS_NULL(fdObj)) {
2249         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
2250                         "Socket closed");
2251         return -1;
2252     }
2253     fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
2254 
2255     if (ioctl(fd, FIONREAD, &retval) < 0) {
2256         return -1;
2257     }
2258     return retval;
2259 }