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