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 = ∈ 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 = ∈ 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 }