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