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