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 /* 894 * Class: java_net_PlainDatagramSocketImpl 895 * Method: datagramSocketCreate 896 * Signature: ()V 897 */ 898 JNIEXPORT void JNICALL 899 Java_java_net_PlainDatagramSocketImpl_datagramSocketCreate(JNIEnv *env, 900 jobject this) { 901 jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID); 902 int arg, fd, t = 1; 903 char tmpbuf[1024]; 904 int domain = ipv6_available() ? AF_INET6 : AF_INET; 905 906 if (IS_NULL(fdObj)) { 907 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", 908 "Socket closed"); 909 return; 910 } 911 912 if ((fd = socket(domain, SOCK_DGRAM, 0)) == -1) { 913 JNU_ThrowByNameWithMessageAndLastError 914 (env, JNU_JAVANETPKG "SocketException", "Error creating socket"); 915 return; 916 } 917 918 /* Disable IPV6_V6ONLY to ensure dual-socket support */ 919 if (domain == AF_INET6) { 920 arg = 1; // ENABLE 921 if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&arg, 922 sizeof(int)) < 0) { 923 NET_ThrowNew(env, errno, "cannot set IPPROTO_IPV6"); 924 close(fd); 925 return; 926 } 927 } 928 929 #ifdef __APPLE__ 930 arg = 65507; 931 if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, 932 (char *)&arg, sizeof(arg)) < 0) { 933 getErrorString(errno, tmpbuf, sizeof(tmpbuf)); 934 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", tmpbuf); 935 close(fd); 936 return; 937 } 938 if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, 939 (char *)&arg, sizeof(arg)) < 0) { 940 getErrorString(errno, tmpbuf, sizeof(tmpbuf)); 941 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", tmpbuf); 942 close(fd); 943 return; 944 } 945 #endif /* __APPLE__ */ 946 947 if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (char*) &t, sizeof (int)) < 0) { 948 getErrorString(errno, tmpbuf, sizeof(tmpbuf)); 949 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", tmpbuf); 950 close(fd); 951 return; 952 } 953 954 #if defined(__linux__) 955 arg = 0; 956 int level = (domain == AF_INET6) ? IPPROTO_IPV6 : IPPROTO_IP; 957 if ((setsockopt(fd, level, IP_MULTICAST_ALL, (char*)&arg, sizeof(arg)) < 0) && 958 (errno != ENOPROTOOPT)) 959 { 960 getErrorString(errno, tmpbuf, sizeof(tmpbuf)); 961 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", tmpbuf); 962 close(fd); 963 return; 964 } 965 #endif 966 967 #if defined (__linux__) 968 /* 969 * On Linux for IPv6 sockets we must set the hop limit 970 * to 1 to be compatible with default TTL of 1 for IPv4 sockets. 971 */ 972 if (domain == AF_INET6) { 973 int ttl = 1; 974 if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (char *) &ttl, 975 sizeof (ttl)) < 0) { 976 getErrorString(errno, tmpbuf, sizeof(tmpbuf)); 977 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", tmpbuf); 978 close(fd); 979 return; 980 } 981 } 982 #endif /* __linux__ */ 983 984 (*env)->SetIntField(env, fdObj, IO_fd_fdID, fd); 985 } 986 987 /* 988 * Class: java_net_PlainDatagramSocketImpl 989 * Method: datagramSocketClose 990 * Signature: ()V 991 */ 992 JNIEXPORT void JNICALL 993 Java_java_net_PlainDatagramSocketImpl_datagramSocketClose(JNIEnv *env, 994 jobject this) { 995 /* 996 * REMIND: PUT A LOCK AROUND THIS CODE 997 */ 998 jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID); 999 int fd; 1000 1001 if (IS_NULL(fdObj)) { 1002 return; 1003 } 1004 fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID); 1005 if (fd == -1) { 1006 return; 1007 } 1008 (*env)->SetIntField(env, fdObj, IO_fd_fdID, -1); 1009 NET_SocketClose(fd); 1010 } 1011 1012 1013 /* 1014 * Set outgoing multicast interface designated by a NetworkInterface. 1015 * Throw exception if failed. 1016 */ 1017 static void mcast_set_if_by_if_v4(JNIEnv *env, jobject this, int fd, jobject value) { 1018 static jfieldID ni_addrsID; 1019 struct in_addr in; 1020 jobjectArray addrArray; 1021 jsize len; 1022 jint family; 1023 jobject addr; 1024 int i; 1025 1026 if (ni_addrsID == NULL ) { 1027 jclass c = (*env)->FindClass(env, "java/net/NetworkInterface"); 1028 CHECK_NULL(c); 1029 ni_addrsID = (*env)->GetFieldID(env, c, "addrs", 1030 "[Ljava/net/InetAddress;"); 1031 CHECK_NULL(ni_addrsID); 1032 } 1033 1034 addrArray = (*env)->GetObjectField(env, value, ni_addrsID); 1035 len = (*env)->GetArrayLength(env, addrArray); 1036 1037 /* 1038 * Check that there is at least one address bound to this 1039 * interface. 1040 */ 1041 if (len < 1) { 1042 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", 1043 "bad argument for IP_MULTICAST_IF2: No IP addresses bound to interface"); 1044 return; 1045 } 1046 1047 /* 1048 * We need an ipv4 address here 1049 */ 1050 in.s_addr = 0; 1051 for (i = 0; i < len; i++) { 1052 addr = (*env)->GetObjectArrayElement(env, addrArray, i); 1053 family = getInetAddress_family(env, addr); 1054 JNU_CHECK_EXCEPTION(env); 1055 if (family == java_net_InetAddress_IPv4) { 1056 in.s_addr = htonl(getInetAddress_addr(env, addr)); 1057 JNU_CHECK_EXCEPTION(env); 1058 break; 1059 } 1060 } 1061 1062 if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, 1063 (const char *)&in, sizeof(in)) < 0) { 1064 JNU_ThrowByNameWithMessageAndLastError 1065 (env, JNU_JAVANETPKG "SocketException", "Error setting socket option"); 1066 } 1067 } 1068 1069 /* 1070 * Set outgoing multicast interface designated by a NetworkInterface. 1071 * Throw exception if failed. 1072 */ 1073 static void mcast_set_if_by_if_v6(JNIEnv *env, jobject this, int fd, jobject value) { 1074 static jfieldID ni_indexID; 1075 int index; 1076 1077 if (ni_indexID == NULL) { 1078 jclass c = (*env)->FindClass(env, "java/net/NetworkInterface"); 1079 CHECK_NULL(c); 1080 ni_indexID = (*env)->GetFieldID(env, c, "index", "I"); 1081 CHECK_NULL(ni_indexID); 1082 } 1083 index = (*env)->GetIntField(env, value, ni_indexID); 1084 1085 if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, 1086 (const char*)&index, sizeof(index)) < 0) { 1087 if ((errno == EINVAL || errno == EADDRNOTAVAIL) && index > 0) { 1088 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", 1089 "IPV6_MULTICAST_IF failed (interface has IPv4 " 1090 "address only?)"); 1091 } else { 1092 JNU_ThrowByNameWithMessageAndLastError 1093 (env, JNU_JAVANETPKG "SocketException", "Error setting socket option"); 1094 } 1095 return; 1096 } 1097 } 1098 1099 /* 1100 * Set outgoing multicast interface designated by an InetAddress. 1101 * Throw exception if failed. 1102 */ 1103 static void mcast_set_if_by_addr_v4(JNIEnv *env, jobject this, int fd, jobject value) { 1104 struct in_addr in; 1105 1106 in.s_addr = htonl( getInetAddress_addr(env, value) ); 1107 JNU_CHECK_EXCEPTION(env); 1108 if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, 1109 (const char*)&in, sizeof(in)) < 0) { 1110 JNU_ThrowByNameWithMessageAndLastError 1111 (env, JNU_JAVANETPKG "SocketException", "Error setting socket option"); 1112 } 1113 } 1114 1115 /* 1116 * Set outgoing multicast interface designated by an InetAddress. 1117 * Throw exception if failed. 1118 */ 1119 static void mcast_set_if_by_addr_v6(JNIEnv *env, jobject this, int fd, jobject value) { 1120 static jclass ni_class; 1121 if (ni_class == NULL) { 1122 jclass c = (*env)->FindClass(env, "java/net/NetworkInterface"); 1123 CHECK_NULL(c); 1124 ni_class = (*env)->NewGlobalRef(env, c); 1125 CHECK_NULL(ni_class); 1126 } 1127 1128 value = Java_java_net_NetworkInterface_getByInetAddress0(env, ni_class, value); 1129 if (value == NULL) { 1130 if (!(*env)->ExceptionOccurred(env)) { 1131 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", 1132 "bad argument for IP_MULTICAST_IF" 1133 ": address not bound to any interface"); 1134 } 1135 return; 1136 } 1137 1138 mcast_set_if_by_if_v6(env, this, fd, value); 1139 } 1140 1141 /* 1142 * Sets the multicast interface. 1143 * 1144 * SocketOptions.IP_MULTICAST_IF :- 1145 * value is a InetAddress 1146 * IPv4: set outgoing multicast interface using 1147 * IPPROTO_IP/IP_MULTICAST_IF 1148 * IPv6: Get the index of the interface to which the 1149 * InetAddress is bound 1150 * Set outgoing multicast interface using 1151 * IPPROTO_IPV6/IPV6_MULTICAST_IF 1152 * 1153 * SockOptions.IF_MULTICAST_IF2 :- 1154 * value is a NetworkInterface 1155 * IPv4: Obtain IP address bound to network interface 1156 * (NetworkInterface.addres[0]) 1157 * set outgoing multicast interface using 1158 * IPPROTO_IP/IP_MULTICAST_IF 1159 * IPv6: Obtain NetworkInterface.index 1160 * Set outgoing multicast interface using 1161 * IPPROTO_IPV6/IPV6_MULTICAST_IF 1162 * 1163 */ 1164 static void setMulticastInterface(JNIEnv *env, jobject this, int fd, 1165 jint opt, jobject value) 1166 { 1167 if (opt == java_net_SocketOptions_IP_MULTICAST_IF) { 1168 /* 1169 * value is an InetAddress. 1170 */ 1171 #ifdef __linux__ 1172 mcast_set_if_by_addr_v4(env, this, fd, value); 1173 if (ipv6_available()) { 1174 if ((*env)->ExceptionCheck(env)){ 1175 (*env)->ExceptionClear(env); 1176 } 1177 mcast_set_if_by_addr_v6(env, this, fd, value); 1178 } 1179 #else /* __linux__ not defined */ 1180 if (ipv6_available()) { 1181 mcast_set_if_by_addr_v6(env, this, fd, value); 1182 } else { 1183 mcast_set_if_by_addr_v4(env, this, fd, value); 1184 } 1185 #endif /* __linux__ */ 1186 } 1187 1188 if (opt == java_net_SocketOptions_IP_MULTICAST_IF2) { 1189 /* 1190 * value is a NetworkInterface. 1191 */ 1192 #ifdef __linux__ 1193 mcast_set_if_by_if_v4(env, this, fd, value); 1194 if (ipv6_available()) { 1195 if ((*env)->ExceptionCheck(env)){ 1196 (*env)->ExceptionClear(env); 1197 } 1198 mcast_set_if_by_if_v6(env, this, fd, value); 1199 } 1200 #else /* __linux__ not defined */ 1201 if (ipv6_available()) { 1202 mcast_set_if_by_if_v6(env, this, fd, value); 1203 } else { 1204 mcast_set_if_by_if_v4(env, this, fd, value); 1205 } 1206 #endif /* __linux__ */ 1207 } 1208 } 1209 1210 /* 1211 * Enable/disable local loopback of multicast datagrams. 1212 */ 1213 static void mcast_set_loop_v4(JNIEnv *env, jobject this, int fd, jobject value) { 1214 jclass cls; 1215 jfieldID fid; 1216 jboolean on; 1217 char loopback; 1218 1219 cls = (*env)->FindClass(env, "java/lang/Boolean"); 1220 CHECK_NULL(cls); 1221 fid = (*env)->GetFieldID(env, cls, "value", "Z"); 1222 CHECK_NULL(fid); 1223 1224 on = (*env)->GetBooleanField(env, value, fid); 1225 loopback = (!on ? 1 : 0); 1226 1227 if (NET_SetSockOpt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, 1228 (const void *)&loopback, sizeof(char)) < 0) { 1229 JNU_ThrowByNameWithMessageAndLastError 1230 (env, JNU_JAVANETPKG "SocketException", "Error setting socket option"); 1231 return; 1232 } 1233 } 1234 1235 /* 1236 * Enable/disable local loopback of multicast datagrams. 1237 */ 1238 static void mcast_set_loop_v6(JNIEnv *env, jobject this, int fd, jobject value) { 1239 jclass cls; 1240 jfieldID fid; 1241 jboolean on; 1242 int loopback; 1243 1244 cls = (*env)->FindClass(env, "java/lang/Boolean"); 1245 CHECK_NULL(cls); 1246 fid = (*env)->GetFieldID(env, cls, "value", "Z"); 1247 CHECK_NULL(fid); 1248 1249 on = (*env)->GetBooleanField(env, value, fid); 1250 loopback = (!on ? 1 : 0); 1251 1252 if (NET_SetSockOpt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, 1253 (const void *)&loopback, sizeof(int)) < 0) { 1254 JNU_ThrowByNameWithMessageAndLastError 1255 (env, JNU_JAVANETPKG "SocketException", "Error setting socket option"); 1256 return; 1257 } 1258 1259 } 1260 1261 /* 1262 * Sets the multicast loopback mode. 1263 */ 1264 static void setMulticastLoopbackMode(JNIEnv *env, jobject this, int fd, 1265 jint opt, jobject value) { 1266 #ifdef __linux__ 1267 mcast_set_loop_v4(env, this, fd, value); 1268 if (ipv6_available()) { 1269 if ((*env)->ExceptionCheck(env)){ 1270 (*env)->ExceptionClear(env); 1271 } 1272 mcast_set_loop_v6(env, this, fd, value); 1273 } 1274 #else /* __linux__ not defined */ 1275 if (ipv6_available()) { 1276 mcast_set_loop_v6(env, this, fd, value); 1277 } else { 1278 mcast_set_loop_v4(env, this, fd, value); 1279 } 1280 #endif /* __linux__ */ 1281 } 1282 1283 /* 1284 * Class: java_net_PlainDatagramSocketImpl 1285 * Method: socketSetOption0 1286 * Signature: (ILjava/lang/Object;)V 1287 */ 1288 JNIEXPORT void JNICALL 1289 Java_java_net_PlainDatagramSocketImpl_socketSetOption0 1290 (JNIEnv *env, jobject this, jint opt, jobject value) 1291 { 1292 int fd; 1293 int level, optname, optlen; 1294 int optval; 1295 optlen = sizeof(int); 1296 1297 /* 1298 * Check that socket hasn't been closed 1299 */ 1300 fd = getFD(env, this); 1301 if (fd < 0) { 1302 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", 1303 "Socket closed"); 1304 return; 1305 } 1306 1307 /* 1308 * Check argument has been provided 1309 */ 1310 if (IS_NULL(value)) { 1311 JNU_ThrowNullPointerException(env, "value argument"); 1312 return; 1313 } 1314 1315 /* 1316 * Setting the multicast interface handled separately 1317 */ 1318 if (opt == java_net_SocketOptions_IP_MULTICAST_IF || 1319 opt == java_net_SocketOptions_IP_MULTICAST_IF2) { 1320 1321 setMulticastInterface(env, this, fd, opt, value); 1322 return; 1323 } 1324 1325 /* 1326 * Setting the multicast loopback mode handled separately 1327 */ 1328 if (opt == java_net_SocketOptions_IP_MULTICAST_LOOP) { 1329 setMulticastLoopbackMode(env, this, fd, opt, value); 1330 return; 1331 } 1332 1333 /* 1334 * Map the Java level socket option to the platform specific 1335 * level and option name. 1336 */ 1337 if (NET_MapSocketOption(opt, &level, &optname)) { 1338 JNU_ThrowByName(env, "java/net/SocketException", "Invalid option"); 1339 return; 1340 } 1341 1342 switch (opt) { 1343 case java_net_SocketOptions_SO_SNDBUF : 1344 case java_net_SocketOptions_SO_RCVBUF : 1345 case java_net_SocketOptions_IP_TOS : 1346 { 1347 jclass cls; 1348 jfieldID fid; 1349 1350 cls = (*env)->FindClass(env, "java/lang/Integer"); 1351 CHECK_NULL(cls); 1352 fid = (*env)->GetFieldID(env, cls, "value", "I"); 1353 CHECK_NULL(fid); 1354 1355 optval = (*env)->GetIntField(env, value, fid); 1356 break; 1357 } 1358 1359 case java_net_SocketOptions_SO_REUSEADDR: 1360 case java_net_SocketOptions_SO_REUSEPORT: 1361 case java_net_SocketOptions_SO_BROADCAST: 1362 { 1363 jclass cls; 1364 jfieldID fid; 1365 jboolean on; 1366 1367 cls = (*env)->FindClass(env, "java/lang/Boolean"); 1368 CHECK_NULL(cls); 1369 fid = (*env)->GetFieldID(env, cls, "value", "Z"); 1370 CHECK_NULL(fid); 1371 1372 on = (*env)->GetBooleanField(env, value, fid); 1373 1374 /* SO_REUSEADDR or SO_BROADCAST */ 1375 optval = (on ? 1 : 0); 1376 1377 break; 1378 } 1379 1380 default : 1381 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", 1382 "Socket option not supported by PlainDatagramSocketImp"); 1383 return; 1384 1385 } 1386 1387 if (NET_SetSockOpt(fd, level, optname, (const void *)&optval, optlen) < 0) { 1388 JNU_ThrowByNameWithMessageAndLastError 1389 (env, JNU_JAVANETPKG "SocketException", "Error setting socket option"); 1390 return; 1391 } 1392 } 1393 1394 1395 /* 1396 * Return the multicast interface: 1397 * 1398 * SocketOptions.IP_MULTICAST_IF 1399 * IPv4: Query IPPROTO_IP/IP_MULTICAST_IF 1400 * Create InetAddress 1401 * IP_MULTICAST_IF returns struct ip_mreqn on 2.2 1402 * kernel but struct in_addr on 2.4 kernel 1403 * IPv6: Query IPPROTO_IPV6 / IPV6_MULTICAST_IF 1404 * If index == 0 return InetAddress representing 1405 * anyLocalAddress. 1406 * If index > 0 query NetworkInterface by index 1407 * and returns addrs[0] 1408 * 1409 * SocketOptions.IP_MULTICAST_IF2 1410 * IPv4: Query IPPROTO_IP/IP_MULTICAST_IF 1411 * Query NetworkInterface by IP address and 1412 * return the NetworkInterface that the address 1413 * is bound too. 1414 * IPv6: Query IPPROTO_IPV6 / IPV6_MULTICAST_IF 1415 * (except Linux .2 kernel) 1416 * Query NetworkInterface by index and 1417 * return NetworkInterface. 1418 */ 1419 jobject getMulticastInterface(JNIEnv *env, jobject this, int fd, jint opt) { 1420 jboolean isIPV4 = JNI_TRUE; 1421 1422 if (ipv6_available()) { 1423 isIPV4 = JNI_FALSE; 1424 } 1425 1426 /* 1427 * IPv4 implementation 1428 */ 1429 if (isIPV4) { 1430 static jclass inet4_class; 1431 static jmethodID inet4_ctrID; 1432 1433 static jclass ni_class; 1434 static jmethodID ni_ctrID; 1435 static jfieldID ni_indexID; 1436 static jfieldID ni_addrsID; 1437 static jfieldID ni_nameID; 1438 1439 jobjectArray addrArray; 1440 jobject addr; 1441 jobject ni; 1442 jobject ni_name; 1443 1444 struct in_addr in; 1445 struct in_addr *inP = ∈ 1446 socklen_t len = sizeof(struct in_addr); 1447 1448 if (getsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, 1449 (char *)inP, &len) < 0) { 1450 JNU_ThrowByNameWithMessageAndLastError 1451 (env, JNU_JAVANETPKG "SocketException", "Error getting socket option"); 1452 return NULL; 1453 } 1454 1455 /* 1456 * Construct and populate an Inet4Address 1457 */ 1458 if (inet4_class == NULL) { 1459 jclass c = (*env)->FindClass(env, "java/net/Inet4Address"); 1460 CHECK_NULL_RETURN(c, NULL); 1461 inet4_ctrID = (*env)->GetMethodID(env, c, "<init>", "()V"); 1462 CHECK_NULL_RETURN(inet4_ctrID, NULL); 1463 inet4_class = (*env)->NewGlobalRef(env, c); 1464 CHECK_NULL_RETURN(inet4_class, NULL); 1465 } 1466 addr = (*env)->NewObject(env, inet4_class, inet4_ctrID, 0); 1467 CHECK_NULL_RETURN(addr, NULL); 1468 1469 setInetAddress_addr(env, addr, ntohl(in.s_addr)); 1470 JNU_CHECK_EXCEPTION_RETURN(env, NULL); 1471 1472 /* 1473 * For IP_MULTICAST_IF return InetAddress 1474 */ 1475 if (opt == java_net_SocketOptions_IP_MULTICAST_IF) { 1476 return addr; 1477 } 1478 1479 /* 1480 * For IP_MULTICAST_IF2 we get the NetworkInterface for 1481 * this address and return it 1482 */ 1483 if (ni_class == NULL) { 1484 jclass c = (*env)->FindClass(env, "java/net/NetworkInterface"); 1485 CHECK_NULL_RETURN(c, NULL); 1486 ni_ctrID = (*env)->GetMethodID(env, c, "<init>", "()V"); 1487 CHECK_NULL_RETURN(ni_ctrID, NULL); 1488 ni_indexID = (*env)->GetFieldID(env, c, "index", "I"); 1489 CHECK_NULL_RETURN(ni_indexID, NULL); 1490 ni_addrsID = (*env)->GetFieldID(env, c, "addrs", 1491 "[Ljava/net/InetAddress;"); 1492 CHECK_NULL_RETURN(ni_addrsID, NULL); 1493 ni_nameID = (*env)->GetFieldID(env, c,"name", "Ljava/lang/String;"); 1494 CHECK_NULL_RETURN(ni_nameID, NULL); 1495 ni_class = (*env)->NewGlobalRef(env, c); 1496 CHECK_NULL_RETURN(ni_class, NULL); 1497 } 1498 ni = Java_java_net_NetworkInterface_getByInetAddress0(env, ni_class, addr); 1499 if (ni) { 1500 return ni; 1501 } 1502 1503 /* 1504 * The address doesn't appear to be bound at any known 1505 * NetworkInterface. Therefore we construct a NetworkInterface 1506 * with this address. 1507 */ 1508 ni = (*env)->NewObject(env, ni_class, ni_ctrID, 0); 1509 CHECK_NULL_RETURN(ni, NULL); 1510 1511 (*env)->SetIntField(env, ni, ni_indexID, -1); 1512 addrArray = (*env)->NewObjectArray(env, 1, inet4_class, NULL); 1513 CHECK_NULL_RETURN(addrArray, NULL); 1514 (*env)->SetObjectArrayElement(env, addrArray, 0, addr); 1515 (*env)->SetObjectField(env, ni, ni_addrsID, addrArray); 1516 ni_name = (*env)->NewStringUTF(env, ""); 1517 if (ni_name != NULL) { 1518 (*env)->SetObjectField(env, ni, ni_nameID, ni_name); 1519 } 1520 return ni; 1521 } 1522 1523 1524 /* 1525 * IPv6 implementation 1526 */ 1527 if ((opt == java_net_SocketOptions_IP_MULTICAST_IF) || 1528 (opt == java_net_SocketOptions_IP_MULTICAST_IF2)) { 1529 1530 static jclass ni_class; 1531 static jmethodID ni_ctrID; 1532 static jfieldID ni_indexID; 1533 static jfieldID ni_addrsID; 1534 static jclass ia_class; 1535 static jfieldID ni_nameID; 1536 static jmethodID ia_anyLocalAddressID; 1537 1538 int index = 0; 1539 socklen_t len = sizeof(index); 1540 1541 jobjectArray addrArray; 1542 jobject addr; 1543 jobject ni; 1544 jobject ni_name; 1545 1546 if (getsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, 1547 (char*)&index, &len) < 0) { 1548 JNU_ThrowByNameWithMessageAndLastError 1549 (env, JNU_JAVANETPKG "SocketException", "Error getting socket option"); 1550 return NULL; 1551 } 1552 1553 if (ni_class == NULL) { 1554 jclass c = (*env)->FindClass(env, "java/net/NetworkInterface"); 1555 CHECK_NULL_RETURN(c, NULL); 1556 ni_ctrID = (*env)->GetMethodID(env, c, "<init>", "()V"); 1557 CHECK_NULL_RETURN(ni_ctrID, NULL); 1558 ni_indexID = (*env)->GetFieldID(env, c, "index", "I"); 1559 CHECK_NULL_RETURN(ni_indexID, NULL); 1560 ni_addrsID = (*env)->GetFieldID(env, c, "addrs", 1561 "[Ljava/net/InetAddress;"); 1562 CHECK_NULL_RETURN(ni_addrsID, NULL); 1563 1564 ia_class = (*env)->FindClass(env, "java/net/InetAddress"); 1565 CHECK_NULL_RETURN(ia_class, NULL); 1566 ia_class = (*env)->NewGlobalRef(env, ia_class); 1567 CHECK_NULL_RETURN(ia_class, NULL); 1568 ia_anyLocalAddressID = (*env)->GetStaticMethodID(env, 1569 ia_class, 1570 "anyLocalAddress", 1571 "()Ljava/net/InetAddress;"); 1572 CHECK_NULL_RETURN(ia_anyLocalAddressID, NULL); 1573 ni_nameID = (*env)->GetFieldID(env, c,"name", "Ljava/lang/String;"); 1574 CHECK_NULL_RETURN(ni_nameID, NULL); 1575 ni_class = (*env)->NewGlobalRef(env, c); 1576 CHECK_NULL_RETURN(ni_class, NULL); 1577 } 1578 1579 /* 1580 * If multicast to a specific interface then return the 1581 * interface (for IF2) or the any address on that interface 1582 * (for IF). 1583 */ 1584 if (index > 0) { 1585 ni = Java_java_net_NetworkInterface_getByIndex0(env, ni_class, 1586 index); 1587 if (ni == NULL) { 1588 char errmsg[255]; 1589 sprintf(errmsg, 1590 "IPV6_MULTICAST_IF returned index to unrecognized interface: %d", 1591 index); 1592 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", errmsg); 1593 return NULL; 1594 } 1595 1596 /* 1597 * For IP_MULTICAST_IF2 return the NetworkInterface 1598 */ 1599 if (opt == java_net_SocketOptions_IP_MULTICAST_IF2) { 1600 return ni; 1601 } 1602 1603 /* 1604 * For IP_MULTICAST_IF return addrs[0] 1605 */ 1606 addrArray = (*env)->GetObjectField(env, ni, ni_addrsID); 1607 if ((*env)->GetArrayLength(env, addrArray) < 1) { 1608 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", 1609 "IPV6_MULTICAST_IF returned interface without IP bindings"); 1610 return NULL; 1611 } 1612 1613 addr = (*env)->GetObjectArrayElement(env, addrArray, 0); 1614 return addr; 1615 } 1616 1617 /* 1618 * Multicast to any address - return anyLocalAddress 1619 * or a NetworkInterface with addrs[0] set to anyLocalAddress 1620 */ 1621 1622 addr = (*env)->CallStaticObjectMethod(env, ia_class, ia_anyLocalAddressID, 1623 NULL); 1624 if (opt == java_net_SocketOptions_IP_MULTICAST_IF) { 1625 return addr; 1626 } 1627 1628 ni = (*env)->NewObject(env, ni_class, ni_ctrID, 0); 1629 CHECK_NULL_RETURN(ni, NULL); 1630 (*env)->SetIntField(env, ni, ni_indexID, -1); 1631 addrArray = (*env)->NewObjectArray(env, 1, ia_class, NULL); 1632 CHECK_NULL_RETURN(addrArray, NULL); 1633 (*env)->SetObjectArrayElement(env, addrArray, 0, addr); 1634 (*env)->SetObjectField(env, ni, ni_addrsID, addrArray); 1635 ni_name = (*env)->NewStringUTF(env, ""); 1636 if (ni_name != NULL) { 1637 (*env)->SetObjectField(env, ni, ni_nameID, ni_name); 1638 } 1639 return ni; 1640 } 1641 return NULL; 1642 } 1643 1644 1645 1646 /* 1647 * Returns relevant info as a jint. 1648 * 1649 * Class: java_net_PlainDatagramSocketImpl 1650 * Method: socketGetOption 1651 * Signature: (I)Ljava/lang/Object; 1652 */ 1653 JNIEXPORT jobject JNICALL 1654 Java_java_net_PlainDatagramSocketImpl_socketGetOption 1655 (JNIEnv *env, jobject this, jint opt) 1656 { 1657 int fd; 1658 int level, optname, optlen; 1659 union { 1660 int i; 1661 char c; 1662 } optval; 1663 1664 fd = getFD(env, this); 1665 if (fd < 0) { 1666 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", 1667 "socket closed"); 1668 return NULL; 1669 } 1670 1671 /* 1672 * Handle IP_MULTICAST_IF separately 1673 */ 1674 if (opt == java_net_SocketOptions_IP_MULTICAST_IF || 1675 opt == java_net_SocketOptions_IP_MULTICAST_IF2) { 1676 return getMulticastInterface(env, this, fd, opt); 1677 1678 } 1679 1680 /* 1681 * SO_BINDADDR implemented using getsockname 1682 */ 1683 if (opt == java_net_SocketOptions_SO_BINDADDR) { 1684 /* find out local IP address */ 1685 SOCKETADDRESS sa; 1686 socklen_t len = sizeof(SOCKETADDRESS); 1687 int port; 1688 jobject iaObj; 1689 1690 if (getsockname(fd, &sa.sa, &len) == -1) { 1691 JNU_ThrowByNameWithMessageAndLastError 1692 (env, JNU_JAVANETPKG "SocketException", "Error getting socket name"); 1693 return NULL; 1694 } 1695 iaObj = NET_SockaddrToInetAddress(env, &sa, &port); 1696 1697 return iaObj; 1698 } 1699 1700 /* 1701 * Map the Java level socket option to the platform specific 1702 * level and option name. 1703 */ 1704 if (NET_MapSocketOption(opt, &level, &optname)) { 1705 JNU_ThrowByName(env, "java/net/SocketException", "Invalid option"); 1706 return NULL; 1707 } 1708 1709 if (opt == java_net_SocketOptions_IP_MULTICAST_LOOP && 1710 level == IPPROTO_IP) { 1711 optlen = sizeof(optval.c); 1712 } else { 1713 optlen = sizeof(optval.i); 1714 } 1715 1716 if (NET_GetSockOpt(fd, level, optname, (void *)&optval, &optlen) < 0) { 1717 JNU_ThrowByNameWithMessageAndLastError 1718 (env, JNU_JAVANETPKG "SocketException", "Error getting socket option"); 1719 return NULL; 1720 } 1721 1722 switch (opt) { 1723 case java_net_SocketOptions_IP_MULTICAST_LOOP: 1724 /* getLoopbackMode() returns true if IP_MULTICAST_LOOP disabled */ 1725 if (level == IPPROTO_IP) { 1726 return createBoolean(env, (int)!optval.c); 1727 } else { 1728 return createBoolean(env, !optval.i); 1729 } 1730 1731 case java_net_SocketOptions_SO_BROADCAST: 1732 case java_net_SocketOptions_SO_REUSEADDR: 1733 return createBoolean(env, optval.i); 1734 1735 case java_net_SocketOptions_SO_REUSEPORT: 1736 return createBoolean(env, optval.i); 1737 1738 case java_net_SocketOptions_SO_SNDBUF: 1739 case java_net_SocketOptions_SO_RCVBUF: 1740 case java_net_SocketOptions_IP_TOS: 1741 return createInteger(env, optval.i); 1742 1743 } 1744 1745 /* should never reach here */ 1746 return NULL; 1747 } 1748 1749 /* 1750 * Multicast-related calls 1751 */ 1752 1753 JNIEXPORT void JNICALL 1754 Java_java_net_PlainDatagramSocketImpl_setTTL(JNIEnv *env, jobject this, 1755 jbyte ttl) { 1756 jint ittl = ttl; 1757 if (ittl < 0) { 1758 ittl += 0x100; 1759 } 1760 Java_java_net_PlainDatagramSocketImpl_setTimeToLive(env, this, ittl); 1761 } 1762 1763 /* 1764 * Set TTL for a socket. Throw exception if failed. 1765 */ 1766 static void setTTL(JNIEnv *env, int fd, jint ttl) { 1767 char ittl = (char)ttl; 1768 if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, (char*)&ittl, 1769 sizeof(ittl)) < 0) { 1770 JNU_ThrowByNameWithMessageAndLastError 1771 (env, JNU_JAVANETPKG "SocketException", "Error setting socket option"); 1772 } 1773 } 1774 1775 /* 1776 * Set hops limit for a socket. Throw exception if failed. 1777 */ 1778 static void setHopLimit(JNIEnv *env, int fd, jint ttl) { 1779 int ittl = (int)ttl; 1780 if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, 1781 (char*)&ittl, sizeof(ittl)) < 0) { 1782 JNU_ThrowByNameWithMessageAndLastError 1783 (env, JNU_JAVANETPKG "SocketException", "Error setting socket option"); 1784 } 1785 } 1786 1787 /* 1788 * Class: java_net_PlainDatagramSocketImpl 1789 * Method: setTTL 1790 * Signature: (B)V 1791 */ 1792 JNIEXPORT void JNICALL 1793 Java_java_net_PlainDatagramSocketImpl_setTimeToLive(JNIEnv *env, jobject this, 1794 jint ttl) { 1795 1796 jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID); 1797 int fd; 1798 /* it is important to cast this to a char, otherwise setsockopt gets confused */ 1799 1800 if (IS_NULL(fdObj)) { 1801 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", 1802 "Socket closed"); 1803 return; 1804 } else { 1805 fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID); 1806 } 1807 /* setsockopt to be correct TTL */ 1808 #ifdef __linux__ 1809 setTTL(env, fd, ttl); 1810 JNU_CHECK_EXCEPTION(env); 1811 if (ipv6_available()) { 1812 setHopLimit(env, fd, ttl); 1813 } 1814 #else /* __linux__ not defined */ 1815 if (ipv6_available()) { 1816 setHopLimit(env, fd, ttl); 1817 } else { 1818 setTTL(env, fd, ttl); 1819 } 1820 #endif /* __linux__ */ 1821 } 1822 1823 /* 1824 * Class: java_net_PlainDatagramSocketImpl 1825 * Method: getTTL 1826 * Signature: ()B 1827 */ 1828 JNIEXPORT jbyte JNICALL 1829 Java_java_net_PlainDatagramSocketImpl_getTTL(JNIEnv *env, jobject this) { 1830 return (jbyte)Java_java_net_PlainDatagramSocketImpl_getTimeToLive(env, this); 1831 } 1832 1833 1834 /* 1835 * Class: java_net_PlainDatagramSocketImpl 1836 * Method: getTTL 1837 * Signature: ()B 1838 */ 1839 JNIEXPORT jint JNICALL 1840 Java_java_net_PlainDatagramSocketImpl_getTimeToLive(JNIEnv *env, jobject this) { 1841 1842 jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID); 1843 jint fd = -1; 1844 1845 if (IS_NULL(fdObj)) { 1846 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", 1847 "Socket closed"); 1848 return -1; 1849 } else { 1850 fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID); 1851 } 1852 /* getsockopt of TTL */ 1853 if (ipv6_available()) { 1854 int ttl = 0; 1855 socklen_t len = sizeof(ttl); 1856 1857 if (getsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, 1858 (char*)&ttl, &len) < 0) { 1859 JNU_ThrowByNameWithMessageAndLastError 1860 (env, JNU_JAVANETPKG "SocketException", "Error getting socket option"); 1861 return -1; 1862 } 1863 return (jint)ttl; 1864 } else { 1865 u_char ttl = 0; 1866 socklen_t len = sizeof(ttl); 1867 if (getsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, 1868 (char*)&ttl, &len) < 0) { 1869 JNU_ThrowByNameWithMessageAndLastError 1870 (env, JNU_JAVANETPKG "SocketException", "Error getting socket option"); 1871 return -1; 1872 } 1873 return (jint)ttl; 1874 } 1875 } 1876 1877 1878 /* 1879 * mcast_join_leave: Join or leave a multicast group. 1880 * 1881 * For IPv4 sockets use IP_ADD_MEMBERSHIP/IP_DROP_MEMBERSHIP socket option 1882 * to join/leave multicast group. 1883 * 1884 * For IPv6 sockets use IPV6_ADD_MEMBERSHIP/IPV6_DROP_MEMBERSHIP socket option 1885 * to join/leave multicast group. If multicast group is an IPv4 address then 1886 * an IPv4-mapped address is used. 1887 * 1888 * On Linux with IPv6 if we wish to join/leave an IPv4 multicast group then 1889 * we must use the IPv4 socket options. This is because the IPv6 socket options 1890 * don't support IPv4-mapped addresses. This is true as per 2.2.19 and 2.4.7 1891 * kernel releases. In the future it's possible that IP_ADD_MEMBERSHIP 1892 * will be updated to return ENOPROTOOPT if uses with an IPv6 socket (Solaris 1893 * already does this). Thus to cater for this we first try with the IPv4 1894 * socket options and if they fail we use the IPv6 socket options. This 1895 * seems a reasonable failsafe solution. 1896 */ 1897 static void mcast_join_leave(JNIEnv *env, jobject this, 1898 jobject iaObj, jobject niObj, 1899 jboolean join) { 1900 1901 jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID); 1902 jint fd; 1903 jint family; 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 ipv6_join_leave = ipv6_available(); 1922 1923 #ifdef __linux__ 1924 family = getInetAddress_family(env, iaObj); 1925 JNU_CHECK_EXCEPTION(env); 1926 if (family == java_net_InetAddress_IPv4) { 1927 ipv6_join_leave = JNI_FALSE; 1928 } 1929 #endif 1930 1931 /* 1932 * For IPv4 join use IP_ADD_MEMBERSHIP/IP_DROP_MEMBERSHIP socket option 1933 * 1934 * On Linux if IPv4 or IPv6 use IP_ADD_MEMBERSHIP/IP_DROP_MEMBERSHIP 1935 */ 1936 if (!ipv6_join_leave) { 1937 #ifdef __linux__ 1938 struct ip_mreqn mname; 1939 #else 1940 struct ip_mreq mname; 1941 #endif 1942 int mname_len; 1943 1944 /* 1945 * joinGroup(InetAddress, NetworkInterface) implementation :- 1946 * 1947 * Linux/IPv6: use ip_mreqn structure populated with multicast 1948 * address and interface index. 1949 * 1950 * IPv4: use ip_mreq structure populated with multicast 1951 * address and first address obtained from 1952 * NetworkInterface 1953 */ 1954 if (niObj != NULL) { 1955 #if defined(__linux__) 1956 if (ipv6_available()) { 1957 static jfieldID ni_indexID; 1958 1959 if (ni_indexID == NULL) { 1960 jclass c = (*env)->FindClass(env, "java/net/NetworkInterface"); 1961 CHECK_NULL(c); 1962 ni_indexID = (*env)->GetFieldID(env, c, "index", "I"); 1963 CHECK_NULL(ni_indexID); 1964 } 1965 1966 mname.imr_multiaddr.s_addr = htonl(getInetAddress_addr(env, iaObj)); 1967 JNU_CHECK_EXCEPTION(env); 1968 mname.imr_address.s_addr = 0; 1969 mname.imr_ifindex = (*env)->GetIntField(env, niObj, ni_indexID); 1970 mname_len = sizeof(struct ip_mreqn); 1971 } else 1972 #endif 1973 { 1974 jobjectArray addrArray = (*env)->GetObjectField(env, niObj, ni_addrsID); 1975 jobject addr; 1976 1977 if ((*env)->GetArrayLength(env, addrArray) < 1) { 1978 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", 1979 "bad argument for IP_ADD_MEMBERSHIP: " 1980 "No IP addresses bound to interface"); 1981 return; 1982 } 1983 addr = (*env)->GetObjectArrayElement(env, addrArray, 0); 1984 1985 mname.imr_multiaddr.s_addr = htonl(getInetAddress_addr(env, iaObj)); 1986 JNU_CHECK_EXCEPTION(env); 1987 #ifdef __linux__ 1988 mname.imr_address.s_addr = htonl(getInetAddress_addr(env, addr)); 1989 JNU_CHECK_EXCEPTION(env); 1990 mname.imr_ifindex = 0; 1991 #else 1992 mname.imr_interface.s_addr = htonl(getInetAddress_addr(env, addr)); 1993 JNU_CHECK_EXCEPTION(env); 1994 #endif 1995 mname_len = sizeof(struct ip_mreq); 1996 } 1997 } 1998 1999 2000 /* 2001 * joinGroup(InetAddress) implementation :- 2002 * 2003 * Linux/IPv6: use ip_mreqn structure populated with multicast 2004 * address and interface index. index obtained 2005 * from cached value or IPV6_MULTICAST_IF. 2006 * 2007 * IPv4: use ip_mreq structure populated with multicast 2008 * address and local address obtained from 2009 * IP_MULTICAST_IF. On Linux IP_MULTICAST_IF 2010 * returns different structure depending on 2011 * kernel. 2012 */ 2013 2014 if (niObj == NULL) { 2015 2016 #if defined(__linux__) 2017 if (ipv6_available()) { 2018 2019 int index; 2020 socklen_t len = sizeof(index); 2021 2022 if (getsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, 2023 (char*)&index, &len) < 0) { 2024 NET_ThrowCurrent(env, "getsockopt IPV6_MULTICAST_IF failed"); 2025 return; 2026 } 2027 2028 mname.imr_multiaddr.s_addr = htonl(getInetAddress_addr(env, iaObj)); 2029 JNU_CHECK_EXCEPTION(env); 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 mname.imr_ifindex = 0; 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 JNU_CHECK_EXCEPTION(env); 2053 mname_len = sizeof(struct ip_mreq); 2054 } 2055 } 2056 2057 2058 /* 2059 * Join the multicast group. 2060 */ 2061 if (setsockopt(fd, IPPROTO_IP, (join ? IP_ADD_MEMBERSHIP:IP_DROP_MEMBERSHIP), 2062 (char *) &mname, mname_len) < 0) { 2063 2064 /* 2065 * If IP_ADD_MEMBERSHIP returns ENOPROTOOPT on Linux and we've got 2066 * IPv6 enabled then it's possible that the kernel has been fixed 2067 * so we switch to IPV6_ADD_MEMBERSHIP socket option. 2068 * As of 2.4.7 kernel IPV6_ADD_MEMBERSHIP can't handle IPv4-mapped 2069 * addresses so we have to use IP_ADD_MEMBERSHIP for IPv4 multicast 2070 * groups. However if the socket is an IPv6 socket then setsockopt 2071 * should return ENOPROTOOPT. We assume this will be fixed in Linux 2072 * at some stage. 2073 */ 2074 #if defined(__linux__) 2075 if (errno == ENOPROTOOPT) { 2076 if (ipv6_available()) { 2077 ipv6_join_leave = JNI_TRUE; 2078 errno = 0; 2079 } else { 2080 errno = ENOPROTOOPT; /* errno can be changed by ipv6_available */ 2081 } 2082 } 2083 #endif 2084 if (errno) { 2085 if (join) { 2086 NET_ThrowCurrent(env, "setsockopt IP_ADD_MEMBERSHIP failed"); 2087 } else { 2088 if (errno == ENOENT) 2089 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", 2090 "Not a member of the multicast group"); 2091 else 2092 NET_ThrowCurrent(env, "setsockopt IP_DROP_MEMBERSHIP failed"); 2093 } 2094 return; 2095 } 2096 } 2097 2098 /* 2099 * If we haven't switched to IPv6 socket option then we're done. 2100 */ 2101 if (!ipv6_join_leave) { 2102 return; 2103 } 2104 } 2105 2106 2107 /* 2108 * IPv6 join. If it's an IPv4 multicast group then we use an IPv4-mapped 2109 * address. 2110 */ 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) == java_net_InetAddress_IPv4 ? 2118 AF_INET : AF_INET6; 2119 JNU_CHECK_EXCEPTION(env); 2120 if (family == AF_INET) { /* will convert to IPv4-mapped address */ 2121 memset((char *) caddr, 0, 16); 2122 address = getInetAddress_addr(env, iaObj); 2123 JNU_CHECK_EXCEPTION(env); 2124 caddr[10] = 0xff; 2125 caddr[11] = 0xff; 2126 2127 caddr[12] = ((address >> 24) & 0xff); 2128 caddr[13] = ((address >> 16) & 0xff); 2129 caddr[14] = ((address >> 8) & 0xff); 2130 caddr[15] = (address & 0xff); 2131 } else { 2132 getInet6Address_ipaddress(env, iaObj, (char*)caddr); 2133 } 2134 2135 memcpy((void *)&(mname6.ipv6mr_multiaddr), caddr, sizeof(struct in6_addr)); 2136 if (IS_NULL(niObj)) { 2137 int index; 2138 socklen_t len = sizeof(index); 2139 2140 if (getsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, 2141 (char*)&index, &len) < 0) { 2142 NET_ThrowCurrent(env, "getsockopt IPV6_MULTICAST_IF failed"); 2143 return; 2144 } 2145 2146 #ifdef __linux__ 2147 /* 2148 * On 2.4.8+ if we join a group with the interface set to 0 2149 * then the kernel records the interface it decides. This causes 2150 * subsequent leave groups to fail as there is no match. Thus we 2151 * pick the interface if there is a matching route. 2152 */ 2153 if (index == 0) { 2154 int rt_index = getDefaultIPv6Interface(&(mname6.ipv6mr_multiaddr)); 2155 if (rt_index > 0) { 2156 index = rt_index; 2157 } 2158 } 2159 #endif 2160 #ifdef MACOSX 2161 if (family == AF_INET6 && index == 0) { 2162 index = getDefaultScopeID(env); 2163 } 2164 #endif 2165 mname6.ipv6mr_interface = index; 2166 } else { 2167 jint idx = (*env)->GetIntField(env, niObj, ni_indexID); 2168 mname6.ipv6mr_interface = idx; 2169 } 2170 2171 #if defined(_ALLBSD_SOURCE) 2172 #define ADD_MEMBERSHIP IPV6_JOIN_GROUP 2173 #define DRP_MEMBERSHIP IPV6_LEAVE_GROUP 2174 #define S_ADD_MEMBERSHIP "IPV6_JOIN_GROUP" 2175 #define S_DRP_MEMBERSHIP "IPV6_LEAVE_GROUP" 2176 #else 2177 #define ADD_MEMBERSHIP IPV6_ADD_MEMBERSHIP 2178 #define DRP_MEMBERSHIP IPV6_DROP_MEMBERSHIP 2179 #define S_ADD_MEMBERSHIP "IPV6_ADD_MEMBERSHIP" 2180 #define S_DRP_MEMBERSHIP "IPV6_DROP_MEMBERSHIP" 2181 #endif 2182 2183 /* Join the multicast group */ 2184 if (setsockopt(fd, IPPROTO_IPV6, (join ? ADD_MEMBERSHIP : DRP_MEMBERSHIP), 2185 (char *) &mname6, sizeof (mname6)) < 0) { 2186 2187 if (join) { 2188 NET_ThrowCurrent(env, "setsockopt " S_ADD_MEMBERSHIP " failed"); 2189 } else { 2190 if (errno == ENOENT) { 2191 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", 2192 "Not a member of the multicast group"); 2193 } else { 2194 NET_ThrowCurrent(env, "setsockopt " S_DRP_MEMBERSHIP " failed"); 2195 } 2196 } 2197 } 2198 } 2199 } 2200 2201 /* 2202 * Class: java_net_PlainDatagramSocketImpl 2203 * Method: join 2204 * Signature: (Ljava/net/InetAddress;)V 2205 */ 2206 JNIEXPORT void JNICALL 2207 Java_java_net_PlainDatagramSocketImpl_join(JNIEnv *env, jobject this, 2208 jobject iaObj, jobject niObj) 2209 { 2210 mcast_join_leave(env, this, iaObj, niObj, JNI_TRUE); 2211 } 2212 2213 /* 2214 * Class: java_net_PlainDatagramSocketImpl 2215 * Method: leave 2216 * Signature: (Ljava/net/InetAddress;)V 2217 */ 2218 JNIEXPORT void JNICALL 2219 Java_java_net_PlainDatagramSocketImpl_leave(JNIEnv *env, jobject this, 2220 jobject iaObj, jobject niObj) 2221 { 2222 mcast_join_leave(env, this, iaObj, niObj, JNI_FALSE); 2223 } 2224 2225 /* 2226 * Class: java_net_PlainDatagramSocketImpl 2227 * Method: dataAvailable 2228 * Signature: ()I 2229 */ 2230 JNIEXPORT jint JNICALL 2231 Java_java_net_PlainDatagramSocketImpl_dataAvailable(JNIEnv *env, jobject this) 2232 { 2233 int fd, retval; 2234 2235 jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID); 2236 2237 if (IS_NULL(fdObj)) { 2238 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", 2239 "Socket closed"); 2240 return -1; 2241 } 2242 fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID); 2243 2244 if (ioctl(fd, FIONREAD, &retval) < 0) { 2245 return -1; 2246 } 2247 return retval; 2248 }