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