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 static jclass i_class;
  87 static jmethodID i_ctrID;
  88 static jfieldID i_valueID;



  89 
  90 int initIntegerIDs(JNIEnv* env) {
  91     if (i_class == NULL) {
  92         jclass c = (*env)->FindClass(env, "java/lang/Integer");
  93         CHECK_NULL_RETURN(c, NULL);
  94         i_ctrID = (*env)->GetMethodID(env, c, "<init>", "(I)V");
  95         CHECK_NULL_RETURN(i_ctrID, NULL);
  96         i_valueID = (*env)->GetFieldID(env, c, "value", "I");
  97         CHECK_NULL_RETURN(i_valueID, NULL);
  98         i_class = (*env)->NewGlobalRef(env, c);
  99         CHECK_NULL_RETURN(i_class, NULL);
 100     }
 101     return 1;
 102 }
 103 
 104 /* Returns a java.lang.Integer based on 'i' */
 105 static jobject createInteger(JNIEnv *env, int i) {
 106     CHECK_NULL_RETURN(initIntegerIDs(env), NULL);
 107     return ( (*env)->NewObject(env, i_class, i_ctrID, i) );
 108 }
 109 
 110 /* Returns a jint based on the given java.lang.Integer */
 111 static jint retrieveInteger(JNIEnv *env, jobject i) {
 112     CHECK_NULL_RETURN(initIntegerIDs(env), NULL);
 113     return (*env)->GetIntField(env, i, i_valueID);
 114 }

 115 
 116 static jclass b_class;
 117 static jmethodID b_ctrID;
 118 static jfieldID b_valueID;
 119 
 120 int initBooleanIDs(JNIEnv* env) {
 121     if (b_class == NULL) {
 122         jclass c = (*env)->FindClass(env, "java/lang/Boolean");
 123         CHECK_NULL_RETURN(c, NULL);
 124         b_ctrID = (*env)->GetMethodID(env, c, "<init>", "(Z)V");
 125         CHECK_NULL_RETURN(b_ctrID, NULL);
 126         b_valueID = (*env)->GetFieldID(env, c, "value", "Z");
 127         CHECK_NULL_RETURN(b_valueID, NULL);
 128         b_class = (*env)->NewGlobalRef(env, c);
 129         CHECK_NULL_RETURN(b_class, NULL);
 130     }
 131     return 1;
 132 }
 133 
 134 /* Returns a java.lang.Boolean based on 'b' */
 135 static jobject createBoolean(JNIEnv *env, int b) {
 136     CHECK_NULL_RETURN(initBooleanIDs(env), NULL);
 137     return( (*env)->NewObject(env, b_class, b_ctrID, (jboolean)(b!=0)) );
 138 }
 139 
 140 /* Returns a jboolean based on the given java.lang.Boolean */
 141 static jboolean retrieveBoolean(JNIEnv *env, jobject b) {
 142     CHECK_NULL_RETURN(initBooleanIDs(env), NULL);
 143     return (*env)->GetBooleanField(env, b, b_valueID);
 144 }
 145 
 146 /*
 147  * Returns the fd for a PlainDatagramSocketImpl or -1
 148  * if closed.
 149  */
 150 static int getFD(JNIEnv *env, jobject this) {
 151     jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
 152     if (fdObj == NULL) {
 153         return -1;
 154     }
 155     return (*env)->GetIntField(env, fdObj, IO_fd_fdID);
 156 }
 157 
 158 
 159 /*
 160  * Class:     java_net_PlainDatagramSocketImpl
 161  * Method:    init
 162  * Signature: ()V
 163  */
 164 JNIEXPORT void JNICALL
 165 Java_java_net_PlainDatagramSocketImpl_init(JNIEnv *env, jclass cls) {
 166 
 167 #ifdef __linux__
 168     struct utsname sysinfo;
 169 #endif
 170     pdsi_fdID = (*env)->GetFieldID(env, cls, "fd",
 171                                    "Ljava/io/FileDescriptor;");
 172     CHECK_NULL(pdsi_fdID);
 173     pdsi_timeoutID = (*env)->GetFieldID(env, cls, "timeout", "I");
 174     CHECK_NULL(pdsi_timeoutID);
 175     pdsi_trafficClassID = (*env)->GetFieldID(env, cls, "trafficClass", "I");
 176     CHECK_NULL(pdsi_trafficClassID);
 177     pdsi_localPortID = (*env)->GetFieldID(env, cls, "localPort", "I");
 178     CHECK_NULL(pdsi_localPortID);
 179     pdsi_connected = (*env)->GetFieldID(env, cls, "connected", "Z");
 180     CHECK_NULL(pdsi_connected);
 181     pdsi_connectedAddress = (*env)->GetFieldID(env, cls, "connectedAddress",
 182                                                "Ljava/net/InetAddress;");
 183     CHECK_NULL(pdsi_connectedAddress);
 184     pdsi_connectedPort = (*env)->GetFieldID(env, cls, "connectedPort", "I");
 185     CHECK_NULL(pdsi_connectedPort);
 186 
 187     IO_fd_fdID = NET_GetFileDescriptorID(env);
 188     CHECK_NULL(IO_fd_fdID);
 189 
 190     Java_java_net_InetAddress_init(env, 0);
 191     Java_java_net_Inet4Address_init(env, 0);
 192     Java_java_net_Inet6Address_init(env, 0);
 193     Java_java_net_NetworkInterface_init(env, 0);
 194 
 195 #ifdef __linux__
 196     /*
 197      * We need to determine if this is a 2.2 kernel.
 198      */
 199     if (uname(&sysinfo) == 0) {
 200         sysinfo.release[3] = '\0';
 201         isOldKernel = (strcmp(sysinfo.release, "2.2") == 0);
 202     } else {
 203         /*
 204          * uname failed - move to plan B and examine /proc/version
 205          * If this fails assume that /proc has changed and that
 206          * this must be new /proc format and hence new kernel.
 207          */
 208         FILE *fP;
 209         isOldKernel = JNI_FALSE;
 210         if ((fP = fopen("/proc/version", "r")) != NULL) {
 211             char ver[25];
 212             if (fgets(ver, sizeof(ver), fP) != NULL) {
 213                 isOldKernel = (strstr(ver, "2.2.") != NULL);
 214             }
 215             fclose(fP);
 216         }
 217     }
 218 
 219 #ifdef AF_INET6
 220     pdsi_multicastInterfaceID = (*env)->GetFieldID(env, cls, "multicastInterface", "I");
 221     CHECK_NULL(pdsi_multicastInterfaceID);
 222     pdsi_loopbackID = (*env)->GetFieldID(env, cls, "loopbackMode", "Z");
 223     CHECK_NULL(pdsi_loopbackID);
 224     pdsi_ttlID = (*env)->GetFieldID(env, cls, "ttl", "I");
 225     CHECK_NULL(pdsi_ttlID);
 226 #endif
 227 
 228 #endif
 229 
 230 }
 231 
 232 /*
 233  * Class:     java_net_PlainDatagramSocketImpl
 234  * Method:    bind
 235  * Signature: (ILjava/net/InetAddress;)V
 236  */
 237 JNIEXPORT void JNICALL
 238 Java_java_net_PlainDatagramSocketImpl_bind0(JNIEnv *env, jobject this,
 239                                            jint localport, jobject iaObj) {
 240     /* fdObj is the FileDescriptor field on this */
 241     jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
 242     /* fd is an int field on fdObj */
 243     int fd;
 244     int len = 0;
 245     SOCKADDR him;
 246 
 247     if (IS_NULL(fdObj)) {
 248         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
 249                         "Socket closed");
 250         return;
 251     } else {
 252         fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
 253     }
 254 
 255     if (IS_NULL(iaObj)) {
 256         JNU_ThrowNullPointerException(env, "iaObj is null.");
 257         return;
 258     }
 259 
 260     /* bind */
 261     if (NET_InetAddressToSockaddr(env, iaObj, localport, (struct sockaddr *)&him, &len, JNI_TRUE) != 0) {
 262       return;
 263     }
 264 
 265     if (NET_Bind(fd, (struct sockaddr *)&him, len) < 0)  {
 266         if (errno == EADDRINUSE || errno == EADDRNOTAVAIL ||
 267             errno == EPERM || errno == EACCES) {
 268             NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "BindException",
 269                             "Bind failed");
 270         } else {
 271             NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
 272                             "Bind failed");
 273         }
 274         return;
 275     }
 276 
 277     /* intialize the local port */
 278     if (localport == 0) {
 279         /* Now that we're a connected socket, let's extract the port number
 280          * that the system chose for us and store it in the Socket object.
 281          */
 282         if (JVM_GetSockName(fd, (struct sockaddr *)&him, &len) == -1) {
 283             NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
 284                             "Error getting socket name");
 285             return;
 286         }
 287 
 288         localport = NET_GetPortFromSockaddr((struct sockaddr *)&him);
 289 
 290         (*env)->SetIntField(env, this, pdsi_localPortID, localport);
 291     } else {
 292         (*env)->SetIntField(env, this, pdsi_localPortID, localport);
 293     }
 294 }
 295 
 296 /*
 297  * Class:     java_net_PlainDatagramSocketImpl
 298  * Method:    connect0
 299  * Signature: (Ljava/net/InetAddress;I)V
 300  */
 301 JNIEXPORT void JNICALL
 302 Java_java_net_PlainDatagramSocketImpl_connect0(JNIEnv *env, jobject this,
 303                                                jobject address, jint port) {
 304     /* The object's field */
 305     jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
 306     /* The fdObj'fd */
 307     jint fd;
 308     /* The packetAddress address, family and port */
 309     SOCKADDR rmtaddr;
 310     int len = 0;
 311 
 312     if (IS_NULL(fdObj)) {
 313         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
 314                         "Socket closed");
 315         return;
 316     }
 317     fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
 318 
 319     if (IS_NULL(address)) {
 320         JNU_ThrowNullPointerException(env, "address");
 321         return;
 322     }
 323 
 324     if (NET_InetAddressToSockaddr(env, address, port, (struct sockaddr *)&rmtaddr, &len, JNI_TRUE) != 0) {
 325       return;
 326     }
 327 
 328 #ifdef __linux__
 329     if (isOldKernel) {
 330         int t = 0;
 331         setsockopt(fd, SOL_SOCKET, SO_BSDCOMPAT, (char*) &t, sizeof(int));
 332     } else
 333 #endif
 334     {
 335         if (JVM_Connect(fd, (struct sockaddr *)&rmtaddr, len) == -1) {
 336             NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "ConnectException",
 337                             "Connect failed");
 338             return;
 339         }
 340     }
 341 }
 342 
 343 /*
 344  * Class:     java_net_PlainDatagramSocketImpl
 345  * Method:    disconnect0
 346  * Signature: ()V
 347  */
 348 JNIEXPORT void JNICALL
 349 Java_java_net_PlainDatagramSocketImpl_disconnect0(JNIEnv *env, jobject this, jint family) {
 350     /* The object's field */
 351     jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
 352     /* The fdObj'fd */
 353     jint fd;
 354 
 355 #ifdef __linux__
 356     SOCKADDR addr;
 357     int len;
 358 #endif
 359 
 360     if (IS_NULL(fdObj)) {
 361         return;
 362     }
 363     fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
 364 
 365 #ifdef __linux__
 366     if (isOldKernel) {
 367         int t = 1;
 368         setsockopt(fd, SOL_SOCKET, SO_BSDCOMPAT, (char*) &t, sizeof(int));
 369     } else {
 370         memset(&addr, 0, sizeof(addr));
 371 #ifdef AF_INET6
 372         if (ipv6_available()) {
 373             struct sockaddr_in6 *him6 = (struct sockaddr_in6 *)&addr;
 374             him6->sin6_family = AF_UNSPEC;
 375             len = sizeof(struct sockaddr_in6);
 376         } else
 377 #endif
 378         {
 379             struct sockaddr_in *him4 = (struct sockaddr_in*)&addr;
 380             him4->sin_family = AF_UNSPEC;
 381             len = sizeof(struct sockaddr_in);
 382         }
 383         JVM_Connect(fd, (struct sockaddr *)&addr, len);
 384 
 385         // After disconnecting a UDP socket, Linux kernel will set
 386         // local port to zero if the port number comes from implicit
 387         // bind. Successive send/recv on the same socket will fail.
 388         // So bind again with former port number here.
 389         int localPort = 0;
 390         if (JVM_GetSockName(fd, (struct sockaddr *)&addr, &len) == -1) {
 391             return;
 392         }
 393         localPort = NET_GetPortFromSockaddr((struct sockaddr *)&addr);
 394         if (localPort == 0) {
 395             localPort = (*env)->GetIntField(env, this, pdsi_localPortID);
 396 #ifdef AF_INET6
 397             if (((struct sockaddr*)&addr)->sa_family == AF_INET6) {
 398                 ((struct sockaddr_in6*)&addr)->sin6_port = htons(localPort);
 399             } else
 400 #endif /* AF_INET6 */
 401             {
 402                 ((struct sockaddr_in*)&addr)->sin_port = htons(localPort);
 403             }
 404             NET_Bind(fd, (struct sockaddr *)&addr, len);
 405         }
 406     }
 407 #else
 408     JVM_Connect(fd, 0, 0);
 409 #endif
 410 }
 411 
 412 /*
 413  * Class:     java_net_PlainDatagramSocketImpl
 414  * Method:    send
 415  * Signature: (Ljava/net/DatagramPacket;)V
 416  */
 417 JNIEXPORT void JNICALL
 418 Java_java_net_PlainDatagramSocketImpl_send(JNIEnv *env, jobject this,
 419                                            jobject packet) {
 420 
 421     char BUF[MAX_BUFFER_LEN];
 422     char *fullPacket = NULL;
 423     int ret, mallocedPacket = JNI_FALSE;
 424     /* The object's field */
 425     jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
 426     jint trafficClass = (*env)->GetIntField(env, this, pdsi_trafficClassID);
 427 
 428     jbyteArray packetBuffer;
 429     jobject packetAddress;
 430     jint packetBufferOffset, packetBufferLen, packetPort;
 431     jboolean connected;
 432 
 433     /* The fdObj'fd */
 434     jint fd;
 435 
 436     SOCKADDR rmtaddr, *rmtaddrP=&rmtaddr;
 437     int len;
 438 
 439     if (IS_NULL(fdObj)) {
 440         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
 441                         "Socket closed");
 442         return;
 443     }
 444     fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
 445 
 446     if (IS_NULL(packet)) {
 447         JNU_ThrowNullPointerException(env, "packet");
 448         return;
 449     }
 450 
 451     connected = (*env)->GetBooleanField(env, this, pdsi_connected);
 452 
 453     packetBuffer = (*env)->GetObjectField(env, packet, dp_bufID);
 454     packetAddress = (*env)->GetObjectField(env, packet, dp_addressID);
 455     if (IS_NULL(packetBuffer) || IS_NULL(packetAddress)) {
 456         JNU_ThrowNullPointerException(env, "null buffer || null address");
 457         return;
 458     }
 459 
 460     packetBufferOffset = (*env)->GetIntField(env, packet, dp_offsetID);
 461     packetBufferLen = (*env)->GetIntField(env, packet, dp_lengthID);
 462 
 463 #ifdef __linux__
 464     if (connected && !isOldKernel) {
 465 #else
 466     if (connected) {
 467 #endif
 468         /* arg to NET_Sendto () null in this case */
 469         len = 0;
 470         rmtaddrP = 0;
 471     } else {
 472         packetPort = (*env)->GetIntField(env, packet, dp_portID);
 473         if (NET_InetAddressToSockaddr(env, packetAddress, packetPort, (struct sockaddr *)&rmtaddr, &len, JNI_TRUE) != 0) {
 474           return;
 475         }
 476     }
 477 
 478     if (packetBufferLen > MAX_BUFFER_LEN) {
 479         /* When JNI-ifying the JDK's IO routines, we turned
 480          * read's and write's of byte arrays of size greater
 481          * than 2048 bytes into several operations of size 2048.
 482          * This saves a malloc()/memcpy()/free() for big
 483          * buffers.  This is OK for file IO and TCP, but that
 484          * strategy violates the semantics of a datagram protocol.
 485          * (one big send) != (several smaller sends).  So here
 486          * we *must* alloc the buffer.  Note it needn't be bigger
 487          * than 65,536 (0xFFFF) the max size of an IP packet.
 488          * Anything bigger should be truncated anyway.
 489          *
 490          * We may want to use a smarter allocation scheme at some
 491          * point.
 492          */
 493         if (packetBufferLen > MAX_PACKET_LEN) {
 494             packetBufferLen = MAX_PACKET_LEN;
 495         }
 496         fullPacket = (char *)malloc(packetBufferLen);
 497 
 498         if (!fullPacket) {
 499             JNU_ThrowOutOfMemoryError(env, "heap allocation failed");
 500             return;
 501         } else {
 502             mallocedPacket = JNI_TRUE;
 503         }
 504     } else {
 505         fullPacket = &(BUF[0]);
 506     }
 507 
 508     (*env)->GetByteArrayRegion(env, packetBuffer, packetBufferOffset, packetBufferLen,
 509                                (jbyte *)fullPacket);
 510 #ifdef AF_INET6
 511     if (trafficClass != 0 && ipv6_available()) {
 512         NET_SetTrafficClass((struct sockaddr *)&rmtaddr, trafficClass);
 513     }
 514 #endif /* AF_INET6 */
 515 
 516 
 517     /*
 518      * Send the datagram.
 519      *
 520      * If we are connected it's possible that sendto will return
 521      * ECONNREFUSED indicating that an ICMP port unreachable has
 522      * received.
 523      */
 524     ret = NET_SendTo(fd, fullPacket, packetBufferLen, 0,
 525                      (struct sockaddr *)rmtaddrP, len);
 526 
 527     if (ret < 0) {
 528         switch (ret) {
 529             case JVM_IO_ERR :
 530                 if (errno == ECONNREFUSED) {
 531                     JNU_ThrowByName(env, JNU_JAVANETPKG "PortUnreachableException",
 532                             "ICMP Port Unreachable");
 533                 } else {
 534                     NET_ThrowByNameWithLastError(env, "java/io/IOException", "sendto failed");
 535                 }
 536                 break;
 537 
 538             case JVM_IO_INTR:
 539                 JNU_ThrowByName(env, "java/io/InterruptedIOException",
 540                                 "operation interrupted");
 541                 break;
 542         }
 543     }
 544 
 545     if (mallocedPacket) {
 546         free(fullPacket);
 547     }
 548     return;
 549 }
 550 
 551 /*
 552  * Class:     java_net_PlainDatagramSocketImpl
 553  * Method:    peek
 554  * Signature: (Ljava/net/InetAddress;)I
 555  */
 556 JNIEXPORT jint JNICALL
 557 Java_java_net_PlainDatagramSocketImpl_peek(JNIEnv *env, jobject this,
 558                                            jobject addressObj) {
 559 
 560     jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
 561     jint timeout = (*env)->GetIntField(env, this, pdsi_timeoutID);
 562     jint fd;
 563     ssize_t n;
 564     SOCKADDR remote_addr;
 565     int len;
 566     char buf[1];
 567     jint family;
 568     jobject iaObj;
 569     int port;
 570     if (IS_NULL(fdObj)) {
 571         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");
 572         return -1;
 573     } else {
 574         fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
 575     }
 576     if (IS_NULL(addressObj)) {
 577         JNU_ThrowNullPointerException(env, "Null address in peek()");
 578     }
 579     if (timeout) {
 580         int ret = NET_Timeout(fd, timeout);
 581         if (ret == 0) {
 582             JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException",
 583                             "Peek timed out");
 584             return ret;
 585         } else if (ret == JVM_IO_ERR) {
 586             if (errno == EBADF) {
 587                  JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");
 588             } else {
 589                  NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "Peek failed");
 590             }
 591             return ret;
 592         } else if (ret == JVM_IO_INTR) {
 593             JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException",
 594                             "operation interrupted");
 595             return ret; /* WARNING: SHOULD WE REALLY RETURN -2??? */
 596         }
 597     }
 598 
 599     len = SOCKADDR_LEN;
 600     n = NET_RecvFrom(fd, buf, 1, MSG_PEEK,
 601                      (struct sockaddr *)&remote_addr, &len);
 602 
 603     if (n == JVM_IO_ERR) {
 604 
 605 #ifdef __solaris__
 606         if (errno == ECONNREFUSED) {
 607             int orig_errno = errno;
 608             (void) recv(fd, buf, 1, 0);
 609             errno = orig_errno;
 610         }
 611 #endif
 612         if (errno == ECONNREFUSED) {
 613             JNU_ThrowByName(env, JNU_JAVANETPKG "PortUnreachableException",
 614                             "ICMP Port Unreachable");
 615         } else {
 616             if (errno == EBADF) {
 617                  JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");
 618             } else {
 619                  NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "Peek failed");
 620             }
 621         }
 622         return 0;
 623     } else if (n == JVM_IO_INTR) {
 624         JNU_ThrowByName(env, "java/io/InterruptedIOException", 0);
 625         return 0;
 626     }
 627 
 628     iaObj = NET_SockaddrToInetAddress(env, (struct sockaddr *)&remote_addr, &port);
 629 #ifdef AF_INET6
 630     family = (*env)->GetIntField(env, iaObj, ia_familyID) == IPv4?
 631         AF_INET : AF_INET6;
 632 #else
 633     family = AF_INET;
 634 #endif
 635     if (family == AF_INET) { /* this api can't handle IPV6 addresses */
 636         int address = (*env)->GetIntField(env, iaObj, ia_addressID);
 637         (*env)->SetIntField(env, addressObj, ia_addressID, address);
 638     }
 639     return port;
 640 }
 641 
 642 JNIEXPORT jint JNICALL
 643 Java_java_net_PlainDatagramSocketImpl_peekData(JNIEnv *env, jobject this,
 644                                            jobject packet) {
 645 
 646     char BUF[MAX_BUFFER_LEN];
 647     char *fullPacket = NULL;
 648     int mallocedPacket = JNI_FALSE;
 649     jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
 650     jint timeout = (*env)->GetIntField(env, this, pdsi_timeoutID);
 651 
 652     jbyteArray packetBuffer;
 653     jint packetBufferOffset, packetBufferLen;
 654 
 655     int fd;
 656 
 657     int n;
 658     SOCKADDR remote_addr;
 659     int len;
 660     int port;
 661 
 662     if (IS_NULL(fdObj)) {
 663         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
 664                         "Socket closed");
 665         return -1;
 666     }
 667 
 668     fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
 669 
 670     if (IS_NULL(packet)) {
 671         JNU_ThrowNullPointerException(env, "packet");
 672         return -1;
 673     }
 674 
 675     packetBuffer = (*env)->GetObjectField(env, packet, dp_bufID);
 676     if (IS_NULL(packetBuffer)) {
 677         JNU_ThrowNullPointerException(env, "packet buffer");
 678         return -1;
 679     }
 680     packetBufferOffset = (*env)->GetIntField(env, packet, dp_offsetID);
 681     packetBufferLen = (*env)->GetIntField(env, packet, dp_bufLengthID);
 682     if (timeout) {
 683         int ret = NET_Timeout(fd, timeout);
 684         if (ret == 0) {
 685             JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException",
 686                             "Receive timed out");
 687             return -1;
 688         } else if (ret == JVM_IO_ERR) {
 689 #ifdef __linux__
 690             if (errno == EBADF) {
 691                 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");
 692             } else {
 693                 NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "Receive failed");
 694             }
 695 #else
 696             JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");
 697 #endif
 698             return -1;
 699         } else if (ret == JVM_IO_INTR) {
 700             JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException",
 701                             "operation interrupted");
 702             return -1;
 703         }
 704     }
 705 
 706     if (packetBufferLen > MAX_BUFFER_LEN) {
 707 
 708         /* When JNI-ifying the JDK's IO routines, we turned
 709          * read's and write's of byte arrays of size greater
 710          * than 2048 bytes into several operations of size 2048.
 711          * This saves a malloc()/memcpy()/free() for big
 712          * buffers.  This is OK for file IO and TCP, but that
 713          * strategy violates the semantics of a datagram protocol.
 714          * (one big send) != (several smaller sends).  So here
 715          * we *must* alloc the buffer.  Note it needn't be bigger
 716          * than 65,536 (0xFFFF) the max size of an IP packet.
 717          * anything bigger is truncated anyway.
 718          *
 719          * We may want to use a smarter allocation scheme at some
 720          * point.
 721          */
 722         if (packetBufferLen > MAX_PACKET_LEN) {
 723             packetBufferLen = MAX_PACKET_LEN;
 724         }
 725         fullPacket = (char *)malloc(packetBufferLen);
 726 
 727         if (!fullPacket) {
 728             JNU_ThrowOutOfMemoryError(env, "heap allocation failed");
 729             return -1;
 730         } else {
 731             mallocedPacket = JNI_TRUE;
 732         }
 733     } else {
 734         fullPacket = &(BUF[0]);
 735     }
 736 
 737     len = SOCKADDR_LEN;
 738     n = NET_RecvFrom(fd, fullPacket, packetBufferLen, MSG_PEEK,
 739                      (struct sockaddr *)&remote_addr, &len);
 740     /* truncate the data if the packet's length is too small */
 741     if (n > packetBufferLen) {
 742         n = packetBufferLen;
 743     }
 744     if (n == JVM_IO_ERR) {
 745 
 746 #ifdef __solaris__
 747         if (errno == ECONNREFUSED) {
 748             int orig_errno = errno;
 749             (void) recv(fd, fullPacket, 1, 0);
 750             errno = orig_errno;
 751         }
 752 #endif
 753         (*env)->SetIntField(env, packet, dp_offsetID, 0);
 754         (*env)->SetIntField(env, packet, dp_lengthID, 0);
 755         if (errno == ECONNREFUSED) {
 756             JNU_ThrowByName(env, JNU_JAVANETPKG "PortUnreachableException",
 757                             "ICMP Port Unreachable");
 758         } else {
 759             if (errno == EBADF) {
 760                  JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");
 761             } else {
 762                  NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "Receive failed");
 763             }
 764         }
 765     } else if (n == JVM_IO_INTR) {
 766         (*env)->SetIntField(env, packet, dp_offsetID, 0);
 767         (*env)->SetIntField(env, packet, dp_lengthID, 0);
 768         JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException",
 769                         "operation interrupted");
 770     } else {
 771         /*
 772          * success - fill in received address...
 773          *
 774          * REMIND: Fill in an int on the packet, and create inetadd
 775          * object in Java, as a performance improvement. Also
 776          * construct the inetadd object lazily.
 777          */
 778 
 779         jobject packetAddress;
 780 
 781         /*
 782          * Check if there is an InetAddress already associated with this
 783          * packet. If so we check if it is the same source address. We
 784          * can't update any existing InetAddress because it is immutable
 785          */
 786         packetAddress = (*env)->GetObjectField(env, packet, dp_addressID);
 787         if (packetAddress != NULL) {
 788             if (!NET_SockaddrEqualsInetAddress(env, (struct sockaddr *)&remote_addr, packetAddress)) {
 789                 /* force a new InetAddress to be created */
 790                 packetAddress = NULL;
 791             }
 792         }
 793         if (packetAddress == NULL) {
 794             packetAddress = NET_SockaddrToInetAddress(env, (struct sockaddr *)&remote_addr, &port);
 795             /* stuff the new Inetaddress in the packet */
 796             (*env)->SetObjectField(env, packet, dp_addressID, packetAddress);
 797         } else {
 798             /* only get the new port number */
 799             port = NET_GetPortFromSockaddr((struct sockaddr *)&remote_addr);
 800         }
 801         /* and fill in the data, remote address/port and such */
 802         (*env)->SetByteArrayRegion(env, packetBuffer, packetBufferOffset, n,
 803                                    (jbyte *)fullPacket);
 804         (*env)->SetIntField(env, packet, dp_portID, port);
 805         (*env)->SetIntField(env, packet, dp_lengthID, n);
 806     }
 807 
 808     if (mallocedPacket) {
 809         free(fullPacket);
 810     }
 811     return port;
 812 }
 813 
 814 /*
 815  * Class:     java_net_PlainDatagramSocketImpl
 816  * Method:    receive
 817  * Signature: (Ljava/net/DatagramPacket;)V
 818  */
 819 JNIEXPORT void JNICALL
 820 Java_java_net_PlainDatagramSocketImpl_receive0(JNIEnv *env, jobject this,
 821                                               jobject packet) {
 822 
 823     char BUF[MAX_BUFFER_LEN];
 824     char *fullPacket = NULL;
 825     int mallocedPacket = JNI_FALSE;
 826     jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
 827     jint timeout = (*env)->GetIntField(env, this, pdsi_timeoutID);
 828 
 829     jbyteArray packetBuffer;
 830     jint packetBufferOffset, packetBufferLen;
 831 
 832     int fd;
 833 
 834     int n;
 835     SOCKADDR remote_addr;
 836     int len;
 837     jboolean retry;
 838 #ifdef __linux__
 839     jboolean connected = JNI_FALSE;
 840     jobject connectedAddress = NULL;
 841     jint connectedPort = 0;
 842     jlong prevTime = 0;
 843 #endif
 844 
 845     if (IS_NULL(fdObj)) {
 846         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
 847                         "Socket closed");
 848         return;
 849     }
 850 
 851     fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
 852 
 853     if (IS_NULL(packet)) {
 854         JNU_ThrowNullPointerException(env, "packet");
 855         return;
 856     }
 857 
 858     packetBuffer = (*env)->GetObjectField(env, packet, dp_bufID);
 859     if (IS_NULL(packetBuffer)) {
 860         JNU_ThrowNullPointerException(env, "packet buffer");
 861         return;
 862     }
 863     packetBufferOffset = (*env)->GetIntField(env, packet, dp_offsetID);
 864     packetBufferLen = (*env)->GetIntField(env, packet, dp_bufLengthID);
 865 
 866     if (packetBufferLen > MAX_BUFFER_LEN) {
 867 
 868         /* When JNI-ifying the JDK's IO routines, we turned
 869          * read's and write's of byte arrays of size greater
 870          * than 2048 bytes into several operations of size 2048.
 871          * This saves a malloc()/memcpy()/free() for big
 872          * buffers.  This is OK for file IO and TCP, but that
 873          * strategy violates the semantics of a datagram protocol.
 874          * (one big send) != (several smaller sends).  So here
 875          * we *must* alloc the buffer.  Note it needn't be bigger
 876          * than 65,536 (0xFFFF) the max size of an IP packet.
 877          * anything bigger is truncated anyway.
 878          *
 879          * We may want to use a smarter allocation scheme at some
 880          * point.
 881          */
 882         if (packetBufferLen > MAX_PACKET_LEN) {
 883             packetBufferLen = MAX_PACKET_LEN;
 884         }
 885         fullPacket = (char *)malloc(packetBufferLen);
 886 
 887         if (!fullPacket) {
 888             JNU_ThrowOutOfMemoryError(env, "heap allocation failed");
 889             return;
 890         } else {
 891             mallocedPacket = JNI_TRUE;
 892         }
 893     } else {
 894         fullPacket = &(BUF[0]);
 895     }
 896 
 897 #ifdef __linux__
 898     /*
 899      * On Linux with the 2.2 kernel we simulate connected datagrams by
 900      * discarding packets
 901      */
 902     if (isOldKernel) {
 903         connected = (*env)->GetBooleanField(env, this, pdsi_connected);
 904         if (connected) {
 905             connectedAddress = (*env)->GetObjectField(env, this, pdsi_connectedAddress);
 906             connectedPort = (*env)->GetIntField(env, this, pdsi_connectedPort);
 907 
 908             if (timeout) {
 909                 prevTime = JVM_CurrentTimeMillis(env, 0);
 910             }
 911         }
 912     }
 913 #endif
 914 
 915     do {
 916         retry = JNI_FALSE;
 917 
 918         if (timeout) {
 919             int ret = NET_Timeout(fd, timeout);
 920             if (ret <= 0) {
 921                 if (ret == 0) {
 922                     JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException",
 923                                     "Receive timed out");
 924                 } else if (ret == JVM_IO_ERR) {
 925 #ifdef __linux__
 926                     if (errno == EBADF) {
 927                          JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");
 928                      } else {
 929                          NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "Receive failed");
 930                      }
 931 #else
 932                      JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");
 933 #endif
 934                 } else if (ret == JVM_IO_INTR) {
 935                     JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException",
 936                                     "operation interrupted");
 937                 }
 938 
 939                 if (mallocedPacket) {
 940                     free(fullPacket);
 941                 }
 942 
 943                 return;
 944             }
 945         }
 946 
 947         /*
 948          * Security Note: For Linux 2.2 with connected datagrams ensure that
 949          * you receive into the stack/heap allocated buffer - do not attempt
 950          * to receive directly into DatagramPacket's byte array.
 951          * (ie: if the virtual machine support pinning don't use
 952          * GetByteArrayElements or a JNI critical section and receive
 953          * directly into the byte array)
 954          */
 955         len = SOCKADDR_LEN;
 956         n = NET_RecvFrom(fd, fullPacket, packetBufferLen, 0,
 957                          (struct sockaddr *)&remote_addr, &len);
 958         /* truncate the data if the packet's length is too small */
 959         if (n > packetBufferLen) {
 960             n = packetBufferLen;
 961         }
 962         if (n == JVM_IO_ERR) {
 963             (*env)->SetIntField(env, packet, dp_offsetID, 0);
 964             (*env)->SetIntField(env, packet, dp_lengthID, 0);
 965             if (errno == ECONNREFUSED) {
 966                 JNU_ThrowByName(env, JNU_JAVANETPKG "PortUnreachableException",
 967                                 "ICMP Port Unreachable");
 968             } else {
 969                 if (errno == EBADF) {
 970                      JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");
 971                  } else {
 972                      NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "Receive failed");
 973                  }
 974             }
 975         } else if (n == JVM_IO_INTR) {
 976             (*env)->SetIntField(env, packet, dp_offsetID, 0);
 977             (*env)->SetIntField(env, packet, dp_lengthID, 0);
 978             JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException",
 979                             "operation interrupted");
 980         } else {
 981             int port;
 982             jobject packetAddress;
 983 
 984             /*
 985              * If we are connected then we know that the datagram that we have
 986              * received is from the address that we are connected too. However
 987              * on Linux with 2.2 kernel we have to simulate this behaviour by
 988              * discarding any datagrams that aren't from the connected address.
 989              */
 990 #ifdef __linux__
 991             if (isOldKernel && connected) {
 992 
 993                 if (NET_GetPortFromSockaddr((struct sockaddr *)&remote_addr) != connectedPort ||
 994                     !NET_SockaddrEqualsInetAddress(env, (struct sockaddr *)&remote_addr, connectedAddress)) {
 995 
 996                     /*
 997                      * Discard the datagram as it's not from the connected
 998                      * address
 999                      */
1000                     retry = JNI_TRUE;
1001 
1002                     /*
1003                      * Adjust timeout if necessary to ensure that we adhere to
1004                      * timeout semantics.
1005                      */
1006                     if (timeout) {
1007                         jlong newTime = JVM_CurrentTimeMillis(env, 0);
1008                         timeout -= (newTime - prevTime);
1009                         if (timeout <= 0) {
1010                             JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException",
1011                                     "Receive timed out");
1012                             if (mallocedPacket) {
1013                                 free(fullPacket);
1014                             }
1015                             return;
1016                         }
1017                         prevTime = newTime;
1018                     }
1019 
1020                     continue;
1021                 }
1022             }
1023 #endif
1024 
1025             /*
1026              * success - fill in received address...
1027              *
1028              * REMIND: Fill in an int on the packet, and create inetadd
1029              * object in Java, as a performance improvement. Also
1030              * construct the inetadd object lazily.
1031              */
1032 
1033             /*
1034              * Check if there is an InetAddress already associated with this
1035              * packet. If so we check if it is the same source address. We
1036              * can't update any existing InetAddress because it is immutable
1037              */
1038             packetAddress = (*env)->GetObjectField(env, packet, dp_addressID);
1039             if (packetAddress != NULL) {
1040                 if (!NET_SockaddrEqualsInetAddress(env, (struct sockaddr *)&remote_addr, packetAddress)) {
1041                     /* force a new InetAddress to be created */
1042                     packetAddress = NULL;
1043                 }
1044             }
1045             if (packetAddress == NULL) {
1046                 packetAddress = NET_SockaddrToInetAddress(env, (struct sockaddr *)&remote_addr, &port);
1047                 /* stuff the new Inetaddress in the packet */
1048                 (*env)->SetObjectField(env, packet, dp_addressID, packetAddress);
1049             } else {
1050                 /* only get the new port number */
1051                 port = NET_GetPortFromSockaddr((struct sockaddr *)&remote_addr);
1052             }
1053             /* and fill in the data, remote address/port and such */
1054             (*env)->SetByteArrayRegion(env, packetBuffer, packetBufferOffset, n,
1055                                        (jbyte *)fullPacket);
1056             (*env)->SetIntField(env, packet, dp_portID, port);
1057             (*env)->SetIntField(env, packet, dp_lengthID, n);
1058         }
1059 
1060     } while (retry);
1061 
1062     if (mallocedPacket) {
1063         free(fullPacket);
1064     }
1065 }
1066 
1067 /*
1068  * Class:     java_net_PlainDatagramSocketImpl
1069  * Method:    datagramSocketCreate
1070  * Signature: ()V
1071  */
1072 JNIEXPORT void JNICALL
1073 Java_java_net_PlainDatagramSocketImpl_datagramSocketCreate(JNIEnv *env,
1074                                                            jobject this) {
1075     jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
1076     int fd;
1077 
1078     int t = 1;
1079 
1080     if (IS_NULL(fdObj)) {
1081         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
1082                         "Socket closed");
1083         return;
1084     } else {
1085 #ifdef AF_INET6
1086         if (ipv6_available()) {
1087             fd =  JVM_Socket(AF_INET6, SOCK_DGRAM, 0);
1088         } else
1089 #endif /* AF_INET6 */
1090             {
1091                 fd =  JVM_Socket(AF_INET, SOCK_DGRAM, 0);
1092             }
1093     }
1094     if (fd == JVM_IO_ERR) {
1095         NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
1096                        "Error creating socket");
1097         return;
1098     }
1099 
1100      setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (char*) &t, sizeof(int));
1101 
1102 #ifdef __linux__
1103     if (isOldKernel) {
1104         setsockopt(fd, SOL_SOCKET, SO_BSDCOMPAT, (char*) &t, sizeof(int));
1105     }
1106 
1107 #ifdef AF_INET6
1108     /*
1109      * On Linux for IPv6 sockets we must set the hop limit
1110      * to 1 to be compatible with default ttl of 1 for IPv4 sockets.
1111      */
1112     if (ipv6_available()) {
1113         int ttl = 1;
1114         setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (char *)&ttl,
1115                    sizeof(ttl));
1116 
1117         if (isOldKernel) {
1118             (*env)->SetIntField(env, this, pdsi_ttlID, ttl);
1119         }
1120     }
1121 #endif
1122 
1123 #endif /* __linux__ */
1124 
1125     (*env)->SetIntField(env, fdObj, IO_fd_fdID, fd);
1126 }
1127 
1128 /*
1129  * Class:     java_net_PlainDatagramSocketImpl
1130  * Method:    datagramSocketClose
1131  * Signature: ()V
1132  */
1133 JNIEXPORT void JNICALL
1134 Java_java_net_PlainDatagramSocketImpl_datagramSocketClose(JNIEnv *env,
1135                                                           jobject this) {
1136     /*
1137      * REMIND: PUT A LOCK AROUND THIS CODE
1138      */
1139     jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
1140     int fd;
1141 
1142     if (IS_NULL(fdObj)) {
1143         return;
1144     }
1145     fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
1146     if (fd == -1) {
1147         return;
1148     }
1149     (*env)->SetIntField(env, fdObj, IO_fd_fdID, -1);
1150     NET_SocketClose(fd);
1151 }
1152 
1153 
1154 /*
1155  * Set outgoing multicast interface designated by a NetworkInterface.
1156  * Throw exception if failed.
1157  */
1158 static void mcast_set_if_by_if_v4(JNIEnv *env, jobject this, int fd, jobject value) {


1159     struct in_addr in;
1160     jobjectArray addrArray;
1161     jsize len;
1162     jobject addr;
1163     int i;
1164 












1165     addrArray = (*env)->GetObjectField(env, value, ni_addrsID);
1166     len = (*env)->GetArrayLength(env, addrArray);
1167 
1168     /*
1169      * Check that there is at least one address bound to this
1170      * interface.
1171      */
1172     if (len < 1) {
1173         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
1174             "bad argument for IP_MULTICAST_IF2: No IP addresses bound to interface");
1175         return;
1176     }
1177 
1178     /*
1179      * We need an ipv4 address here
1180      */
1181     for (i = 0; i < len; i++) {
1182         addr = (*env)->GetObjectArrayElement(env, addrArray, i);
1183         if ((*env)->GetIntField(env, addr, ia_familyID) == IPv4) {
1184             in.s_addr = htonl((*env)->GetIntField(env, addr, ia_addressID));
1185             break;
1186         }
1187     }
1188 
1189     if (JVM_SetSockOpt(fd, IPPROTO_IP, IP_MULTICAST_IF,
1190                        (const char*)&in, sizeof(in)) < 0) {
1191         NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
1192                        "Error setting socket option");
1193     }
1194 }
1195 
1196 /*
1197  * Set outgoing multicast interface designated by a NetworkInterface.
1198  * Throw exception if failed.
1199  */
1200 #ifdef AF_INET6
1201 static void mcast_set_if_by_if_v6(JNIEnv *env, jobject this, int fd, jobject value) {

1202     int index;
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     struct in_addr in;
1238 







1239     in.s_addr = htonl( (*env)->GetIntField(env, value, ia_addressID) );
1240 
1241     if (JVM_SetSockOpt(fd, IPPROTO_IP, IP_MULTICAST_IF,
1242                        (const char*)&in, sizeof(in)) < 0) {
1243         NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
1244                          "Error setting socket option");
1245     }
1246 }
1247 
1248 /*
1249  * Set outgoing multicast interface designated by an InetAddress.
1250  * Throw exception if failed.
1251  */
1252 #ifdef AF_INET6
1253 static void mcast_set_if_by_addr_v6(JNIEnv *env, jobject this, int fd, jobject value) {








1254     value = Java_java_net_NetworkInterface_getByInetAddress0(env, ni_class, value);
1255     if (value == NULL) {
1256         if (!(*env)->ExceptionOccurred(env)) {
1257             JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
1258                  "bad argument for IP_MULTICAST_IF"
1259                  ": address not bound to any interface");
1260         }
1261         return;
1262     }
1263 
1264     mcast_set_if_by_if_v6(env, this, fd, value);
1265 }
1266 #endif
1267 
1268 /*
1269  * Sets the multicast interface.
1270  *
1271  * SocketOptions.IP_MULTICAST_IF :-
1272  *      value is a InetAddress
1273  *      IPv4:   set outgoing multicast interface using
1274  *              IPPROTO_IP/IP_MULTICAST_IF
1275  *      IPv6:   Get the index of the interface to which the
1276  *              InetAddress is bound
1277  *              Set outgoing multicast interface using
1278  *              IPPROTO_IPV6/IPV6_MULTICAST_IF
1279  *              On Linux 2.2 record interface index as can't
1280  *              query the multicast interface.
1281  *
1282  * SockOptions.IF_MULTICAST_IF2 :-
1283  *      value is a NetworkInterface
1284  *      IPv4:   Obtain IP address bound to network interface
1285  *              (NetworkInterface.addres[0])
1286  *              set outgoing multicast interface using
1287  *              IPPROTO_IP/IP_MULTICAST_IF
1288  *      IPv6:   Obtain NetworkInterface.index
1289  *              Set outgoing multicast interface using
1290  *              IPPROTO_IPV6/IPV6_MULTICAST_IF
1291  *              On Linux 2.2 record interface index as can't
1292  *              query the multicast interface.
1293  *
1294  */
1295 static void setMulticastInterface(JNIEnv *env, jobject this, int fd,
1296                                   jint opt, jobject value)
1297 {
1298     if (opt == java_net_SocketOptions_IP_MULTICAST_IF) {
1299         /*
1300          * value is an InetAddress.
1301          */
1302 #ifdef AF_INET6
1303 #ifdef __solaris__
1304         if (ipv6_available()) {
1305             mcast_set_if_by_addr_v6(env, this, fd, value);
1306         } else {
1307             mcast_set_if_by_addr_v4(env, this, fd, value);
1308         }
1309 #endif
1310 #ifdef __linux__
1311         mcast_set_if_by_addr_v4(env, this, fd, value);
1312         if (ipv6_available()) {
1313             mcast_set_if_by_addr_v6(env, this, fd, value);
1314         }
1315 #endif
1316 #else
1317         mcast_set_if_by_addr_v4(env, this, fd, value);
1318 #endif  /* AF_INET6 */
1319     }
1320 
1321     if (opt == java_net_SocketOptions_IP_MULTICAST_IF2) {
1322         /*
1323          * value is a NetworkInterface.
1324          */
1325 #ifdef AF_INET6
1326 #ifdef __solaris__
1327         if (ipv6_available()) {
1328             mcast_set_if_by_if_v6(env, this, fd, value);
1329         } else {
1330             mcast_set_if_by_if_v4(env, this, fd, value);
1331         }
1332 #endif
1333 #ifdef __linux__
1334         mcast_set_if_by_if_v4(env, this, fd, value);
1335         if (ipv6_available()) {
1336             mcast_set_if_by_if_v6(env, this, fd, value);
1337         }
1338 #endif
1339 #else
1340         mcast_set_if_by_if_v4(env, this, fd, value);
1341 #endif  /* AF_INET6 */
1342     }
1343 }
1344 
1345 /*
1346  * Enable/disable local loopback of multicast datagrams.
1347  */
1348 static void mcast_set_loop_v4(JNIEnv *env, jobject this, int fd, jobject value) {


1349     jboolean on;
1350     char loopback;
1351 
1352     on = retrieveBoolean(env, value);





1353     loopback = (!on ? 1 : 0);
1354 
1355     if (NET_SetSockOpt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, (const void *)&loopback, sizeof(char)) < 0) {
1356         NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "Error setting socket option");
1357         return;
1358     }
1359 }
1360 
1361 /*
1362  * Enable/disable local loopback of multicast datagrams.
1363  */
1364 #ifdef AF_INET6
1365 static void mcast_set_loop_v6(JNIEnv *env, jobject this, int fd, jobject value) {


1366     jboolean on;
1367     int loopback;
1368 
1369     on = retrieveBoolean(env, value);





1370     loopback = (!on ? 1 : 0);
1371 
1372     if (NET_SetSockOpt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (const void *)&loopback, sizeof(int)) < 0) {
1373         NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "Error setting socket option");
1374         return;
1375     }
1376 
1377 #ifdef __linux__
1378     /*
1379      * Can't query IPV6_MULTICAST_LOOP on Linux 2.2 kernel so
1380      * store it in impl so that we can simulate getsockopt.
1381      */
1382     if (isOldKernel) {
1383         (*env)->SetBooleanField(env, this, pdsi_loopbackID, on);
1384     }
1385 #endif
1386 }
1387 #endif  /* AF_INET6 */
1388 
1389 /*
1390  * Sets the multicast loopback mode.
1391  */
1392 static void setMulticastLoopbackMode(JNIEnv *env, jobject this, int fd,
1393                                   jint opt, jobject value) {
1394 #ifdef AF_INET6
1395 #ifdef __solaris__
1396     if (ipv6_available()) {
1397         mcast_set_loop_v6(env, this, fd, value);
1398     } else {
1399         mcast_set_loop_v4(env, this, fd, value);
1400     }
1401 #endif
1402 #ifdef __linux__
1403     mcast_set_loop_v4(env, this, fd, value);
1404     if (ipv6_available()) {
1405         mcast_set_loop_v6(env, this, fd, value);
1406     }
1407 #endif
1408 #else
1409     mcast_set_loop_v4(env, this, fd, value);
1410 #endif  /* AF_INET6 */
1411 }
1412 
1413 /*
1414  * Class:     java_net_PlainDatagramSocketImpl
1415  * Method:    socketSetOption
1416  * Signature: (ILjava/lang/Object;)V
1417  */
1418 JNIEXPORT void JNICALL
1419 Java_java_net_PlainDatagramSocketImpl_socketSetOption(JNIEnv *env,
1420                                                       jobject this,
1421                                                       jint opt,
1422                                                       jobject value) {
1423     int fd;
1424     int level, optname, optlen;
1425     union {
1426         int i;
1427         char c;
1428     } optval;
1429 
1430     /*
1431      * Check that socket hasn't been closed
1432      */
1433     fd = getFD(env, this);
1434     if (fd < 0) {
1435         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
1436                         "Socket closed");
1437         return;
1438     }
1439 
1440     /*
1441      * Check argument has been provided
1442      */
1443     if (IS_NULL(value)) {
1444         JNU_ThrowNullPointerException(env, "value argument");
1445         return;
1446     }
1447 
1448     /*
1449      * Setting the multicast interface handled seperately
1450      */
1451     if (opt == java_net_SocketOptions_IP_MULTICAST_IF ||
1452         opt == java_net_SocketOptions_IP_MULTICAST_IF2) {
1453 
1454         setMulticastInterface(env, this, fd, opt, value);
1455         return;
1456     }
1457 
1458     /*
1459      * Setting the multicast loopback mode handled separately
1460      */
1461     if (opt == java_net_SocketOptions_IP_MULTICAST_LOOP) {
1462         setMulticastLoopbackMode(env, this, fd, opt, value);
1463         return;
1464     }
1465 
1466     /*
1467      * Map the Java level socket option to the platform specific
1468      * level and option name.
1469      */
1470     if (NET_MapSocketOption(opt, &level, &optname)) {
1471         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Invalid option");
1472         return;
1473     }
1474 
1475     switch (opt) {
1476         case java_net_SocketOptions_SO_SNDBUF :
1477         case java_net_SocketOptions_SO_RCVBUF :
1478         case java_net_SocketOptions_IP_TOS :
1479             {
1480                 optval.i = retrieveInteger(env, value);








1481                 optlen = sizeof(optval.i);
1482                 break;
1483             }
1484 
1485         case java_net_SocketOptions_SO_REUSEADDR:
1486         case java_net_SocketOptions_SO_BROADCAST:
1487             {
1488                 jboolean on = retrieveBoolean(env, value);


1489 







1490                 /* SO_REUSEADDR or SO_BROADCAST */
1491                 optval.i = (on ? 1 : 0);
1492                 optlen = sizeof(optval.i);
1493 
1494                 break;
1495             }
1496 
1497         default :
1498             JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
1499                 "Socket option not supported by PlainDatagramSocketImp");
1500             break;
1501 
1502     }
1503 
1504     if (NET_SetSockOpt(fd, level, optname, (const void *)&optval, optlen) < 0) {
1505         NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "Error setting socket option");
1506         return;
1507     }
1508 }
1509 
1510 
1511 /*
1512  * Return the multicast interface:
1513  *
1514  * SocketOptions.IP_MULTICAST_IF
1515  *      IPv4:   Query IPPROTO_IP/IP_MULTICAST_IF
1516  *              Create InetAddress
1517  *              IP_MULTICAST_IF returns struct ip_mreqn on 2.2
1518  *              kernel but struct in_addr on 2.4 kernel
1519  *      IPv6:   Query IPPROTO_IPV6 / IPV6_MULTICAST_IF or
1520  *              obtain from impl is Linux 2.2 kernel
1521  *              If index == 0 return InetAddress representing
1522  *              anyLocalAddress.
1523  *              If index > 0 query NetworkInterface by index
1524  *              and returns addrs[0]
1525  *
1526  * SocketOptions.IP_MULTICAST_IF2
1527  *      IPv4:   Query IPPROTO_IP/IP_MULTICAST_IF
1528  *              Query NetworkInterface by IP address and
1529  *              return the NetworkInterface that the address
1530  *              is bound too.
1531  *      IPv6:   Query IPPROTO_IPV6 / IPV6_MULTICAST_IF
1532  *              (except Linux .2 kernel)
1533  *              Query NetworkInterface by index and
1534  *              return NetworkInterface.
1535  */
1536 jobject getMulticastInterface(JNIEnv *env, jobject this, int fd, jint opt) {
1537     jboolean isIPV4 = JNI_TRUE;
1538 
1539 #ifdef AF_INET6
1540     if (ipv6_available()) {
1541         isIPV4 = JNI_FALSE;
1542     }
1543 #endif
1544 
1545     /*
1546      * IPv4 implementation
1547      */
1548     if (isIPV4) {









1549         jobjectArray addrArray;
1550         jobject addr;
1551         jobject ni;
1552 
1553         struct in_addr in;
1554         struct in_addr *inP = &in;
1555         int len = sizeof(struct in_addr);
1556 
1557 #ifdef __linux__
1558         struct ip_mreqn mreqn;
1559         if (isOldKernel) {
1560             inP = (struct in_addr *)&mreqn;
1561             len = sizeof(struct ip_mreqn);
1562         }
1563 #endif
1564 
1565         if (JVM_GetSockOpt(fd, IPPROTO_IP, IP_MULTICAST_IF,
1566                            (char *)inP, &len) < 0) {
1567             NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
1568                              "Error getting socket option");
1569             return NULL;
1570         }
1571 
1572         /*
1573          * Construct and populate an Inet4Address
1574          */
1575         addr = (*env)->NewObject(env, ia4_class, ia4_ctrID, 0);










1576         CHECK_NULL_RETURN(addr, NULL);
1577 
1578 #ifdef __linux__
1579         (*env)->SetIntField(env, addr, ia_addressID,
1580                 (isOldKernel ? ntohl(mreqn.imr_address.s_addr) : ntohl(in.s_addr)) );
1581 #else
1582         (*env)->SetIntField(env, addr, ia_addressID, ntohl(in.s_addr));
1583 #endif
1584 
1585         /*
1586          * For IP_MULTICAST_IF return InetAddress
1587          */
1588         if (opt == java_net_SocketOptions_IP_MULTICAST_IF) {
1589             return addr;
1590         }
1591 
1592         /*
1593          * For IP_MULTICAST_IF2 we get the NetworkInterface for
1594          * this address and return it
1595          */













1596         ni = Java_java_net_NetworkInterface_getByInetAddress0(env, ni_class, addr);
1597         if (ni) {
1598             return ni;
1599         }
1600 
1601         /*
1602          * The address doesn't appear to be bound at any known
1603          * NetworkInterface. Therefore we construct a NetworkInterface
1604          * with this address.
1605          */
1606         ni = (*env)->NewObject(env, ni_class, ni_ctrID, 0);
1607         CHECK_NULL_RETURN(ni, NULL);
1608 
1609         (*env)->SetIntField(env, ni, ni_indexID, -1);
1610         addrArray = (*env)->NewObjectArray(env, 1, ia4_class, NULL);
1611         CHECK_NULL_RETURN(addrArray, NULL);
1612         (*env)->SetObjectArrayElement(env, addrArray, 0, addr);
1613         (*env)->SetObjectField(env, ni, ni_addrsID, addrArray);
1614         return ni;
1615     }
1616 
1617 
1618 #ifdef AF_INET6
1619     /*
1620      * IPv6 implementation
1621      */
1622     if ((opt == java_net_SocketOptions_IP_MULTICAST_IF) ||
1623         (opt == java_net_SocketOptions_IP_MULTICAST_IF2)) {






1624         static jmethodID ia_anyLocalAddressID;
1625 
1626         int index;
1627         int len = sizeof(index);
1628 
1629         jobjectArray addrArray;
1630         jobject addr;
1631         jobject ni;
1632 
1633 #ifdef __linux__
1634         /*
1635          * Linux 2.2 kernel doesn't support IPV6_MULTICAST_IF socke option
1636          * so use cached index.
1637          */
1638         if (isOldKernel) {
1639             index = (*env)->GetIntField(env, this, pdsi_multicastInterfaceID);
1640         } else
1641 #endif
1642         {
1643             if (JVM_GetSockOpt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF,
1644                                (char*)&index, &len) < 0) {
1645                 NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
1646                                "Error getting socket option");
1647                 return NULL;
1648             }
1649         }
1650 
1651         if (ia_anyLocalAddressID == NULL) {














1652             ia_anyLocalAddressID = (*env)->GetStaticMethodID(env,
1653                                                              ia_class,
1654                                                              "anyLocalAddress",
1655                                                              "()Ljava/net/InetAddress;");
1656             CHECK_NULL_RETURN(ia_anyLocalAddressID, NULL);


1657         }
1658 
1659         /*
1660          * If multicast to a specific interface then return the
1661          * interface (for IF2) or the any address on that interface
1662          * (for IF).
1663          */
1664         if (index > 0) {
1665             ni = Java_java_net_NetworkInterface_getByIndex0(env, ni_class,
1666                                                                    index);
1667             if (ni == NULL) {
1668                 char errmsg[255];
1669                 sprintf(errmsg,
1670                         "IPV6_MULTICAST_IF returned index to unrecognized interface: %d",
1671                         index);
1672                 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", errmsg);
1673                 return NULL;
1674             }
1675 
1676             /*
1677              * For IP_MULTICAST_IF2 return the NetworkInterface
1678              */
1679             if (opt == java_net_SocketOptions_IP_MULTICAST_IF2) {
1680                 return ni;
1681             }
1682 
1683             /*
1684              * For IP_MULTICAST_IF return addrs[0]
1685              */
1686             addrArray = (*env)->GetObjectField(env, ni, ni_addrsID);
1687             if ((*env)->GetArrayLength(env, addrArray) < 1) {
1688                 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
1689                     "IPV6_MULTICAST_IF returned interface without IP bindings");
1690                 return NULL;
1691             }
1692 
1693             addr = (*env)->GetObjectArrayElement(env, addrArray, 0);
1694             return addr;
1695         }
1696 
1697         /*
1698          * Multicast to any address - return anyLocalAddress
1699          * or a NetworkInterface with addrs[0] set to anyLocalAddress
1700          */
1701 
1702         addr = (*env)->CallStaticObjectMethod(env, ia_class, ia_anyLocalAddressID,
1703                                               NULL);
1704         if (opt == java_net_SocketOptions_IP_MULTICAST_IF) {
1705             return addr;
1706         }
1707 
1708         ni = (*env)->NewObject(env, ni_class, ni_ctrID, 0);
1709         CHECK_NULL_RETURN(ni, NULL);
1710         (*env)->SetIntField(env, ni, ni_indexID, -1);
1711         addrArray = (*env)->NewObjectArray(env, 1, ia_class, NULL);
1712         CHECK_NULL_RETURN(addrArray, NULL);
1713         (*env)->SetObjectArrayElement(env, addrArray, 0, addr);
1714         (*env)->SetObjectField(env, ni, ni_addrsID, addrArray);
1715         return ni;
1716     }
1717 #endif
1718     return NULL;
1719 }
1720 
1721 
1722 
1723 /*
1724  * Returns relevant info as a jint.
1725  *
1726  * Class:     java_net_PlainDatagramSocketImpl
1727  * Method:    socketGetOption
1728  * Signature: (I)Ljava/lang/Object;
1729  */
1730 JNIEXPORT jobject JNICALL
1731 Java_java_net_PlainDatagramSocketImpl_socketGetOption(JNIEnv *env, jobject this,
1732                                                       jint opt) {
1733     int fd;
1734     int level, optname, optlen;
1735     union {
1736         int i;
1737         char c;
1738     } optval;
1739 
1740     fd = getFD(env, this);
1741     if (fd < 0) {
1742         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
1743                         "socket closed");
1744         return NULL;
1745     }
1746 
1747     /*
1748      * Handle IP_MULTICAST_IF seperately
1749      */
1750     if (opt == java_net_SocketOptions_IP_MULTICAST_IF ||
1751         opt == java_net_SocketOptions_IP_MULTICAST_IF2) {
1752         return getMulticastInterface(env, this, fd, opt);
1753 
1754     }
1755 
1756     /*
1757      * SO_BINDADDR implemented using getsockname
1758      */
1759     if (opt == java_net_SocketOptions_SO_BINDADDR) {
1760         /* find out local IP address */
1761         SOCKADDR him;
1762         socklen_t len = 0;
1763         int port;
1764         jobject iaObj;
1765 
1766         len = SOCKADDR_LEN;
1767 
1768         if (getsockname(fd, (struct sockaddr *)&him, &len) == -1) {
1769             NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
1770                            "Error getting socket name");
1771             return NULL;
1772         }
1773         iaObj = NET_SockaddrToInetAddress(env, (struct sockaddr *)&him, &port);
1774 
1775         return iaObj;
1776     }
1777 
1778     /*
1779      * Map the Java level socket option to the platform specific
1780      * level and option name.
1781      */
1782     if (NET_MapSocketOption(opt, &level, &optname)) {
1783         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Invalid option");
1784         return NULL;
1785     }
1786 
1787     /*
1788      * IP_MULTICAST_LOOP socket option isn't available on Linux 2.2
1789      * kernel with IPv6 so return value stored in impl.
1790      */
1791 #if defined(AF_INET6) && defined(__linux__)
1792     if (isOldKernel && opt == java_net_SocketOptions_IP_MULTICAST_LOOP &&
1793         level == IPPROTO_IPV6) {
1794         int mode = (int)(*env)->GetBooleanField(env, this, pdsi_loopbackID);
1795         return createBoolean(env, mode);
1796     }
1797 #endif
1798 
1799     if (opt == java_net_SocketOptions_IP_MULTICAST_LOOP &&
1800         level == IPPROTO_IP) {
1801         optlen = sizeof(optval.c);
1802     } else {
1803         optlen = sizeof(optval.i);
1804     }
1805 
1806     if (NET_GetSockOpt(fd, level, optname, (void *)&optval, &optlen) < 0) {
1807         NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
1808                          "Error getting socket option");
1809         return NULL;
1810     }
1811 
1812     switch (opt) {
1813         case java_net_SocketOptions_IP_MULTICAST_LOOP:
1814             /* getLoopbackMode() returns true if IP_MULTICAST_LOOP disabled */
1815             if (level == IPPROTO_IP) {
1816                 return createBoolean(env, (int)!optval.c);
1817             } else {
1818                 return createBoolean(env, !optval.i);
1819             }
1820 
1821         case java_net_SocketOptions_SO_BROADCAST:
1822         case java_net_SocketOptions_SO_REUSEADDR:
1823             return createBoolean(env, optval.i);
1824 
1825         case java_net_SocketOptions_SO_SNDBUF:
1826         case java_net_SocketOptions_SO_RCVBUF:
1827         case java_net_SocketOptions_IP_TOS:
1828             return createInteger(env, optval.i);
1829 
1830     }
1831 
1832     /* should never rearch here */
1833     return NULL;
1834 }
1835 
1836 /*
1837  * Multicast-related calls
1838  */
1839 
1840 JNIEXPORT void JNICALL
1841 Java_java_net_PlainDatagramSocketImpl_setTTL(JNIEnv *env, jobject this,
1842                                              jbyte ttl) {
1843     jint ittl = ttl;
1844     if (ittl < 0) {
1845         ittl += 0x100;
1846     }
1847     Java_java_net_PlainDatagramSocketImpl_setTimeToLive(env, this, ittl);
1848 }
1849 
1850 /*
1851  * Set TTL for a socket. Throw exception if failed.
1852  */
1853 static void setTTL(JNIEnv *env, int fd, jint ttl) {
1854     char ittl = (char)ttl;
1855     if (JVM_SetSockOpt(fd, IPPROTO_IP, IP_MULTICAST_TTL, (char*)&ittl,
1856                        sizeof(ittl)) < 0) {
1857         NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
1858                        "Error setting socket option");
1859     }
1860 }
1861 
1862 /*
1863  * Set hops limit for a socket. Throw exception if failed.
1864  */
1865 #ifdef AF_INET6
1866 static void setHopLimit(JNIEnv *env, int fd, jint ttl) {
1867     int ittl = (int)ttl;
1868     if (JVM_SetSockOpt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
1869                        (char*)&ittl, sizeof(ittl)) < 0) {
1870         NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
1871                        "Error setting socket option");
1872     }
1873 }
1874 #endif
1875 
1876 /*
1877  * Class:     java_net_PlainDatagramSocketImpl
1878  * Method:    setTTL
1879  * Signature: (B)V
1880  */
1881 JNIEXPORT void JNICALL
1882 Java_java_net_PlainDatagramSocketImpl_setTimeToLive(JNIEnv *env, jobject this,
1883                                                     jint ttl) {
1884 
1885     jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
1886     int fd;
1887     /* it is important to cast this to a char, otherwise setsockopt gets confused */
1888 
1889     if (IS_NULL(fdObj)) {
1890         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
1891                         "Socket closed");
1892         return;
1893     } else {
1894         fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
1895     }
1896     /* setsockopt to be correct ttl */
1897 #ifdef AF_INET6
1898 #ifdef __solaris__
1899     if (ipv6_available()) {
1900         setHopLimit(env, fd, ttl);
1901     } else {
1902         setTTL(env, fd, ttl);
1903     }
1904 #endif
1905 #ifdef __linux__
1906     setTTL(env, fd, ttl);
1907     if (ipv6_available()) {
1908         setHopLimit(env, fd, ttl);
1909         if (isOldKernel) {
1910             (*env)->SetIntField(env, this, pdsi_ttlID, ttl);
1911         }
1912     }
1913 #endif  // __linux__
1914 #else
1915     setTTL(env, fd, ttl);
1916 #endif  /* AF_INET6 */
1917 }
1918 
1919 /*
1920  * Class:     java_net_PlainDatagramSocketImpl
1921  * Method:    getTTL
1922  * Signature: ()B
1923  */
1924 JNIEXPORT jbyte JNICALL
1925 Java_java_net_PlainDatagramSocketImpl_getTTL(JNIEnv *env, jobject this) {
1926     return (jbyte)Java_java_net_PlainDatagramSocketImpl_getTimeToLive(env, this);
1927 }
1928 
1929 
1930 /*
1931  * Class:     java_net_PlainDatagramSocketImpl
1932  * Method:    getTTL
1933  * Signature: ()B
1934  */
1935 JNIEXPORT jint JNICALL
1936 Java_java_net_PlainDatagramSocketImpl_getTimeToLive(JNIEnv *env, jobject this) {
1937 
1938     jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
1939     jint fd = -1;
1940 
1941     if (IS_NULL(fdObj)) {
1942         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
1943                         "Socket closed");
1944         return -1;
1945     } else {
1946         fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
1947     }
1948     /* getsockopt of ttl */
1949 #ifdef AF_INET6
1950     if (ipv6_available()) {
1951         int ttl = 0;
1952         int len = sizeof(ttl);
1953 
1954 #ifdef __linux__
1955         /*
1956          * Linux 2.2 kernel doesn't support IPV6_MULTICAST_HOPS socket option
1957          */
1958         if (isOldKernel) {
1959             return (*env)->GetIntField(env, this, pdsi_ttlID);
1960         }
1961 #endif
1962 
1963         if (JVM_GetSockOpt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
1964                                (char*)&ttl, &len) < 0) {
1965                 NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
1966                                "Error getting socket option");
1967                 return -1;
1968             }
1969         return (jint)ttl;
1970     } else
1971 #endif /* AF_INET6 */
1972         {
1973             u_char ttl = 0;
1974             int len = sizeof(ttl);
1975             if (JVM_GetSockOpt(fd, IPPROTO_IP, IP_MULTICAST_TTL,
1976                                (char*)&ttl, &len) < 0) {
1977                 NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
1978                                "Error getting socket option");
1979                 return -1;
1980             }
1981             return (jint)ttl;
1982         }
1983 }
1984 
1985 
1986 /*
1987  * mcast_join_leave: Join or leave a multicast group.
1988  *
1989  * For IPv4 sockets use IP_ADD_MEMBERSHIP/IP_DROP_MEMBERSHIP socket option
1990  * to join/leave multicast group.
1991  *
1992  * For IPv6 sockets use IPV6_ADD_MEMBERSHIP/IPV6_DROP_MEMBERSHIP socket option
1993  * to join/leave multicast group. If multicast group is an IPv4 address then
1994  * an IPv4-mapped address is used.
1995  *
1996  * On Linux with IPv6 if we wish to join/leave an IPv4 multicast group then
1997  * we must use the IPv4 socket options. This is because the IPv6 socket options
1998  * don't support IPv4-mapped addresses. This is true as per 2.2.19 and 2.4.7
1999  * kernel releases. In the future it's possible that IP_ADD_MEMBERSHIP
2000  * will be updated to return ENOPROTOOPT if uses with an IPv6 socket (Solaris
2001  * already does this). Thus to cater for this we first try with the IPv4
2002  * socket options and if they fail we use the IPv6 socket options. This
2003  * seems a reasonable failsafe solution.
2004  */
2005 static void mcast_join_leave(JNIEnv *env, jobject this,
2006                              jobject iaObj, jobject niObj,
2007                              jboolean join) {
2008 
2009     jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
2010     jint fd;
2011     jint ipv6_join_leave;
2012 
2013     if (IS_NULL(fdObj)) {
2014         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
2015                         "Socket closed");
2016         return;
2017     } else {
2018         fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
2019     }
2020     if (IS_NULL(iaObj)) {
2021         JNU_ThrowNullPointerException(env, "iaObj");
2022         return;
2023     }
2024 
2025     /*
2026      * Determine if this is an IPv4 or IPv6 join/leave.
2027      */
2028 #ifdef AF_INET6
2029     ipv6_join_leave = ipv6_available();
2030 
2031 #ifdef __linux__
2032     if ((*env)->GetIntField(env, iaObj, ia_familyID) == IPv4) {
2033         ipv6_join_leave = JNI_FALSE;
2034     }
2035 #endif
2036 
2037 #else
2038     /*
2039      * IPv6 not compiled in
2040      */
2041     ipv6_join_leave = JNI_FALSE;
2042 #endif
2043 
2044     /*
2045      * For IPv4 join use IP_ADD_MEMBERSHIP/IP_DROP_MEMBERSHIP socket option
2046      *
2047      * On Linux if IPv4 or IPv6 use IP_ADD_MEMBERSHIP/IP_DROP_MEMBERSHIP
2048      */
2049     if (!ipv6_join_leave) {
2050 #ifdef __linux__
2051         struct ip_mreqn mname;
2052 #else
2053         struct ip_mreq mname;
2054 #endif
2055         int mname_len;
2056 
2057         /*
2058          * joinGroup(InetAddress, NetworkInterface) implementation :-
2059          *
2060          * Linux/IPv6:  use ip_mreqn structure populated with multicast
2061          *              address and interface index.
2062          *
2063          * IPv4:        use ip_mreq structure populated with multicast
2064          *              address and first address obtained from
2065          *              NetworkInterface
2066          */
2067         if (niObj != NULL) {
2068 #if defined(__linux__) && defined(AF_INET6)
2069             if (ipv6_available()) {









2070                 mname.imr_multiaddr.s_addr = htonl((*env)->GetIntField(env, iaObj, ia_addressID));
2071                 mname.imr_address.s_addr = 0;
2072                 mname.imr_ifindex =  (*env)->GetIntField(env, niObj, ni_indexID);
2073                 mname_len = sizeof(struct ip_mreqn);
2074             } else
2075 #endif
2076             {
2077                 jobjectArray addrArray = (*env)->GetObjectField(env, niObj, ni_addrsID);
2078                 jobject addr;
2079 
2080                 if ((*env)->GetArrayLength(env, addrArray) < 1) {
2081                     JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
2082                         "bad argument for IP_ADD_MEMBERSHIP: "
2083                         "No IP addresses bound to interface");
2084                     return;
2085                 }
2086                 addr = (*env)->GetObjectArrayElement(env, addrArray, 0);
2087 
2088                 mname.imr_multiaddr.s_addr = htonl((*env)->GetIntField(env, iaObj, ia_addressID));
2089 #ifdef __linux__
2090                 mname.imr_address.s_addr = htonl((*env)->GetIntField(env, addr, ia_addressID));
2091 #else
2092                 mname.imr_interface.s_addr = htonl((*env)->GetIntField(env, addr, ia_addressID));
2093 #endif
2094                 mname_len = sizeof(struct ip_mreq);
2095             }
2096         }
2097 
2098 
2099         /*
2100          * joinGroup(InetAddress) implementation :-
2101          *
2102          * Linux/IPv6:  use ip_mreqn structure populated with multicast
2103          *              address and interface index. index obtained
2104          *              from cached value or IPV6_MULTICAST_IF.
2105          *
2106          * IPv4:        use ip_mreq structure populated with multicast
2107          *              address and local address obtained from
2108          *              IP_MULTICAST_IF. On Linux IP_MULTICAST_IF
2109          *              returns different structure depending on
2110          *              kernel.
2111          */
2112 
2113         if (niObj == NULL) {
2114 
2115 #if defined(__linux__) && defined(AF_INET6)
2116             if (ipv6_available()) {
2117 
2118                 int index;
2119                 int len = sizeof(index);
2120 
2121                 if (isOldKernel) {
2122                     index = (*env)->GetIntField(env, this, pdsi_multicastInterfaceID);
2123                 } else {
2124                     if (JVM_GetSockOpt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF,
2125                                        (char*)&index, &len) < 0) {
2126                         NET_ThrowCurrent(env, "getsockopt IPV6_MULTICAST_IF failed");
2127                         return;
2128                     }
2129                 }
2130 
2131                 mname.imr_multiaddr.s_addr = htonl((*env)->GetIntField(env, iaObj, ia_addressID));
2132                 mname.imr_address.s_addr = 0 ;
2133                 mname.imr_ifindex = index;
2134                 mname_len = sizeof(struct ip_mreqn);
2135             } else
2136 #endif
2137             {
2138                 struct in_addr in;
2139                 struct in_addr *inP = &in;
2140                 int len = sizeof(struct in_addr);
2141 
2142 #ifdef __linux__
2143                 struct ip_mreqn mreqn;
2144                 if (isOldKernel) {
2145                     inP = (struct in_addr *)&mreqn;
2146                     len = sizeof(struct ip_mreqn);
2147                 }
2148 #endif
2149                 if (getsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, (char *)inP, &len) < 0) {
2150                     NET_ThrowCurrent(env, "getsockopt IP_MULTICAST_IF failed");
2151                     return;
2152                 }
2153 
2154 #ifdef __linux__
2155                 mname.imr_address.s_addr =
2156                     (isOldKernel ? mreqn.imr_address.s_addr : in.s_addr);
2157 
2158 #else
2159                 mname.imr_interface.s_addr = in.s_addr;
2160 #endif
2161                 mname.imr_multiaddr.s_addr = htonl((*env)->GetIntField(env, iaObj, ia_addressID));
2162                 mname_len = sizeof(struct ip_mreq);
2163             }
2164         }
2165 
2166 
2167         /*
2168          * Join the multicast group.
2169          */
2170         if (JVM_SetSockOpt(fd, IPPROTO_IP, (join ? IP_ADD_MEMBERSHIP:IP_DROP_MEMBERSHIP),
2171                            (char *) &mname, mname_len) < 0) {
2172 
2173             /*
2174              * If IP_ADD_MEMBERSHIP returns ENOPROTOOPT on Linux and we've got
2175              * IPv6 enabled then it's possible that the kernel has been fixed
2176              * so we switch to IPV6_ADD_MEMBERSHIP socket option.
2177              * As of 2.4.7 kernel IPV6_ADD_MEMERSHIP can't handle IPv4-mapped
2178              * addresses so we have to use IP_ADD_MEMERSHIP for IPv4 multicast
2179              * groups. However if the socket is an IPv6 socket then then setsockopt
2180              * should reurn ENOPROTOOPT. We assume this will be fixed in Linux
2181              * at some stage.
2182              */
2183 #if defined(__linux__) && defined(AF_INET6)
2184             if (errno == ENOPROTOOPT) {
2185                 if (ipv6_available()) {
2186                     ipv6_join_leave = JNI_TRUE;
2187                     errno = 0;
2188                 } else  {
2189                     errno = ENOPROTOOPT;    /* errno can be changed by ipv6_available */
2190                 }
2191             }
2192 #endif
2193             if (errno) {
2194                 if (join) {
2195                     NET_ThrowCurrent(env, "setsockopt IP_ADD_MEMBERSHIP failed");
2196                 } else {
2197                     if (errno == ENOENT)
2198                         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
2199                             "Not a member of the multicast group");
2200                     else
2201                         NET_ThrowCurrent(env, "setsockopt IP_DROP_MEMBERSHIP failed");
2202                 }
2203             }
2204         }
2205 
2206         /*
2207          * If we haven't switched to IPv6 socket option then we're done.
2208          */
2209         if (!ipv6_join_leave) {
2210             return;
2211         }
2212     }
2213 
2214 
2215     /*
2216      * IPv6 join. If it's an IPv4 multicast group then we use an IPv4-mapped
2217      * address.
2218      */
2219 #ifdef AF_INET6
2220     {
2221         struct ipv6_mreq mname6;
2222         jbyteArray ipaddress;
2223         jbyte caddr[16];
2224         jint family;
2225         jint address;
2226         family = (*env)->GetIntField(env, iaObj, ia_familyID) == IPv4? AF_INET : AF_INET6;
2227         if (family == AF_INET) { /* will convert to IPv4-mapped address */
2228             memset((char *) caddr, 0, 16);
2229             address = (*env)->GetIntField(env, iaObj, ia_addressID);
2230 
2231             caddr[10] = 0xff;
2232             caddr[11] = 0xff;
2233 
2234             caddr[12] = ((address >> 24) & 0xff);
2235             caddr[13] = ((address >> 16) & 0xff);
2236             caddr[14] = ((address >> 8) & 0xff);
2237             caddr[15] = (address & 0xff);
2238         } else {
2239             ipaddress = (*env)->GetObjectField(env, iaObj, ia6_ipaddressID);
2240             (*env)->GetByteArrayRegion(env, ipaddress, 0, 16, caddr);
2241         }
2242 
2243         memcpy((void *)&(mname6.ipv6mr_multiaddr), caddr, sizeof(struct in6_addr));
2244         if (IS_NULL(niObj)) {
2245             int index;
2246             int len = sizeof(index);
2247 
2248 #ifdef __linux__
2249             /*
2250              * 2.2 kernel doens't support IPV6_MULTICAST_IF socket option
2251              */
2252             if (isOldKernel) {
2253                 index = (*env)->GetIntField(env, this, pdsi_multicastInterfaceID);
2254             } else
2255 #endif
2256             {
2257                 if (JVM_GetSockOpt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF,
2258                                  (char*)&index, &len) < 0) {
2259                     NET_ThrowCurrent(env, "getsockopt IPV6_MULTICAST_IF failed");
2260                     return;
2261                 }
2262             }
2263 
2264 #ifdef __linux__
2265             /*
2266              * On 2.4.8+ if we join a group with the interface set to 0
2267              * then the kernel records the interface it decides. This causes
2268              * subsequent leave groups to fail as there is no match. Thus we
2269              * pick the interface if there is a matching route.
2270              */
2271             if (index == 0 && !isOldKernel) {
2272                 int rt_index = getDefaultIPv6Interface(&(mname6.ipv6mr_multiaddr));
2273                 if (rt_index > 0) {
2274                     index = rt_index;
2275                 }
2276             }
2277 #endif
2278 
2279             mname6.ipv6mr_interface = index;
2280         } else {
2281             jint idx = (*env)->GetIntField(env, niObj, ni_indexID);
2282             mname6.ipv6mr_interface = idx;
2283         }
2284 
2285         /* Join the multicast group */
2286         if (JVM_SetSockOpt(fd, IPPROTO_IPV6, (join ? IPV6_ADD_MEMBERSHIP : IPV6_DROP_MEMBERSHIP),
2287                            (char *) &mname6, sizeof (mname6)) < 0) {
2288 
2289             if (join) {
2290                 NET_ThrowCurrent(env, "setsockopt IPV6_ADD_MEMBERSHIP failed");
2291             } else {
2292                 if (errno == ENOENT) {
2293                    JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
2294                         "Not a member of the multicast group");
2295                 } else {
2296                     NET_ThrowCurrent(env, "setsockopt IPV6_DROP_MEMBERSHIP failed");
2297                 }
2298             }
2299         }
2300     }
2301 #endif
2302 }
2303 
2304 /*
2305  * Class:     java_net_PlainDatagramSocketImpl
2306  * Method:    join
2307  * Signature: (Ljava/net/InetAddress;)V
2308  */
2309 JNIEXPORT void JNICALL
2310 Java_java_net_PlainDatagramSocketImpl_join(JNIEnv *env, jobject this,
2311                                            jobject iaObj, jobject niObj)
2312 {
2313     mcast_join_leave(env, this, iaObj, niObj, JNI_TRUE);
2314 }
2315 
2316 /*
2317  * Class:     java_net_PlainDatagramSocketImpl
2318  * Method:    leave
2319  * Signature: (Ljava/net/InetAddress;)V
2320  */
2321 JNIEXPORT void JNICALL
2322 Java_java_net_PlainDatagramSocketImpl_leave(JNIEnv *env, jobject this,
2323                                             jobject iaObj, jobject niObj)
2324 {
2325     mcast_join_leave(env, this, iaObj, niObj, JNI_FALSE);
2326 }
--- EOF ---