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