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