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