1 /*
   2  * Copyright 1997-2008 Sun Microsystems, Inc.  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.  Sun designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
  22  * CA 95054 USA or visit www.sun.com if you need additional information or
  23  * have any 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;
1056 
1057     int t = 1;
1058 
1059     if (IS_NULL(fdObj)) {
1060         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
1061                         "Socket closed");
1062         return;
1063     } else {
1064 #ifdef AF_INET6
1065         if (ipv6_available()) {
1066             fd =  JVM_Socket(AF_INET6, SOCK_DGRAM, 0);
1067         } else
1068 #endif /* AF_INET6 */
1069             {
1070                 fd =  JVM_Socket(AF_INET, SOCK_DGRAM, 0);
1071             }
1072     }
1073     if (fd == JVM_IO_ERR) {
1074         NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
1075                        "Error creating socket");
1076         return;
1077     }
1078 
1079      setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (char*) &t, sizeof(int));
1080 
1081 #ifdef __linux__
1082     if (isOldKernel) {
1083         setsockopt(fd, SOL_SOCKET, SO_BSDCOMPAT, (char*) &t, sizeof(int));
1084     }
1085 
1086 #ifdef AF_INET6
1087     /*
1088      * On Linux for IPv6 sockets we must set the hop limit
1089      * to 1 to be compatible with default ttl of 1 for IPv4 sockets.
1090      */
1091     if (ipv6_available()) {
1092         int ttl = 1;
1093         setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (char *)&ttl,
1094                    sizeof(ttl));
1095 
1096         if (isOldKernel) {
1097             (*env)->SetIntField(env, this, pdsi_ttlID, ttl);
1098         }
1099     }
1100 #endif
1101 
1102 #endif /* __linux__ */
1103 
1104     (*env)->SetIntField(env, fdObj, IO_fd_fdID, fd);
1105 }
1106 
1107 /*
1108  * Class:     java_net_PlainDatagramSocketImpl
1109  * Method:    datagramSocketClose
1110  * Signature: ()V
1111  */
1112 JNIEXPORT void JNICALL
1113 Java_java_net_PlainDatagramSocketImpl_datagramSocketClose(JNIEnv *env,
1114                                                           jobject this) {
1115     /*
1116      * REMIND: PUT A LOCK AROUND THIS CODE
1117      */
1118     jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
1119     int fd;
1120 
1121     if (IS_NULL(fdObj)) {
1122         return;
1123     }
1124     fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
1125     if (fd == -1) {
1126         return;
1127     }
1128     (*env)->SetIntField(env, fdObj, IO_fd_fdID, -1);
1129     NET_SocketClose(fd);
1130 }
1131 
1132 
1133 /*
1134  * Set outgoing multicast interface designated by a NetworkInterface.
1135  * Throw exception if failed.
1136  */
1137 static void mcast_set_if_by_if_v4(JNIEnv *env, jobject this, int fd, jobject value) {
1138     static jfieldID ni_addrsID;
1139     static jfieldID ia_addressID;
1140     struct in_addr in;
1141     jobjectArray addrArray;
1142     jsize len;
1143     jobject addr;
1144     int i;
1145 
1146     if (ni_addrsID == NULL) {
1147         jclass c = (*env)->FindClass(env, "java/net/NetworkInterface");
1148         CHECK_NULL(c);
1149         ni_addrsID = (*env)->GetFieldID(env, c, "addrs",
1150                                         "[Ljava/net/InetAddress;");
1151         CHECK_NULL(ni_addrsID);
1152         c = (*env)->FindClass(env,"java/net/InetAddress");
1153         CHECK_NULL(c);
1154         ia_addressID = (*env)->GetFieldID(env, c, "address", "I");
1155         CHECK_NULL(ia_addressID);
1156     }
1157 
1158     addrArray = (*env)->GetObjectField(env, value, ni_addrsID);
1159     len = (*env)->GetArrayLength(env, addrArray);
1160 
1161     /*
1162      * Check that there is at least one address bound to this
1163      * interface.
1164      */
1165     if (len < 1) {
1166         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
1167             "bad argument for IP_MULTICAST_IF2: No IP addresses bound to interface");
1168         return;
1169     }
1170 
1171     /*
1172      * We need an ipv4 address here
1173      */
1174     for (i = 0; i < len; i++) {
1175         addr = (*env)->GetObjectArrayElement(env, addrArray, i);
1176         if ((*env)->GetIntField(env, addr, ia_familyID) == IPv4) {
1177             in.s_addr = htonl((*env)->GetIntField(env, addr, ia_addressID));
1178             break;
1179         }
1180     }
1181 
1182     if (JVM_SetSockOpt(fd, IPPROTO_IP, IP_MULTICAST_IF,
1183                        (const char*)&in, sizeof(in)) < 0) {
1184         NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
1185                        "Error setting socket option");
1186     }
1187 }
1188 
1189 /*
1190  * Set outgoing multicast interface designated by a NetworkInterface.
1191  * Throw exception if failed.
1192  */
1193 #ifdef AF_INET6
1194 static void mcast_set_if_by_if_v6(JNIEnv *env, jobject this, int fd, jobject value) {
1195     static jfieldID ni_indexID;
1196     int index;
1197 
1198     if (ni_indexID == NULL) {
1199         jclass c = (*env)->FindClass(env, "java/net/NetworkInterface");
1200         CHECK_NULL(c);
1201         ni_indexID = (*env)->GetFieldID(env, c, "index", "I");
1202         CHECK_NULL(ni_indexID);
1203     }
1204     index = (*env)->GetIntField(env, value, ni_indexID);
1205 
1206     if (JVM_SetSockOpt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF,
1207                        (const char*)&index, sizeof(index)) < 0) {
1208         if (errno == EINVAL && index > 0) {
1209             JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
1210                 "IPV6_MULTICAST_IF failed (interface has IPv4 "
1211                 "address only?)");
1212         } else {
1213             NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
1214                            "Error setting socket option");
1215         }
1216         return;
1217     }
1218 
1219 #ifdef __linux__
1220     /*
1221      * Linux 2.2 kernel doesn't support IPV6_MULTICAST_IF socket
1222      * option so record index for later retrival.
1223      */
1224     if (isOldKernel) {
1225         (*env)->SetIntField(env, this, pdsi_multicastInterfaceID,
1226                             (jint)index);
1227     }
1228 #endif
1229 }
1230 #endif /* AF_INET6 */
1231 
1232 /*
1233  * Set outgoing multicast interface designated by an InetAddress.
1234  * Throw exception if failed.
1235  */
1236 static void mcast_set_if_by_addr_v4(JNIEnv *env, jobject this, int fd, jobject value) {
1237     static jfieldID ia_addressID;
1238     struct in_addr in;
1239 
1240     if (ia_addressID == NULL) {
1241         jclass c = (*env)->FindClass(env,"java/net/InetAddress");
1242         CHECK_NULL(c);
1243         ia_addressID = (*env)->GetFieldID(env, c, "address", "I");
1244         CHECK_NULL(ia_addressID);
1245     }
1246 
1247     in.s_addr = htonl( (*env)->GetIntField(env, value, ia_addressID) );
1248 
1249     if (JVM_SetSockOpt(fd, IPPROTO_IP, IP_MULTICAST_IF,
1250                        (const char*)&in, sizeof(in)) < 0) {
1251         NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
1252                          "Error setting socket option");
1253     }
1254 }
1255 
1256 /*
1257  * Set outgoing multicast interface designated by an InetAddress.
1258  * Throw exception if failed.
1259  */
1260 #ifdef AF_INET6
1261 static void mcast_set_if_by_addr_v6(JNIEnv *env, jobject this, int fd, jobject value) {
1262     static jclass ni_class;
1263     if (ni_class == NULL) {
1264         jclass c = (*env)->FindClass(env, "java/net/NetworkInterface");
1265         CHECK_NULL(c);
1266         ni_class = (*env)->NewGlobalRef(env, c);
1267         CHECK_NULL(ni_class);
1268     }
1269 
1270     value = Java_java_net_NetworkInterface_getByInetAddress0(env, ni_class, value);
1271     if (value == NULL) {
1272         if (!(*env)->ExceptionOccurred(env)) {
1273             JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
1274                  "bad argument for IP_MULTICAST_IF"
1275                  ": address not bound to any interface");
1276         }
1277         return;
1278     }
1279 
1280     mcast_set_if_by_if_v6(env, this, fd, value);
1281 }
1282 #endif
1283 
1284 /*
1285  * Sets the multicast interface.
1286  *
1287  * SocketOptions.IP_MULTICAST_IF :-
1288  *      value is a InetAddress
1289  *      IPv4:   set outgoing multicast interface using
1290  *              IPPROTO_IP/IP_MULTICAST_IF
1291  *      IPv6:   Get the index of the interface to which the
1292  *              InetAddress is bound
1293  *              Set outgoing multicast interface using
1294  *              IPPROTO_IPV6/IPV6_MULTICAST_IF
1295  *              On Linux 2.2 record interface index as can't
1296  *              query the multicast interface.
1297  *
1298  * SockOptions.IF_MULTICAST_IF2 :-
1299  *      value is a NetworkInterface
1300  *      IPv4:   Obtain IP address bound to network interface
1301  *              (NetworkInterface.addres[0])
1302  *              set outgoing multicast interface using
1303  *              IPPROTO_IP/IP_MULTICAST_IF
1304  *      IPv6:   Obtain NetworkInterface.index
1305  *              Set outgoing multicast interface using
1306  *              IPPROTO_IPV6/IPV6_MULTICAST_IF
1307  *              On Linux 2.2 record interface index as can't
1308  *              query the multicast interface.
1309  *
1310  */
1311 static void setMulticastInterface(JNIEnv *env, jobject this, int fd,
1312                                   jint opt, jobject value)
1313 {
1314     if (opt == java_net_SocketOptions_IP_MULTICAST_IF) {
1315         /*
1316          * value is an InetAddress.
1317          */
1318 #ifdef AF_INET6
1319 #ifdef __solaris__
1320         if (ipv6_available()) {
1321             mcast_set_if_by_addr_v6(env, this, fd, value);
1322         } else {
1323             mcast_set_if_by_addr_v4(env, this, fd, value);
1324         }
1325 #endif
1326 #ifdef __linux__
1327         mcast_set_if_by_addr_v4(env, this, fd, value);
1328         if (ipv6_available()) {
1329             mcast_set_if_by_addr_v6(env, this, fd, value);
1330         }
1331 #endif
1332 #else
1333         mcast_set_if_by_addr_v4(env, this, fd, value);
1334 #endif  /* AF_INET6 */
1335     }
1336 
1337     if (opt == java_net_SocketOptions_IP_MULTICAST_IF2) {
1338         /*
1339          * value is a NetworkInterface.
1340          */
1341 #ifdef AF_INET6
1342 #ifdef __solaris__
1343         if (ipv6_available()) {
1344             mcast_set_if_by_if_v6(env, this, fd, value);
1345         } else {
1346             mcast_set_if_by_if_v4(env, this, fd, value);
1347         }
1348 #endif
1349 #ifdef __linux__
1350         mcast_set_if_by_if_v4(env, this, fd, value);
1351         if (ipv6_available()) {
1352             mcast_set_if_by_if_v6(env, this, fd, value);
1353         }
1354 #endif
1355 #else
1356         mcast_set_if_by_if_v4(env, this, fd, value);
1357 #endif  /* AF_INET6 */
1358     }
1359 }
1360 
1361 /*
1362  * Enable/disable local loopback of multicast datagrams.
1363  */
1364 static void mcast_set_loop_v4(JNIEnv *env, jobject this, int fd, jobject value) {
1365     jclass cls;
1366     jfieldID fid;
1367     jboolean on;
1368     char loopback;
1369 
1370     cls = (*env)->FindClass(env, "java/lang/Boolean");
1371     CHECK_NULL(cls);
1372     fid =  (*env)->GetFieldID(env, cls, "value", "Z");
1373     CHECK_NULL(fid);
1374 
1375     on = (*env)->GetBooleanField(env, value, fid);
1376     loopback = (!on ? 1 : 0);
1377 
1378     if (NET_SetSockOpt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, (const void *)&loopback, sizeof(char)) < 0) {
1379         NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "Error setting socket option");
1380         return;
1381     }
1382 }
1383 
1384 /*
1385  * Enable/disable local loopback of multicast datagrams.
1386  */
1387 #ifdef AF_INET6
1388 static void mcast_set_loop_v6(JNIEnv *env, jobject this, int fd, jobject value) {
1389     jclass cls;
1390     jfieldID fid;
1391     jboolean on;
1392     int loopback;
1393 
1394     cls = (*env)->FindClass(env, "java/lang/Boolean");
1395     CHECK_NULL(cls);
1396     fid =  (*env)->GetFieldID(env, cls, "value", "Z");
1397     CHECK_NULL(fid);
1398 
1399     on = (*env)->GetBooleanField(env, value, fid);
1400     loopback = (!on ? 1 : 0);
1401 
1402     if (NET_SetSockOpt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (const void *)&loopback, sizeof(int)) < 0) {
1403         NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "Error setting socket option");
1404         return;
1405     }
1406 
1407 #ifdef __linux__
1408     /*
1409      * Can't query IPV6_MULTICAST_LOOP on Linux 2.2 kernel so
1410      * store it in impl so that we can simulate getsockopt.
1411      */
1412     if (isOldKernel) {
1413         (*env)->SetBooleanField(env, this, pdsi_loopbackID, on);
1414     }
1415 #endif
1416 }
1417 #endif  /* AF_INET6 */
1418 
1419 /*
1420  * Sets the multicast loopback mode.
1421  */
1422 static void setMulticastLoopbackMode(JNIEnv *env, jobject this, int fd,
1423                                   jint opt, jobject value) {
1424 #ifdef AF_INET6
1425 #ifdef __solaris__
1426     if (ipv6_available()) {
1427         mcast_set_loop_v6(env, this, fd, value);
1428     } else {
1429         mcast_set_loop_v4(env, this, fd, value);
1430     }
1431 #endif
1432 #ifdef __linux__
1433     mcast_set_loop_v4(env, this, fd, value);
1434     if (ipv6_available()) {
1435         mcast_set_loop_v6(env, this, fd, value);
1436     }
1437 #endif
1438 #else
1439     mcast_set_loop_v4(env, this, fd, value);
1440 #endif  /* AF_INET6 */
1441 }
1442 
1443 /*
1444  * Class:     java_net_PlainDatagramSocketImpl
1445  * Method:    socketSetOption
1446  * Signature: (ILjava/lang/Object;)V
1447  */
1448 JNIEXPORT void JNICALL
1449 Java_java_net_PlainDatagramSocketImpl_socketSetOption(JNIEnv *env,
1450                                                       jobject this,
1451                                                       jint opt,
1452                                                       jobject value) {
1453     int fd;
1454     int level, optname, optlen;
1455     union {
1456         int i;
1457         char c;
1458     } optval;
1459 
1460     /*
1461      * Check that socket hasn't been closed
1462      */
1463     fd = getFD(env, this);
1464     if (fd < 0) {
1465         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
1466                         "Socket closed");
1467         return;
1468     }
1469 
1470     /*
1471      * Check argument has been provided
1472      */
1473     if (IS_NULL(value)) {
1474         JNU_ThrowNullPointerException(env, "value argument");
1475         return;
1476     }
1477 
1478     /*
1479      * Setting the multicast interface handled seperately
1480      */
1481     if (opt == java_net_SocketOptions_IP_MULTICAST_IF ||
1482         opt == java_net_SocketOptions_IP_MULTICAST_IF2) {
1483 
1484         setMulticastInterface(env, this, fd, opt, value);
1485         return;
1486     }
1487 
1488     /*
1489      * Setting the multicast loopback mode handled separately
1490      */
1491     if (opt == java_net_SocketOptions_IP_MULTICAST_LOOP) {
1492         setMulticastLoopbackMode(env, this, fd, opt, value);
1493         return;
1494     }
1495 
1496     /*
1497      * Map the Java level socket option to the platform specific
1498      * level and option name.
1499      */
1500     if (NET_MapSocketOption(opt, &level, &optname)) {
1501         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Invalid option");
1502         return;
1503     }
1504 
1505     switch (opt) {
1506         case java_net_SocketOptions_SO_SNDBUF :
1507         case java_net_SocketOptions_SO_RCVBUF :
1508         case java_net_SocketOptions_IP_TOS :
1509             {
1510                 jclass cls;
1511                 jfieldID fid;
1512 
1513                 cls = (*env)->FindClass(env, "java/lang/Integer");
1514                 CHECK_NULL(cls);
1515                 fid =  (*env)->GetFieldID(env, cls, "value", "I");
1516                 CHECK_NULL(fid);
1517 
1518                 optval.i = (*env)->GetIntField(env, value, fid);
1519                 optlen = sizeof(optval.i);
1520                 break;
1521             }
1522 
1523         case java_net_SocketOptions_SO_REUSEADDR:
1524         case java_net_SocketOptions_SO_BROADCAST:
1525             {
1526                 jclass cls;
1527                 jfieldID fid;
1528                 jboolean on;
1529 
1530                 cls = (*env)->FindClass(env, "java/lang/Boolean");
1531                 CHECK_NULL(cls);
1532                 fid =  (*env)->GetFieldID(env, cls, "value", "Z");
1533                 CHECK_NULL(fid);
1534 
1535                 on = (*env)->GetBooleanField(env, value, fid);
1536 
1537                 /* SO_REUSEADDR or SO_BROADCAST */
1538                 optval.i = (on ? 1 : 0);
1539                 optlen = sizeof(optval.i);
1540 
1541                 break;
1542             }
1543 
1544         default :
1545             JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
1546                 "Socket option not supported by PlainDatagramSocketImp");
1547             break;
1548 
1549     }
1550 
1551     if (NET_SetSockOpt(fd, level, optname, (const void *)&optval, optlen) < 0) {
1552         NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "Error setting socket option");
1553         return;
1554     }
1555 }
1556 
1557 
1558 /*
1559  * Return the multicast interface:
1560  *
1561  * SocketOptions.IP_MULTICAST_IF
1562  *      IPv4:   Query IPPROTO_IP/IP_MULTICAST_IF
1563  *              Create InetAddress
1564  *              IP_MULTICAST_IF returns struct ip_mreqn on 2.2
1565  *              kernel but struct in_addr on 2.4 kernel
1566  *      IPv6:   Query IPPROTO_IPV6 / IPV6_MULTICAST_IF or
1567  *              obtain from impl is Linux 2.2 kernel
1568  *              If index == 0 return InetAddress representing
1569  *              anyLocalAddress.
1570  *              If index > 0 query NetworkInterface by index
1571  *              and returns addrs[0]
1572  *
1573  * SocketOptions.IP_MULTICAST_IF2
1574  *      IPv4:   Query IPPROTO_IP/IP_MULTICAST_IF
1575  *              Query NetworkInterface by IP address and
1576  *              return the NetworkInterface that the address
1577  *              is bound too.
1578  *      IPv6:   Query IPPROTO_IPV6 / IPV6_MULTICAST_IF
1579  *              (except Linux .2 kernel)
1580  *              Query NetworkInterface by index and
1581  *              return NetworkInterface.
1582  */
1583 jobject getMulticastInterface(JNIEnv *env, jobject this, int fd, jint opt) {
1584     jboolean isIPV4 = JNI_TRUE;
1585 
1586 #ifdef AF_INET6
1587     if (ipv6_available()) {
1588         isIPV4 = JNI_FALSE;
1589     }
1590 #endif
1591 
1592     /*
1593      * IPv4 implementation
1594      */
1595     if (isIPV4) {
1596         static jclass inet4_class;
1597         static jmethodID inet4_ctrID;
1598         static jfieldID inet4_addrID;
1599 
1600         static jclass ni_class;
1601         static jmethodID ni_ctrID;
1602         static jfieldID ni_indexID;
1603         static jfieldID ni_addrsID;
1604 
1605         jobjectArray addrArray;
1606         jobject addr;
1607         jobject ni;
1608 
1609         struct in_addr in;
1610         struct in_addr *inP = &in;
1611         int len = sizeof(struct in_addr);
1612 
1613 #ifdef __linux__
1614         struct ip_mreqn mreqn;
1615         if (isOldKernel) {
1616             inP = (struct in_addr *)&mreqn;
1617             len = sizeof(struct ip_mreqn);
1618         }
1619 #endif
1620 
1621         if (JVM_GetSockOpt(fd, IPPROTO_IP, IP_MULTICAST_IF,
1622                            (char *)inP, &len) < 0) {
1623             NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
1624                              "Error getting socket option");
1625             return NULL;
1626         }
1627 
1628         /*
1629          * Construct and populate an Inet4Address
1630          */
1631         if (inet4_class == NULL) {
1632             jclass c = (*env)->FindClass(env, "java/net/Inet4Address");
1633             CHECK_NULL_RETURN(c, NULL);
1634             inet4_ctrID = (*env)->GetMethodID(env, c, "<init>", "()V");
1635             CHECK_NULL_RETURN(inet4_ctrID, NULL);
1636             inet4_addrID = (*env)->GetFieldID(env, c, "address", "I");
1637             CHECK_NULL_RETURN(inet4_addrID, NULL);
1638             inet4_class = (*env)->NewGlobalRef(env, c);
1639             CHECK_NULL_RETURN(inet4_class, NULL);
1640         }
1641         addr = (*env)->NewObject(env, inet4_class, inet4_ctrID, 0);
1642         CHECK_NULL_RETURN(addr, NULL);
1643 
1644 #ifdef __linux__
1645         (*env)->SetIntField(env, addr, inet4_addrID,
1646                 (isOldKernel ? ntohl(mreqn.imr_address.s_addr) : ntohl(in.s_addr)) );
1647 #else
1648         (*env)->SetIntField(env, addr, inet4_addrID, ntohl(in.s_addr));
1649 #endif
1650 
1651         /*
1652          * For IP_MULTICAST_IF return InetAddress
1653          */
1654         if (opt == java_net_SocketOptions_IP_MULTICAST_IF) {
1655             return addr;
1656         }
1657 
1658         /*
1659          * For IP_MULTICAST_IF2 we get the NetworkInterface for
1660          * this address and return it
1661          */
1662         if (ni_class == NULL) {
1663             jclass c = (*env)->FindClass(env, "java/net/NetworkInterface");
1664             CHECK_NULL_RETURN(c, NULL);
1665             ni_ctrID = (*env)->GetMethodID(env, c, "<init>", "()V");
1666             CHECK_NULL_RETURN(ni_ctrID, NULL);
1667             ni_indexID = (*env)->GetFieldID(env, c, "index", "I");
1668             CHECK_NULL_RETURN(ni_indexID, NULL);
1669             ni_addrsID = (*env)->GetFieldID(env, c, "addrs",
1670                                             "[Ljava/net/InetAddress;");
1671             CHECK_NULL_RETURN(ni_addrsID, NULL);
1672             ni_class = (*env)->NewGlobalRef(env, c);
1673             CHECK_NULL_RETURN(ni_class, NULL);
1674         }
1675         ni = Java_java_net_NetworkInterface_getByInetAddress0(env, ni_class, addr);
1676         if (ni) {
1677             return ni;
1678         }
1679 
1680         /*
1681          * The address doesn't appear to be bound at any known
1682          * NetworkInterface. Therefore we construct a NetworkInterface
1683          * with this address.
1684          */
1685         ni = (*env)->NewObject(env, ni_class, ni_ctrID, 0);
1686         CHECK_NULL_RETURN(ni, NULL);
1687 
1688         (*env)->SetIntField(env, ni, ni_indexID, -1);
1689         addrArray = (*env)->NewObjectArray(env, 1, inet4_class, NULL);
1690         CHECK_NULL_RETURN(addrArray, NULL);
1691         (*env)->SetObjectArrayElement(env, addrArray, 0, addr);
1692         (*env)->SetObjectField(env, ni, ni_addrsID, addrArray);
1693         return ni;
1694     }
1695 
1696 
1697 #ifdef AF_INET6
1698     /*
1699      * IPv6 implementation
1700      */
1701     if ((opt == java_net_SocketOptions_IP_MULTICAST_IF) ||
1702         (opt == java_net_SocketOptions_IP_MULTICAST_IF2)) {
1703 
1704         static jclass ni_class;
1705         static jmethodID ni_ctrID;
1706         static jfieldID ni_indexID;
1707         static jfieldID ni_addrsID;
1708         static jclass ia_class;
1709         static jmethodID ia_anyLocalAddressID;
1710 
1711         int index;
1712         int len = sizeof(index);
1713 
1714         jobjectArray addrArray;
1715         jobject addr;
1716         jobject ni;
1717 
1718 #ifdef __linux__
1719         /*
1720          * Linux 2.2 kernel doesn't support IPV6_MULTICAST_IF socke option
1721          * so use cached index.
1722          */
1723         if (isOldKernel) {
1724             index = (*env)->GetIntField(env, this, pdsi_multicastInterfaceID);
1725         } else
1726 #endif
1727         {
1728             if (JVM_GetSockOpt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF,
1729                                (char*)&index, &len) < 0) {
1730                 NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
1731                                "Error getting socket option");
1732                 return NULL;
1733             }
1734         }
1735 
1736         if (ni_class == NULL) {
1737             jclass c = (*env)->FindClass(env, "java/net/NetworkInterface");
1738             CHECK_NULL_RETURN(c, NULL);
1739             ni_ctrID = (*env)->GetMethodID(env, c, "<init>", "()V");
1740             CHECK_NULL_RETURN(ni_ctrID, NULL);
1741             ni_indexID = (*env)->GetFieldID(env, c, "index", "I");
1742             CHECK_NULL_RETURN(ni_indexID, NULL);
1743             ni_addrsID = (*env)->GetFieldID(env, c, "addrs",
1744                                             "[Ljava/net/InetAddress;");
1745             CHECK_NULL_RETURN(ni_addrsID, NULL);
1746 
1747             ia_class = (*env)->FindClass(env, "java/net/InetAddress");
1748             CHECK_NULL_RETURN(ia_class, NULL);
1749             ia_class = (*env)->NewGlobalRef(env, ia_class);
1750             CHECK_NULL_RETURN(ia_class, NULL);
1751             ia_anyLocalAddressID = (*env)->GetStaticMethodID(env,
1752                                                              ia_class,
1753                                                              "anyLocalAddress",
1754                                                              "()Ljava/net/InetAddress;");
1755             CHECK_NULL_RETURN(ia_anyLocalAddressID, NULL);
1756             ni_class = (*env)->NewGlobalRef(env, c);
1757             CHECK_NULL_RETURN(ni_class, NULL);
1758         }
1759 
1760         /*
1761          * If multicast to a specific interface then return the
1762          * interface (for IF2) or the any address on that interface
1763          * (for IF).
1764          */
1765         if (index > 0) {
1766             ni = Java_java_net_NetworkInterface_getByIndex0(env, ni_class,
1767                                                                    index);
1768             if (ni == NULL) {
1769                 char errmsg[255];
1770                 sprintf(errmsg,
1771                         "IPV6_MULTICAST_IF returned index to unrecognized interface: %d",
1772                         index);
1773                 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", errmsg);
1774                 return NULL;
1775             }
1776 
1777             /*
1778              * For IP_MULTICAST_IF2 return the NetworkInterface
1779              */
1780             if (opt == java_net_SocketOptions_IP_MULTICAST_IF2) {
1781                 return ni;
1782             }
1783 
1784             /*
1785              * For IP_MULTICAST_IF return addrs[0]
1786              */
1787             addrArray = (*env)->GetObjectField(env, ni, ni_addrsID);
1788             if ((*env)->GetArrayLength(env, addrArray) < 1) {
1789                 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
1790                     "IPV6_MULTICAST_IF returned interface without IP bindings");
1791                 return NULL;
1792             }
1793 
1794             addr = (*env)->GetObjectArrayElement(env, addrArray, 0);
1795             return addr;
1796         }
1797 
1798         /*
1799          * Multicast to any address - return anyLocalAddress
1800          * or a NetworkInterface with addrs[0] set to anyLocalAddress
1801          */
1802 
1803         addr = (*env)->CallStaticObjectMethod(env, ia_class, ia_anyLocalAddressID,
1804                                               NULL);
1805         if (opt == java_net_SocketOptions_IP_MULTICAST_IF) {
1806             return addr;
1807         }
1808 
1809         ni = (*env)->NewObject(env, ni_class, ni_ctrID, 0);
1810         CHECK_NULL_RETURN(ni, NULL);
1811         (*env)->SetIntField(env, ni, ni_indexID, -1);
1812         addrArray = (*env)->NewObjectArray(env, 1, ia_class, NULL);
1813         CHECK_NULL_RETURN(addrArray, NULL);
1814         (*env)->SetObjectArrayElement(env, addrArray, 0, addr);
1815         (*env)->SetObjectField(env, ni, ni_addrsID, addrArray);
1816         return ni;
1817     }
1818 #endif
1819     return NULL;
1820 }
1821 
1822 
1823 
1824 /*
1825  * Returns relevant info as a jint.
1826  *
1827  * Class:     java_net_PlainDatagramSocketImpl
1828  * Method:    socketGetOption
1829  * Signature: (I)Ljava/lang/Object;
1830  */
1831 JNIEXPORT jobject JNICALL
1832 Java_java_net_PlainDatagramSocketImpl_socketGetOption(JNIEnv *env, jobject this,
1833                                                       jint opt) {
1834     int fd;
1835     int level, optname, optlen;
1836     union {
1837         int i;
1838         char c;
1839     } optval;
1840 
1841     fd = getFD(env, this);
1842     if (fd < 0) {
1843         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
1844                         "socket closed");
1845         return NULL;
1846     }
1847 
1848     /*
1849      * Handle IP_MULTICAST_IF seperately
1850      */
1851     if (opt == java_net_SocketOptions_IP_MULTICAST_IF ||
1852         opt == java_net_SocketOptions_IP_MULTICAST_IF2) {
1853         return getMulticastInterface(env, this, fd, opt);
1854 
1855     }
1856 
1857     /*
1858      * SO_BINDADDR implemented using getsockname
1859      */
1860     if (opt == java_net_SocketOptions_SO_BINDADDR) {
1861         /* find out local IP address */
1862         SOCKADDR him;
1863         socklen_t len = 0;
1864         int port;
1865         jobject iaObj;
1866 
1867         len = SOCKADDR_LEN;
1868 
1869         if (getsockname(fd, (struct sockaddr *)&him, &len) == -1) {
1870             NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
1871                            "Error getting socket name");
1872             return NULL;
1873         }
1874         iaObj = NET_SockaddrToInetAddress(env, (struct sockaddr *)&him, &port);
1875 
1876         return iaObj;
1877     }
1878 
1879     /*
1880      * Map the Java level socket option to the platform specific
1881      * level and option name.
1882      */
1883     if (NET_MapSocketOption(opt, &level, &optname)) {
1884         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Invalid option");
1885         return NULL;
1886     }
1887 
1888     /*
1889      * IP_MULTICAST_LOOP socket option isn't available on Linux 2.2
1890      * kernel with IPv6 so return value stored in impl.
1891      */
1892 #if defined(AF_INET6) && defined(__linux__)
1893     if (isOldKernel && opt == java_net_SocketOptions_IP_MULTICAST_LOOP &&
1894         level == IPPROTO_IPV6) {
1895         int mode = (int)(*env)->GetBooleanField(env, this, pdsi_loopbackID);
1896         return createBoolean(env, mode);
1897     }
1898 #endif
1899 
1900     if (opt == java_net_SocketOptions_IP_MULTICAST_LOOP &&
1901         level == IPPROTO_IP) {
1902         optlen = sizeof(optval.c);
1903     } else {
1904         optlen = sizeof(optval.i);
1905     }
1906 
1907     if (NET_GetSockOpt(fd, level, optname, (void *)&optval, &optlen) < 0) {
1908         NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
1909                          "Error getting socket option");
1910         return NULL;
1911     }
1912 
1913     switch (opt) {
1914         case java_net_SocketOptions_IP_MULTICAST_LOOP:
1915             /* getLoopbackMode() returns true if IP_MULTICAST_LOOP disabled */
1916             if (level == IPPROTO_IP) {
1917                 return createBoolean(env, (int)!optval.c);
1918             } else {
1919                 return createBoolean(env, !optval.i);
1920             }
1921 
1922         case java_net_SocketOptions_SO_BROADCAST:
1923         case java_net_SocketOptions_SO_REUSEADDR:
1924             return createBoolean(env, optval.i);
1925 
1926         case java_net_SocketOptions_SO_SNDBUF:
1927         case java_net_SocketOptions_SO_RCVBUF:
1928         case java_net_SocketOptions_IP_TOS:
1929             return createInteger(env, optval.i);
1930 
1931     }
1932 
1933     /* should never rearch here */
1934     return NULL;
1935 }
1936 
1937 /*
1938  * Multicast-related calls
1939  */
1940 
1941 JNIEXPORT void JNICALL
1942 Java_java_net_PlainDatagramSocketImpl_setTTL(JNIEnv *env, jobject this,
1943                                              jbyte ttl) {
1944     jint ittl = ttl;
1945     if (ittl < 0) {
1946         ittl += 0x100;
1947     }
1948     Java_java_net_PlainDatagramSocketImpl_setTimeToLive(env, this, ittl);
1949 }
1950 
1951 /*
1952  * Set TTL for a socket. Throw exception if failed.
1953  */
1954 static void setTTL(JNIEnv *env, int fd, jint ttl) {
1955     char ittl = (char)ttl;
1956     if (JVM_SetSockOpt(fd, IPPROTO_IP, IP_MULTICAST_TTL, (char*)&ittl,
1957                        sizeof(ittl)) < 0) {
1958         NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
1959                        "Error setting socket option");
1960     }
1961 }
1962 
1963 /*
1964  * Set hops limit for a socket. Throw exception if failed.
1965  */
1966 #ifdef AF_INET6
1967 static void setHopLimit(JNIEnv *env, int fd, jint ttl) {
1968     int ittl = (int)ttl;
1969     if (JVM_SetSockOpt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
1970                        (char*)&ittl, sizeof(ittl)) < 0) {
1971         NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
1972                        "Error setting socket option");
1973     }
1974 }
1975 #endif
1976 
1977 /*
1978  * Class:     java_net_PlainDatagramSocketImpl
1979  * Method:    setTTL
1980  * Signature: (B)V
1981  */
1982 JNIEXPORT void JNICALL
1983 Java_java_net_PlainDatagramSocketImpl_setTimeToLive(JNIEnv *env, jobject this,
1984                                                     jint ttl) {
1985 
1986     jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
1987     int fd;
1988     /* it is important to cast this to a char, otherwise setsockopt gets confused */
1989 
1990     if (IS_NULL(fdObj)) {
1991         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
1992                         "Socket closed");
1993         return;
1994     } else {
1995         fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
1996     }
1997     /* setsockopt to be correct ttl */
1998 #ifdef AF_INET6
1999 #ifdef __solaris__
2000     if (ipv6_available()) {
2001         setHopLimit(env, fd, ttl);
2002     } else {
2003         setTTL(env, fd, ttl);
2004     }
2005 #endif
2006 #ifdef __linux__
2007     setTTL(env, fd, ttl);
2008     if (ipv6_available()) {
2009         setHopLimit(env, fd, ttl);
2010         if (isOldKernel) {
2011             (*env)->SetIntField(env, this, pdsi_ttlID, ttl);
2012         }
2013     }
2014 #endif  // __linux__
2015 #else
2016     setTTL(env, fd, ttl);
2017 #endif  /* AF_INET6 */
2018 }
2019 
2020 /*
2021  * Class:     java_net_PlainDatagramSocketImpl
2022  * Method:    getTTL
2023  * Signature: ()B
2024  */
2025 JNIEXPORT jbyte JNICALL
2026 Java_java_net_PlainDatagramSocketImpl_getTTL(JNIEnv *env, jobject this) {
2027     return (jbyte)Java_java_net_PlainDatagramSocketImpl_getTimeToLive(env, this);
2028 }
2029 
2030 
2031 /*
2032  * Class:     java_net_PlainDatagramSocketImpl
2033  * Method:    getTTL
2034  * Signature: ()B
2035  */
2036 JNIEXPORT jint JNICALL
2037 Java_java_net_PlainDatagramSocketImpl_getTimeToLive(JNIEnv *env, jobject this) {
2038 
2039     jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
2040     jint fd = -1;
2041 
2042     if (IS_NULL(fdObj)) {
2043         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
2044                         "Socket closed");
2045         return -1;
2046     } else {
2047         fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
2048     }
2049     /* getsockopt of ttl */
2050 #ifdef AF_INET6
2051     if (ipv6_available()) {
2052         int ttl = 0;
2053         int len = sizeof(ttl);
2054 
2055 #ifdef __linux__
2056         /*
2057          * Linux 2.2 kernel doesn't support IPV6_MULTICAST_HOPS socket option
2058          */
2059         if (isOldKernel) {
2060             return (*env)->GetIntField(env, this, pdsi_ttlID);
2061         }
2062 #endif
2063 
2064         if (JVM_GetSockOpt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
2065                                (char*)&ttl, &len) < 0) {
2066                 NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
2067                                "Error getting socket option");
2068                 return -1;
2069             }
2070         return (jint)ttl;
2071     } else
2072 #endif /* AF_INET6 */
2073         {
2074             u_char ttl = 0;
2075             int len = sizeof(ttl);
2076             if (JVM_GetSockOpt(fd, IPPROTO_IP, IP_MULTICAST_TTL,
2077                                (char*)&ttl, &len) < 0) {
2078                 NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
2079                                "Error getting socket option");
2080                 return -1;
2081             }
2082             return (jint)ttl;
2083         }
2084 }
2085 
2086 
2087 /*
2088  * mcast_join_leave: Join or leave a multicast group.
2089  *
2090  * For IPv4 sockets use IP_ADD_MEMBERSHIP/IP_DROP_MEMBERSHIP socket option
2091  * to join/leave multicast group.
2092  *
2093  * For IPv6 sockets use IPV6_ADD_MEMBERSHIP/IPV6_DROP_MEMBERSHIP socket option
2094  * to join/leave multicast group. If multicast group is an IPv4 address then
2095  * an IPv4-mapped address is used.
2096  *
2097  * On Linux with IPv6 if we wish to join/leave an IPv4 multicast group then
2098  * we must use the IPv4 socket options. This is because the IPv6 socket options
2099  * don't support IPv4-mapped addresses. This is true as per 2.2.19 and 2.4.7
2100  * kernel releases. In the future it's possible that IP_ADD_MEMBERSHIP
2101  * will be updated to return ENOPROTOOPT if uses with an IPv6 socket (Solaris
2102  * already does this). Thus to cater for this we first try with the IPv4
2103  * socket options and if they fail we use the IPv6 socket options. This
2104  * seems a reasonable failsafe solution.
2105  */
2106 static void mcast_join_leave(JNIEnv *env, jobject this,
2107                              jobject iaObj, jobject niObj,
2108                              jboolean join) {
2109 
2110     jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
2111     jint fd;
2112     jint ipv6_join_leave;
2113 
2114     if (IS_NULL(fdObj)) {
2115         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
2116                         "Socket closed");
2117         return;
2118     } else {
2119         fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
2120     }
2121     if (IS_NULL(iaObj)) {
2122         JNU_ThrowNullPointerException(env, "iaObj");
2123         return;
2124     }
2125 
2126     /*
2127      * Determine if this is an IPv4 or IPv6 join/leave.
2128      */
2129 #ifdef AF_INET6
2130     ipv6_join_leave = ipv6_available();
2131 
2132 #ifdef __linux__
2133     if ((*env)->GetIntField(env, iaObj, ia_familyID) == IPv4) {
2134         ipv6_join_leave = JNI_FALSE;
2135     }
2136 #endif
2137 
2138 #else
2139     /*
2140      * IPv6 not compiled in
2141      */
2142     ipv6_join_leave = JNI_FALSE;
2143 #endif
2144 
2145     /*
2146      * For IPv4 join use IP_ADD_MEMBERSHIP/IP_DROP_MEMBERSHIP socket option
2147      *
2148      * On Linux if IPv4 or IPv6 use IP_ADD_MEMBERSHIP/IP_DROP_MEMBERSHIP
2149      */
2150     if (!ipv6_join_leave) {
2151 #ifdef __linux__
2152         struct ip_mreqn mname;
2153 #else
2154         struct ip_mreq mname;
2155 #endif
2156         int mname_len;
2157 
2158         /*
2159          * joinGroup(InetAddress, NetworkInterface) implementation :-
2160          *
2161          * Linux/IPv6:  use ip_mreqn structure populated with multicast
2162          *              address and interface index.
2163          *
2164          * IPv4:        use ip_mreq structure populated with multicast
2165          *              address and first address obtained from
2166          *              NetworkInterface
2167          */
2168         if (niObj != NULL) {
2169 #if defined(__linux__) && defined(AF_INET6)
2170             if (ipv6_available()) {
2171                 static jfieldID ni_indexID;
2172 
2173                 if (ni_indexID == NULL) {
2174                     jclass c = (*env)->FindClass(env, "java/net/NetworkInterface");
2175                     CHECK_NULL(c);
2176                     ni_indexID = (*env)->GetFieldID(env, c, "index", "I");
2177                     CHECK_NULL(ni_indexID);
2178                 }
2179 
2180                 mname.imr_multiaddr.s_addr = htonl((*env)->GetIntField(env, iaObj, ia_addressID));
2181                 mname.imr_address.s_addr = 0;
2182                 mname.imr_ifindex =  (*env)->GetIntField(env, niObj, ni_indexID);
2183                 mname_len = sizeof(struct ip_mreqn);
2184             } else
2185 #endif
2186             {
2187                 jobjectArray addrArray = (*env)->GetObjectField(env, niObj, ni_addrsID);
2188                 jobject addr;
2189 
2190                 if ((*env)->GetArrayLength(env, addrArray) < 1) {
2191                     JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
2192                         "bad argument for IP_ADD_MEMBERSHIP: "
2193                         "No IP addresses bound to interface");
2194                     return;
2195                 }
2196                 addr = (*env)->GetObjectArrayElement(env, addrArray, 0);
2197 
2198                 mname.imr_multiaddr.s_addr = htonl((*env)->GetIntField(env, iaObj, ia_addressID));
2199 #ifdef __linux__
2200                 mname.imr_address.s_addr = htonl((*env)->GetIntField(env, addr, ia_addressID));
2201 #else
2202                 mname.imr_interface.s_addr = htonl((*env)->GetIntField(env, addr, ia_addressID));
2203 #endif
2204                 mname_len = sizeof(struct ip_mreq);
2205             }
2206         }
2207 
2208 
2209         /*
2210          * joinGroup(InetAddress) implementation :-
2211          *
2212          * Linux/IPv6:  use ip_mreqn structure populated with multicast
2213          *              address and interface index. index obtained
2214          *              from cached value or IPV6_MULTICAST_IF.
2215          *
2216          * IPv4:        use ip_mreq structure populated with multicast
2217          *              address and local address obtained from
2218          *              IP_MULTICAST_IF. On Linux IP_MULTICAST_IF
2219          *              returns different structure depending on
2220          *              kernel.
2221          */
2222 
2223         if (niObj == NULL) {
2224 
2225 #if defined(__linux__) && defined(AF_INET6)
2226             if (ipv6_available()) {
2227 
2228                 int index;
2229                 int len = sizeof(index);
2230 
2231                 if (isOldKernel) {
2232                     index = (*env)->GetIntField(env, this, pdsi_multicastInterfaceID);
2233                 } else {
2234                     if (JVM_GetSockOpt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF,
2235                                        (char*)&index, &len) < 0) {
2236                         NET_ThrowCurrent(env, "getsockopt IPV6_MULTICAST_IF failed");
2237                         return;
2238                     }
2239                 }
2240 
2241                 mname.imr_multiaddr.s_addr = htonl((*env)->GetIntField(env, iaObj, ia_addressID));
2242                 mname.imr_address.s_addr = 0 ;
2243                 mname.imr_ifindex = index;
2244                 mname_len = sizeof(struct ip_mreqn);
2245             } else
2246 #endif
2247             {
2248                 struct in_addr in;
2249                 struct in_addr *inP = &in;
2250                 int len = sizeof(struct in_addr);
2251 
2252 #ifdef __linux__
2253                 struct ip_mreqn mreqn;
2254                 if (isOldKernel) {
2255                     inP = (struct in_addr *)&mreqn;
2256                     len = sizeof(struct ip_mreqn);
2257                 }
2258 #endif
2259                 if (getsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, (char *)inP, &len) < 0) {
2260                     NET_ThrowCurrent(env, "getsockopt IP_MULTICAST_IF failed");
2261                     return;
2262                 }
2263 
2264 #ifdef __linux__
2265                 mname.imr_address.s_addr =
2266                     (isOldKernel ? mreqn.imr_address.s_addr : in.s_addr);
2267 
2268 #else
2269                 mname.imr_interface.s_addr = in.s_addr;
2270 #endif
2271                 mname.imr_multiaddr.s_addr = htonl((*env)->GetIntField(env, iaObj, ia_addressID));
2272                 mname_len = sizeof(struct ip_mreq);
2273             }
2274         }
2275 
2276 
2277         /*
2278          * Join the multicast group.
2279          */
2280         if (JVM_SetSockOpt(fd, IPPROTO_IP, (join ? IP_ADD_MEMBERSHIP:IP_DROP_MEMBERSHIP),
2281                            (char *) &mname, mname_len) < 0) {
2282 
2283             /*
2284              * If IP_ADD_MEMBERSHIP returns ENOPROTOOPT on Linux and we've got
2285              * IPv6 enabled then it's possible that the kernel has been fixed
2286              * so we switch to IPV6_ADD_MEMBERSHIP socket option.
2287              * As of 2.4.7 kernel IPV6_ADD_MEMERSHIP can't handle IPv4-mapped
2288              * addresses so we have to use IP_ADD_MEMERSHIP for IPv4 multicast
2289              * groups. However if the socket is an IPv6 socket then then setsockopt
2290              * should reurn ENOPROTOOPT. We assume this will be fixed in Linux
2291              * at some stage.
2292              */
2293 #if defined(__linux__) && defined(AF_INET6)
2294             if (errno == ENOPROTOOPT) {
2295                 if (ipv6_available()) {
2296                     ipv6_join_leave = JNI_TRUE;
2297                     errno = 0;
2298                 } else  {
2299                     errno = ENOPROTOOPT;    /* errno can be changed by ipv6_available */
2300                 }
2301             }
2302 #endif
2303             if (errno) {
2304                 if (join) {
2305                     NET_ThrowCurrent(env, "setsockopt IP_ADD_MEMBERSHIP failed");
2306                 } else {
2307                     if (errno == ENOENT)
2308                         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
2309                             "Not a member of the multicast group");
2310                     else
2311                         NET_ThrowCurrent(env, "setsockopt IP_DROP_MEMBERSHIP failed");
2312                 }
2313             }
2314         }
2315 
2316         /*
2317          * If we haven't switched to IPv6 socket option then we're done.
2318          */
2319         if (!ipv6_join_leave) {
2320             return;
2321         }
2322     }
2323 
2324 
2325     /*
2326      * IPv6 join. If it's an IPv4 multicast group then we use an IPv4-mapped
2327      * address.
2328      */
2329 #ifdef AF_INET6
2330     {
2331         struct ipv6_mreq mname6;
2332         jbyteArray ipaddress;
2333         jbyte caddr[16];
2334         jint family;
2335         jint address;
2336         family = (*env)->GetIntField(env, iaObj, ia_familyID) == IPv4? AF_INET : AF_INET6;
2337         if (family == AF_INET) { /* will convert to IPv4-mapped address */
2338             memset((char *) caddr, 0, 16);
2339             address = (*env)->GetIntField(env, iaObj, ia_addressID);
2340 
2341             caddr[10] = 0xff;
2342             caddr[11] = 0xff;
2343 
2344             caddr[12] = ((address >> 24) & 0xff);
2345             caddr[13] = ((address >> 16) & 0xff);
2346             caddr[14] = ((address >> 8) & 0xff);
2347             caddr[15] = (address & 0xff);
2348         } else {
2349             ipaddress = (*env)->GetObjectField(env, iaObj, ia6_ipaddressID);
2350             (*env)->GetByteArrayRegion(env, ipaddress, 0, 16, caddr);
2351         }
2352 
2353         memcpy((void *)&(mname6.ipv6mr_multiaddr), caddr, sizeof(struct in6_addr));
2354         if (IS_NULL(niObj)) {
2355             int index;
2356             int len = sizeof(index);
2357 
2358 #ifdef __linux__
2359             /*
2360              * 2.2 kernel doens't support IPV6_MULTICAST_IF socket option
2361              */
2362             if (isOldKernel) {
2363                 index = (*env)->GetIntField(env, this, pdsi_multicastInterfaceID);
2364             } else
2365 #endif
2366             {
2367                 if (JVM_GetSockOpt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF,
2368                                  (char*)&index, &len) < 0) {
2369                     NET_ThrowCurrent(env, "getsockopt IPV6_MULTICAST_IF failed");
2370                     return;
2371                 }
2372             }
2373 
2374 #ifdef __linux__
2375             /*
2376              * On 2.4.8+ if we join a group with the interface set to 0
2377              * then the kernel records the interface it decides. This causes
2378              * subsequent leave groups to fail as there is no match. Thus we
2379              * pick the interface if there is a matching route.
2380              */
2381             if (index == 0 && !isOldKernel) {
2382                 int rt_index = getDefaultIPv6Interface(&(mname6.ipv6mr_multiaddr));
2383                 if (rt_index > 0) {
2384                     index = rt_index;
2385                 }
2386             }
2387 #endif
2388 
2389             mname6.ipv6mr_interface = index;
2390         } else {
2391             jint idx = (*env)->GetIntField(env, niObj, ni_indexID);
2392             mname6.ipv6mr_interface = idx;
2393         }
2394 
2395         /* Join the multicast group */
2396         if (JVM_SetSockOpt(fd, IPPROTO_IPV6, (join ? IPV6_ADD_MEMBERSHIP : IPV6_DROP_MEMBERSHIP),
2397                            (char *) &mname6, sizeof (mname6)) < 0) {
2398 
2399             if (join) {
2400                 NET_ThrowCurrent(env, "setsockopt IPV6_ADD_MEMBERSHIP failed");
2401             } else {
2402                 if (errno == ENOENT) {
2403                    JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
2404                         "Not a member of the multicast group");
2405                 } else {
2406                     NET_ThrowCurrent(env, "setsockopt IPV6_DROP_MEMBERSHIP failed");
2407                 }
2408             }
2409         }
2410     }
2411 #endif
2412 }
2413 
2414 /*
2415  * Class:     java_net_PlainDatagramSocketImpl
2416  * Method:    join
2417  * Signature: (Ljava/net/InetAddress;)V
2418  */
2419 JNIEXPORT void JNICALL
2420 Java_java_net_PlainDatagramSocketImpl_join(JNIEnv *env, jobject this,
2421                                            jobject iaObj, jobject niObj)
2422 {
2423     mcast_join_leave(env, this, iaObj, niObj, JNI_TRUE);
2424 }
2425 
2426 /*
2427  * Class:     java_net_PlainDatagramSocketImpl
2428  * Method:    leave
2429  * Signature: (Ljava/net/InetAddress;)V
2430  */
2431 JNIEXPORT void JNICALL
2432 Java_java_net_PlainDatagramSocketImpl_leave(JNIEnv *env, jobject this,
2433                                             jobject iaObj, jobject niObj)
2434 {
2435     mcast_join_leave(env, this, iaObj, niObj, JNI_FALSE);
2436 }
--- EOF ---