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