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