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