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