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