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