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