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