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