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