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