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