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