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