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