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