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