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