PlainDatagramSocketImpl.c revision f7ab2bc37debba91864bfec6572a3e7bbe994c58
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 */ 1174static void mcast_set_if_by_if_v4(JNIEnv *env, jobject this, int fd, jobject value) { 1175 static jfieldID ni_addrsID; 1176 struct in_addr in; 1177 jobjectArray addrArray; 1178 jsize len; 1179 jobject addr; 1180 int i; 1181 1182 if (ni_addrsID == NULL ) { 1183 jclass c = (*env)->FindClass(env, "java/net/NetworkInterface"); 1184 CHECK_NULL(c); 1185 ni_addrsID = (*env)->GetFieldID(env, c, "addrs", 1186 "[Ljava/net/InetAddress;"); 1187 CHECK_NULL(ni_addrsID); 1188 } 1189 1190 addrArray = (*env)->GetObjectField(env, value, ni_addrsID); 1191 len = (*env)->GetArrayLength(env, addrArray); 1192 1193 /* 1194 * Check that there is at least one address bound to this 1195 * interface. 1196 */ 1197 if (len < 1) { 1198 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", 1199 "bad argument for IP_MULTICAST_IF2: No IP addresses bound to interface"); 1200 return; 1201 } 1202 1203 /* 1204 * We need an ipv4 address here 1205 */ 1206 for (i = 0; i < len; i++) { 1207 addr = (*env)->GetObjectArrayElement(env, addrArray, i); 1208 if (getInetAddress_family(env, addr) == IPv4) { 1209 in.s_addr = htonl(getInetAddress_addr(env, addr)); 1210 break; 1211 } 1212 } 1213 1214 if (JVM_SetSockOpt(fd, IPPROTO_IP, IP_MULTICAST_IF, 1215 (const char*)&in, sizeof(in)) < 0) { 1216 NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", 1217 "Error setting socket option"); 1218 } 1219} 1220 1221/* 1222 * Set outgoing multicast interface designated by a NetworkInterface. 1223 * Throw exception if failed. 1224 */ 1225#ifdef AF_INET6 1226static void mcast_set_if_by_if_v6(JNIEnv *env, jobject this, int fd, jobject value) { 1227 static jfieldID ni_indexID; 1228 int index; 1229 1230 if (ni_indexID == NULL) { 1231 jclass c = (*env)->FindClass(env, "java/net/NetworkInterface"); 1232 CHECK_NULL(c); 1233 ni_indexID = (*env)->GetFieldID(env, c, "index", "I"); 1234 CHECK_NULL(ni_indexID); 1235 } 1236 index = (*env)->GetIntField(env, value, ni_indexID); 1237 1238 if (JVM_SetSockOpt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, 1239 (const char*)&index, sizeof(index)) < 0) { 1240 if (errno == EINVAL && index > 0) { 1241 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", 1242 "IPV6_MULTICAST_IF failed (interface has IPv4 " 1243 "address only?)"); 1244 } else { 1245 NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", 1246 "Error setting socket option"); 1247 } 1248 return; 1249 } 1250 1251#ifdef __linux__ 1252 /* 1253 * Linux 2.2 kernel doesn't support IPV6_MULTICAST_IF socket 1254 * option so record index for later retrival. 1255 */ 1256 if (isOldKernel) { 1257 (*env)->SetIntField(env, this, pdsi_multicastInterfaceID, 1258 (jint)index); 1259 } 1260#endif 1261} 1262#endif /* AF_INET6 */ 1263 1264/* 1265 * Set outgoing multicast interface designated by an InetAddress. 1266 * Throw exception if failed. 1267 */ 1268static void mcast_set_if_by_addr_v4(JNIEnv *env, jobject this, int fd, jobject value) { 1269 struct in_addr in; 1270 1271 in.s_addr = htonl( getInetAddress_addr(env, value) ); 1272 1273 if (JVM_SetSockOpt(fd, IPPROTO_IP, IP_MULTICAST_IF, 1274 (const char*)&in, sizeof(in)) < 0) { 1275 NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", 1276 "Error setting socket option"); 1277 } 1278} 1279 1280/* 1281 * Set outgoing multicast interface designated by an InetAddress. 1282 * Throw exception if failed. 1283 */ 1284#ifdef AF_INET6 1285static void mcast_set_if_by_addr_v6(JNIEnv *env, jobject this, int fd, jobject value) { 1286 static jclass ni_class; 1287 if (ni_class == NULL) { 1288 jclass c = (*env)->FindClass(env, "java/net/NetworkInterface"); 1289 CHECK_NULL(c); 1290 ni_class = (*env)->NewGlobalRef(env, c); 1291 CHECK_NULL(ni_class); 1292 } 1293 1294 value = NetworkInterface_getByInetAddress0(env, ni_class, value); 1295 if (value == NULL) { 1296 if (!(*env)->ExceptionOccurred(env)) { 1297 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", 1298 "bad argument for IP_MULTICAST_IF" 1299 ": address not bound to any interface"); 1300 } 1301 return; 1302 } 1303 1304 mcast_set_if_by_if_v6(env, this, fd, value); 1305} 1306#endif 1307 1308/* 1309 * Sets the multicast interface. 1310 * 1311 * SocketOptions.IP_MULTICAST_IF :- 1312 * value is a InetAddress 1313 * IPv4: set outgoing multicast interface using 1314 * IPPROTO_IP/IP_MULTICAST_IF 1315 * IPv6: Get the index of the interface to which the 1316 * InetAddress is bound 1317 * Set outgoing multicast interface using 1318 * IPPROTO_IPV6/IPV6_MULTICAST_IF 1319 * On Linux 2.2 record interface index as can't 1320 * query the multicast interface. 1321 * 1322 * SockOptions.IF_MULTICAST_IF2 :- 1323 * value is a NetworkInterface 1324 * IPv4: Obtain IP address bound to network interface 1325 * (NetworkInterface.addres[0]) 1326 * set outgoing multicast interface using 1327 * IPPROTO_IP/IP_MULTICAST_IF 1328 * IPv6: Obtain NetworkInterface.index 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 */ 1335static void setMulticastInterface(JNIEnv *env, jobject this, int fd, 1336 jint opt, jobject value) 1337{ 1338 if (opt == java_net_SocketOptions_IP_MULTICAST_IF) { 1339 /* 1340 * value is an InetAddress. 1341 */ 1342#ifdef AF_INET6 1343#ifdef __linux__ 1344 mcast_set_if_by_addr_v4(env, this, fd, value); 1345 if (ipv6_available()) { 1346 mcast_set_if_by_addr_v6(env, this, fd, value); 1347 } 1348#else /* __linux__ not defined */ 1349 if (ipv6_available()) { 1350 mcast_set_if_by_addr_v6(env, this, fd, value); 1351 } else { 1352 mcast_set_if_by_addr_v4(env, this, fd, value); 1353 } 1354#endif /* __linux__ */ 1355#else 1356 mcast_set_if_by_addr_v4(env, this, fd, value); 1357#endif /* AF_INET6 */ 1358 } 1359 1360 if (opt == java_net_SocketOptions_IP_MULTICAST_IF2) { 1361 /* 1362 * value is a NetworkInterface. 1363 */ 1364#ifdef AF_INET6 1365#ifdef __linux__ 1366 mcast_set_if_by_if_v4(env, this, fd, value); 1367 if (ipv6_available()) { 1368 mcast_set_if_by_if_v6(env, this, fd, value); 1369 } 1370#else /* __linux__ not defined */ 1371 if (ipv6_available()) { 1372 mcast_set_if_by_if_v6(env, this, fd, value); 1373 } else { 1374 mcast_set_if_by_if_v4(env, this, fd, value); 1375 } 1376#endif /* __linux__ */ 1377#else 1378 mcast_set_if_by_if_v4(env, this, fd, value); 1379#endif /* AF_INET6 */ 1380 } 1381} 1382 1383/* 1384 * Enable/disable local loopback of multicast datagrams. 1385 */ 1386static void mcast_set_loop_v4(JNIEnv *env, jobject this, int fd, jobject value) { 1387 jclass cls; 1388 jfieldID fid; 1389 jboolean on; 1390 char loopback; 1391 1392 cls = (*env)->FindClass(env, "java/lang/Boolean"); 1393 CHECK_NULL(cls); 1394 fid = (*env)->GetFieldID(env, cls, "value", "Z"); 1395 CHECK_NULL(fid); 1396 1397 on = (*env)->GetBooleanField(env, value, fid); 1398 loopback = (!on ? 1 : 0); 1399 1400 if (NET_SetSockOpt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, (const void *)&loopback, sizeof(char)) < 0) { 1401 NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "Error setting socket option"); 1402 return; 1403 } 1404} 1405 1406/* 1407 * Enable/disable local loopback of multicast datagrams. 1408 */ 1409#ifdef AF_INET6 1410static void mcast_set_loop_v6(JNIEnv *env, jobject this, int fd, jobject value) { 1411 jclass cls; 1412 jfieldID fid; 1413 jboolean on; 1414 int loopback; 1415 1416 cls = (*env)->FindClass(env, "java/lang/Boolean"); 1417 CHECK_NULL(cls); 1418 fid = (*env)->GetFieldID(env, cls, "value", "Z"); 1419 CHECK_NULL(fid); 1420 1421 on = (*env)->GetBooleanField(env, value, fid); 1422 loopback = (!on ? 1 : 0); 1423 1424 if (NET_SetSockOpt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (const void *)&loopback, sizeof(int)) < 0) { 1425 NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "Error setting socket option"); 1426 return; 1427 } 1428 1429#ifdef __linux__ 1430 /* 1431 * Can't query IPV6_MULTICAST_LOOP on Linux 2.2 kernel so 1432 * store it in impl so that we can simulate getsockopt. 1433 */ 1434 if (isOldKernel) { 1435 (*env)->SetBooleanField(env, this, pdsi_loopbackID, on); 1436 } 1437#endif 1438} 1439#endif /* AF_INET6 */ 1440 1441/* 1442 * Sets the multicast loopback mode. 1443 */ 1444static void setMulticastLoopbackMode(JNIEnv *env, jobject this, int fd, 1445 jint opt, jobject value) { 1446#ifdef AF_INET6 1447#ifdef __linux__ 1448 mcast_set_loop_v4(env, this, fd, value); 1449 if (ipv6_available()) { 1450 mcast_set_loop_v6(env, this, fd, value); 1451 } 1452#else /* __linux__ not defined */ 1453 if (ipv6_available()) { 1454 mcast_set_loop_v6(env, this, fd, value); 1455 } else { 1456 mcast_set_loop_v4(env, this, fd, value); 1457 } 1458#endif /* __linux__ */ 1459#else 1460 mcast_set_loop_v4(env, this, fd, value); 1461#endif /* AF_INET6 */ 1462} 1463 1464/* 1465 * Class: java_net_PlainDatagramSocketImpl 1466 * Method: socketSetOption 1467 * Signature: (ILjava/lang/Object;)V 1468 */ 1469JNIEXPORT void JNICALL 1470PlainDatagramSocketImpl_socketSetOption(JNIEnv *env, 1471 jobject this, 1472 jint opt, 1473 jobject value) { 1474 int fd; 1475 int level, optname, optlen; 1476 union { 1477 int i; 1478 char c; 1479 } optval; 1480 1481 /* 1482 * Check that socket hasn't been closed 1483 */ 1484 fd = getFD(env, this); 1485 if (fd < 0) { 1486 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", 1487 "Socket closed"); 1488 return; 1489 } 1490 1491 /* 1492 * Check argument has been provided 1493 */ 1494 if (IS_NULL(value)) { 1495 JNU_ThrowNullPointerException(env, "value argument"); 1496 return; 1497 } 1498 1499 /* 1500 * Setting the multicast interface handled seperately 1501 */ 1502 if (opt == java_net_SocketOptions_IP_MULTICAST_IF || 1503 opt == java_net_SocketOptions_IP_MULTICAST_IF2) { 1504 1505 setMulticastInterface(env, this, fd, opt, value); 1506 return; 1507 } 1508 1509 /* 1510 * Setting the multicast loopback mode handled separately 1511 */ 1512 if (opt == java_net_SocketOptions_IP_MULTICAST_LOOP) { 1513 setMulticastLoopbackMode(env, this, fd, opt, value); 1514 return; 1515 } 1516 1517 /* 1518 * Map the Java level socket option to the platform specific 1519 * level and option name. 1520 */ 1521 if (NET_MapSocketOption(opt, &level, &optname)) { 1522 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Invalid option"); 1523 return; 1524 } 1525 1526 switch (opt) { 1527 case java_net_SocketOptions_SO_SNDBUF : 1528 case java_net_SocketOptions_SO_RCVBUF : 1529 case java_net_SocketOptions_IP_TOS : 1530 { 1531 jclass cls; 1532 jfieldID fid; 1533 1534 cls = (*env)->FindClass(env, "java/lang/Integer"); 1535 CHECK_NULL(cls); 1536 fid = (*env)->GetFieldID(env, cls, "value", "I"); 1537 CHECK_NULL(fid); 1538 1539 optval.i = (*env)->GetIntField(env, value, fid); 1540 optlen = sizeof(optval.i); 1541 break; 1542 } 1543 1544 case java_net_SocketOptions_SO_REUSEADDR: 1545 case java_net_SocketOptions_SO_BROADCAST: 1546 { 1547 jclass cls; 1548 jfieldID fid; 1549 jboolean on; 1550 1551 cls = (*env)->FindClass(env, "java/lang/Boolean"); 1552 CHECK_NULL(cls); 1553 fid = (*env)->GetFieldID(env, cls, "value", "Z"); 1554 CHECK_NULL(fid); 1555 1556 on = (*env)->GetBooleanField(env, value, fid); 1557 1558 /* SO_REUSEADDR or SO_BROADCAST */ 1559 optval.i = (on ? 1 : 0); 1560 optlen = sizeof(optval.i); 1561 1562 break; 1563 } 1564 1565 default : 1566 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", 1567 "Socket option not supported by PlainDatagramSocketImp"); 1568 break; 1569 1570 } 1571 1572 if (NET_SetSockOpt(fd, level, optname, (const void *)&optval, optlen) < 0) { 1573 NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "Error setting socket option"); 1574 return; 1575 } 1576} 1577 1578 1579/* 1580 * Return the multicast interface: 1581 * 1582 * SocketOptions.IP_MULTICAST_IF 1583 * IPv4: Query IPPROTO_IP/IP_MULTICAST_IF 1584 * Create InetAddress 1585 * IP_MULTICAST_IF returns struct ip_mreqn on 2.2 1586 * kernel but struct in_addr on 2.4 kernel 1587 * IPv6: Query IPPROTO_IPV6 / IPV6_MULTICAST_IF or 1588 * obtain from impl is Linux 2.2 kernel 1589 * If index == 0 return InetAddress representing 1590 * anyLocalAddress. 1591 * If index > 0 query NetworkInterface by index 1592 * and returns addrs[0] 1593 * 1594 * SocketOptions.IP_MULTICAST_IF2 1595 * IPv4: Query IPPROTO_IP/IP_MULTICAST_IF 1596 * Query NetworkInterface by IP address and 1597 * return the NetworkInterface that the address 1598 * is bound too. 1599 * IPv6: Query IPPROTO_IPV6 / IPV6_MULTICAST_IF 1600 * (except Linux .2 kernel) 1601 * Query NetworkInterface by index and 1602 * return NetworkInterface. 1603 */ 1604jobject getMulticastInterface(JNIEnv *env, jobject this, int fd, jint opt) { 1605 jboolean isIPV4 = JNI_TRUE; 1606 1607#ifdef AF_INET6 1608 if (ipv6_available()) { 1609 isIPV4 = JNI_FALSE; 1610 } 1611#endif 1612 1613 /* 1614 * IPv4 implementation 1615 */ 1616 if (isIPV4) { 1617 static jclass inet4_class; 1618 static jmethodID inet4_ctrID; 1619 1620 static jclass ni_class; 1621 static jmethodID ni_ctrID; 1622 static jfieldID ni_indexID; 1623 static jfieldID ni_addrsID; 1624 1625 jobjectArray addrArray; 1626 jobject addr; 1627 jobject ni; 1628 1629 struct in_addr in; 1630 struct in_addr *inP = ∈ 1631 int len = sizeof(struct in_addr); 1632 1633#ifdef __linux__ 1634 struct ip_mreqn mreqn; 1635 if (isOldKernel) { 1636 inP = (struct in_addr *)&mreqn; 1637 len = sizeof(struct ip_mreqn); 1638 } 1639#endif 1640 1641 if (JVM_GetSockOpt(fd, IPPROTO_IP, IP_MULTICAST_IF, 1642 (char *)inP, &len) < 0) { 1643 NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", 1644 "Error getting socket option"); 1645 return NULL; 1646 } 1647 1648 /* 1649 * Construct and populate an Inet4Address 1650 */ 1651 if (inet4_class == NULL) { 1652 jclass c = (*env)->FindClass(env, "java/net/Inet4Address"); 1653 CHECK_NULL_RETURN(c, NULL); 1654 inet4_ctrID = (*env)->GetMethodID(env, c, "<init>", "()V"); 1655 CHECK_NULL_RETURN(inet4_ctrID, NULL); 1656 inet4_class = (*env)->NewGlobalRef(env, c); 1657 CHECK_NULL_RETURN(inet4_class, NULL); 1658 } 1659 addr = (*env)->NewObject(env, inet4_class, inet4_ctrID, 0); 1660 CHECK_NULL_RETURN(addr, NULL); 1661 1662#ifdef __linux__ 1663 setInetAddress_addr(env, addr, (isOldKernel ? 1664 ntohl(mreqn.imr_address.s_addr) : ntohl(in.s_addr))); 1665#else 1666 setInetAddress_addr(env, addr, ntohl(in.s_addr)); 1667#endif 1668 1669 /* 1670 * For IP_MULTICAST_IF return InetAddress 1671 */ 1672 if (opt == java_net_SocketOptions_IP_MULTICAST_IF) { 1673 return addr; 1674 } 1675 1676 /* 1677 * For IP_MULTICAST_IF2 we get the NetworkInterface for 1678 * this address and return it 1679 */ 1680 if (ni_class == NULL) { 1681 jclass c = (*env)->FindClass(env, "java/net/NetworkInterface"); 1682 CHECK_NULL_RETURN(c, NULL); 1683 ni_ctrID = (*env)->GetMethodID(env, c, "<init>", "()V"); 1684 CHECK_NULL_RETURN(ni_ctrID, NULL); 1685 ni_indexID = (*env)->GetFieldID(env, c, "index", "I"); 1686 CHECK_NULL_RETURN(ni_indexID, NULL); 1687 ni_addrsID = (*env)->GetFieldID(env, c, "addrs", 1688 "[Ljava/net/InetAddress;"); 1689 CHECK_NULL_RETURN(ni_addrsID, NULL); 1690 ni_class = (*env)->NewGlobalRef(env, c); 1691 CHECK_NULL_RETURN(ni_class, NULL); 1692 } 1693 ni = NetworkInterface_getByInetAddress0(env, ni_class, addr); 1694 if (ni) { 1695 return ni; 1696 } 1697 1698 /* 1699 * The address doesn't appear to be bound at any known 1700 * NetworkInterface. Therefore we construct a NetworkInterface 1701 * with this address. 1702 */ 1703 ni = (*env)->NewObject(env, ni_class, ni_ctrID, 0); 1704 CHECK_NULL_RETURN(ni, NULL); 1705 1706 (*env)->SetIntField(env, ni, ni_indexID, -1); 1707 addrArray = (*env)->NewObjectArray(env, 1, inet4_class, NULL); 1708 CHECK_NULL_RETURN(addrArray, NULL); 1709 (*env)->SetObjectArrayElement(env, addrArray, 0, addr); 1710 (*env)->SetObjectField(env, ni, ni_addrsID, addrArray); 1711 return ni; 1712 } 1713 1714 1715#ifdef AF_INET6 1716 /* 1717 * IPv6 implementation 1718 */ 1719 if ((opt == java_net_SocketOptions_IP_MULTICAST_IF) || 1720 (opt == java_net_SocketOptions_IP_MULTICAST_IF2)) { 1721 1722 static jclass ni_class; 1723 static jmethodID ni_ctrID; 1724 static jfieldID ni_indexID; 1725 static jfieldID ni_addrsID; 1726 static jclass ia_class; 1727 static jmethodID ia_anyLocalAddressID; 1728 1729 int index; 1730 int len = sizeof(index); 1731 1732 jobjectArray addrArray; 1733 jobject addr; 1734 jobject ni; 1735 1736#ifdef __linux__ 1737 /* 1738 * Linux 2.2 kernel doesn't support IPV6_MULTICAST_IF socke option 1739 * so use cached index. 1740 */ 1741 if (isOldKernel) { 1742 index = (*env)->GetIntField(env, this, pdsi_multicastInterfaceID); 1743 } else 1744#endif 1745 { 1746 if (JVM_GetSockOpt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, 1747 (char*)&index, &len) < 0) { 1748 NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", 1749 "Error getting socket option"); 1750 return NULL; 1751 } 1752 } 1753 1754 if (ni_class == NULL) { 1755 jclass c = (*env)->FindClass(env, "java/net/NetworkInterface"); 1756 CHECK_NULL_RETURN(c, NULL); 1757 ni_ctrID = (*env)->GetMethodID(env, c, "<init>", "()V"); 1758 CHECK_NULL_RETURN(ni_ctrID, NULL); 1759 ni_indexID = (*env)->GetFieldID(env, c, "index", "I"); 1760 CHECK_NULL_RETURN(ni_indexID, NULL); 1761 ni_addrsID = (*env)->GetFieldID(env, c, "addrs", 1762 "[Ljava/net/InetAddress;"); 1763 CHECK_NULL_RETURN(ni_addrsID, NULL); 1764 1765 ia_class = (*env)->FindClass(env, "java/net/InetAddress"); 1766 CHECK_NULL_RETURN(ia_class, NULL); 1767 ia_class = (*env)->NewGlobalRef(env, ia_class); 1768 CHECK_NULL_RETURN(ia_class, NULL); 1769 ia_anyLocalAddressID = (*env)->GetStaticMethodID(env, 1770 ia_class, 1771 "anyLocalAddress", 1772 "()Ljava/net/InetAddress;"); 1773 CHECK_NULL_RETURN(ia_anyLocalAddressID, NULL); 1774 ni_class = (*env)->NewGlobalRef(env, c); 1775 CHECK_NULL_RETURN(ni_class, NULL); 1776 } 1777 1778 /* 1779 * If multicast to a specific interface then return the 1780 * interface (for IF2) or the any address on that interface 1781 * (for IF). 1782 */ 1783 if (index > 0) { 1784 ni = NetworkInterface_getByIndex0(env, ni_class, 1785 index); 1786 if (ni == NULL) { 1787 char errmsg[255]; 1788 sprintf(errmsg, 1789 "IPV6_MULTICAST_IF returned index to unrecognized interface: %d", 1790 index); 1791 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", errmsg); 1792 return NULL; 1793 } 1794 1795 /* 1796 * For IP_MULTICAST_IF2 return the NetworkInterface 1797 */ 1798 if (opt == java_net_SocketOptions_IP_MULTICAST_IF2) { 1799 return ni; 1800 } 1801 1802 /* 1803 * For IP_MULTICAST_IF return addrs[0] 1804 */ 1805 addrArray = (*env)->GetObjectField(env, ni, ni_addrsID); 1806 if ((*env)->GetArrayLength(env, addrArray) < 1) { 1807 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", 1808 "IPV6_MULTICAST_IF returned interface without IP bindings"); 1809 return NULL; 1810 } 1811 1812 addr = (*env)->GetObjectArrayElement(env, addrArray, 0); 1813 return addr; 1814 } 1815 1816 /* 1817 * Multicast to any address - return anyLocalAddress 1818 * or a NetworkInterface with addrs[0] set to anyLocalAddress 1819 */ 1820 1821 addr = (*env)->CallStaticObjectMethod(env, ia_class, ia_anyLocalAddressID, 1822 NULL); 1823 if (opt == java_net_SocketOptions_IP_MULTICAST_IF) { 1824 return addr; 1825 } 1826 1827 ni = (*env)->NewObject(env, ni_class, ni_ctrID, 0); 1828 CHECK_NULL_RETURN(ni, NULL); 1829 (*env)->SetIntField(env, ni, ni_indexID, -1); 1830 addrArray = (*env)->NewObjectArray(env, 1, ia_class, NULL); 1831 CHECK_NULL_RETURN(addrArray, NULL); 1832 (*env)->SetObjectArrayElement(env, addrArray, 0, addr); 1833 (*env)->SetObjectField(env, ni, ni_addrsID, addrArray); 1834 return ni; 1835 } 1836#endif 1837 return NULL; 1838} 1839 1840 1841 1842/* 1843 * Returns relevant info as a jint. 1844 * 1845 * Class: java_net_PlainDatagramSocketImpl 1846 * Method: socketGetOption 1847 * Signature: (I)Ljava/lang/Object; 1848 */ 1849JNIEXPORT jobject JNICALL 1850PlainDatagramSocketImpl_socketGetOption(JNIEnv *env, jobject this, 1851 jint opt) { 1852 int fd; 1853 int level, optname, optlen; 1854 union { 1855 int i; 1856 char c; 1857 } optval; 1858 1859 fd = getFD(env, this); 1860 if (fd < 0) { 1861 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", 1862 "socket closed"); 1863 return NULL; 1864 } 1865 1866 /* 1867 * Handle IP_MULTICAST_IF seperately 1868 */ 1869 if (opt == java_net_SocketOptions_IP_MULTICAST_IF || 1870 opt == java_net_SocketOptions_IP_MULTICAST_IF2) { 1871 return getMulticastInterface(env, this, fd, opt); 1872 1873 } 1874 1875 /* 1876 * SO_BINDADDR implemented using getsockname 1877 */ 1878 if (opt == java_net_SocketOptions_SO_BINDADDR) { 1879 /* find out local IP address */ 1880 SOCKADDR him; 1881 socklen_t len = 0; 1882 int port; 1883 jobject iaObj; 1884 1885 len = SOCKADDR_LEN; 1886 1887 if (getsockname(fd, (struct sockaddr *)&him, &len) == -1) { 1888 NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", 1889 "Error getting socket name"); 1890 return NULL; 1891 } 1892 iaObj = NET_SockaddrToInetAddress(env, (struct sockaddr *)&him, &port); 1893 1894 return iaObj; 1895 } 1896 1897 /* 1898 * Map the Java level socket option to the platform specific 1899 * level and option name. 1900 */ 1901 if (NET_MapSocketOption(opt, &level, &optname)) { 1902 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Invalid option"); 1903 return NULL; 1904 } 1905 1906 /* 1907 * IP_MULTICAST_LOOP socket option isn't available on Linux 2.2 1908 * kernel with IPv6 so return value stored in impl. 1909 */ 1910#if defined(AF_INET6) && defined(__linux__) 1911 if (isOldKernel && opt == java_net_SocketOptions_IP_MULTICAST_LOOP && 1912 level == IPPROTO_IPV6) { 1913 int mode = (int)(*env)->GetBooleanField(env, this, pdsi_loopbackID); 1914 return createBoolean(env, mode); 1915 } 1916#endif 1917 1918 if (opt == java_net_SocketOptions_IP_MULTICAST_LOOP && 1919 level == IPPROTO_IP) { 1920 optlen = sizeof(optval.c); 1921 } else { 1922 optlen = sizeof(optval.i); 1923 } 1924 1925 if (NET_GetSockOpt(fd, level, optname, (void *)&optval, &optlen) < 0) { 1926 NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", 1927 "Error getting socket option"); 1928 return NULL; 1929 } 1930 1931 switch (opt) { 1932 case java_net_SocketOptions_IP_MULTICAST_LOOP: 1933 /* getLoopbackMode() returns true if IP_MULTICAST_LOOP disabled */ 1934 if (level == IPPROTO_IP) { 1935 return createBoolean(env, (int)!optval.c); 1936 } else { 1937 return createBoolean(env, !optval.i); 1938 } 1939 1940 case java_net_SocketOptions_SO_BROADCAST: 1941 case java_net_SocketOptions_SO_REUSEADDR: 1942 return createBoolean(env, optval.i); 1943 1944 case java_net_SocketOptions_SO_SNDBUF: 1945 case java_net_SocketOptions_SO_RCVBUF: 1946 case java_net_SocketOptions_IP_TOS: 1947 return createInteger(env, optval.i); 1948 1949 } 1950 1951 /* should never rearch here */ 1952 return NULL; 1953} 1954 1955/* 1956 * Multicast-related calls 1957 */ 1958 1959JNIEXPORT void JNICALL 1960PlainDatagramSocketImpl_setTTL(JNIEnv *env, jobject this, 1961 jbyte ttl) { 1962 jint ittl = ttl; 1963 if (ittl < 0) { 1964 ittl += 0x100; 1965 } 1966 PlainDatagramSocketImpl_setTimeToLive(env, this, ittl); 1967} 1968 1969/* 1970 * Set TTL for a socket. Throw exception if failed. 1971 */ 1972static void setTTL(JNIEnv *env, int fd, jint ttl) { 1973 char ittl = (char)ttl; 1974 if (JVM_SetSockOpt(fd, IPPROTO_IP, IP_MULTICAST_TTL, (char*)&ittl, 1975 sizeof(ittl)) < 0) { 1976 NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", 1977 "Error setting socket option"); 1978 } 1979} 1980 1981/* 1982 * Set hops limit for a socket. Throw exception if failed. 1983 */ 1984#ifdef AF_INET6 1985static void setHopLimit(JNIEnv *env, int fd, jint ttl) { 1986 int ittl = (int)ttl; 1987 if (JVM_SetSockOpt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, 1988 (char*)&ittl, sizeof(ittl)) < 0) { 1989 NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", 1990 "Error setting socket option"); 1991 } 1992} 1993#endif 1994 1995/* 1996 * Class: java_net_PlainDatagramSocketImpl 1997 * Method: setTTL 1998 * Signature: (B)V 1999 */ 2000JNIEXPORT void JNICALL 2001PlainDatagramSocketImpl_setTimeToLive(JNIEnv *env, jobject this, 2002 jint ttl) { 2003 2004 jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID); 2005 int fd; 2006 /* it is important to cast this to a char, otherwise setsockopt gets confused */ 2007 2008 if (IS_NULL(fdObj)) { 2009 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", 2010 "Socket closed"); 2011 return; 2012 } else { 2013 fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID); 2014 } 2015 /* setsockopt to be correct ttl */ 2016#ifdef AF_INET6 2017#ifdef __linux__ 2018 setTTL(env, fd, ttl); 2019 if (ipv6_available()) { 2020 setHopLimit(env, fd, ttl); 2021 if (isOldKernel) { 2022 (*env)->SetIntField(env, this, pdsi_ttlID, ttl); 2023 } 2024 } 2025#else /* __linux__ not defined */ 2026 if (ipv6_available()) { 2027 setHopLimit(env, fd, ttl); 2028 } else { 2029 setTTL(env, fd, ttl); 2030 } 2031#endif /* __linux__ */ 2032#else 2033 setTTL(env, fd, ttl); 2034#endif /* AF_INET6 */ 2035} 2036 2037/* 2038 * Class: java_net_PlainDatagramSocketImpl 2039 * Method: getTTL 2040 * Signature: ()B 2041 */ 2042JNIEXPORT jbyte JNICALL 2043PlainDatagramSocketImpl_getTTL(JNIEnv *env, jobject this) { 2044 return (jbyte)PlainDatagramSocketImpl_getTimeToLive(env, this); 2045} 2046 2047 2048/* 2049 * Class: java_net_PlainDatagramSocketImpl 2050 * Method: getTTL 2051 * Signature: ()B 2052 */ 2053JNIEXPORT jint JNICALL 2054PlainDatagramSocketImpl_getTimeToLive(JNIEnv *env, jobject this) { 2055 2056 jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID); 2057 jint fd = -1; 2058 2059 if (IS_NULL(fdObj)) { 2060 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", 2061 "Socket closed"); 2062 return -1; 2063 } else { 2064 fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID); 2065 } 2066 /* getsockopt of ttl */ 2067#ifdef AF_INET6 2068 if (ipv6_available()) { 2069 int ttl = 0; 2070 int len = sizeof(ttl); 2071 2072#ifdef __linux__ 2073 /* 2074 * Linux 2.2 kernel doesn't support IPV6_MULTICAST_HOPS socket option 2075 */ 2076 if (isOldKernel) { 2077 return (*env)->GetIntField(env, this, pdsi_ttlID); 2078 } 2079#endif 2080 2081 if (JVM_GetSockOpt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, 2082 (char*)&ttl, &len) < 0) { 2083 NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", 2084 "Error getting socket option"); 2085 return -1; 2086 } 2087 return (jint)ttl; 2088 } else 2089#endif /* AF_INET6 */ 2090 { 2091 u_char ttl = 0; 2092 int len = sizeof(ttl); 2093 if (JVM_GetSockOpt(fd, IPPROTO_IP, IP_MULTICAST_TTL, 2094 (char*)&ttl, &len) < 0) { 2095 NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", 2096 "Error getting socket option"); 2097 return -1; 2098 } 2099 return (jint)ttl; 2100 } 2101} 2102 2103 2104/* 2105 * mcast_join_leave: Join or leave a multicast group. 2106 * 2107 * For IPv4 sockets use IP_ADD_MEMBERSHIP/IP_DROP_MEMBERSHIP socket option 2108 * to join/leave multicast group. 2109 * 2110 * For IPv6 sockets use IPV6_ADD_MEMBERSHIP/IPV6_DROP_MEMBERSHIP socket option 2111 * to join/leave multicast group. If multicast group is an IPv4 address then 2112 * an IPv4-mapped address is used. 2113 * 2114 * On Linux with IPv6 if we wish to join/leave an IPv4 multicast group then 2115 * we must use the IPv4 socket options. This is because the IPv6 socket options 2116 * don't support IPv4-mapped addresses. This is true as per 2.2.19 and 2.4.7 2117 * kernel releases. In the future it's possible that IP_ADD_MEMBERSHIP 2118 * will be updated to return ENOPROTOOPT if uses with an IPv6 socket (Solaris 2119 * already does this). Thus to cater for this we first try with the IPv4 2120 * socket options and if they fail we use the IPv6 socket options. This 2121 * seems a reasonable failsafe solution. 2122 */ 2123static void mcast_join_leave(JNIEnv *env, jobject this, 2124 jobject iaObj, jobject niObj, 2125 jboolean join) { 2126 2127 jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID); 2128 jint fd; 2129 jint ipv6_join_leave; 2130 2131 if (IS_NULL(fdObj)) { 2132 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", 2133 "Socket closed"); 2134 return; 2135 } else { 2136 fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID); 2137 } 2138 if (IS_NULL(iaObj)) { 2139 JNU_ThrowNullPointerException(env, "iaObj"); 2140 return; 2141 } 2142 2143 /* 2144 * Determine if this is an IPv4 or IPv6 join/leave. 2145 */ 2146#ifdef AF_INET6 2147 ipv6_join_leave = ipv6_available(); 2148 2149#ifdef __linux__ 2150 if (getInetAddress_family(env, iaObj) == IPv4) { 2151 ipv6_join_leave = JNI_FALSE; 2152 } 2153#endif 2154 2155#else 2156 /* 2157 * IPv6 not compiled in 2158 */ 2159 ipv6_join_leave = JNI_FALSE; 2160#endif 2161 2162 /* 2163 * For IPv4 join use IP_ADD_MEMBERSHIP/IP_DROP_MEMBERSHIP socket option 2164 * 2165 * On Linux if IPv4 or IPv6 use IP_ADD_MEMBERSHIP/IP_DROP_MEMBERSHIP 2166 */ 2167 if (!ipv6_join_leave) { 2168#ifdef __linux__ 2169 struct ip_mreqn mname; 2170#else 2171 struct ip_mreq mname; 2172#endif 2173 int mname_len; 2174 2175 /* 2176 * joinGroup(InetAddress, NetworkInterface) implementation :- 2177 * 2178 * Linux/IPv6: use ip_mreqn structure populated with multicast 2179 * address and interface index. 2180 * 2181 * IPv4: use ip_mreq structure populated with multicast 2182 * address and first address obtained from 2183 * NetworkInterface 2184 */ 2185 if (niObj != NULL) { 2186#if defined(__linux__) && defined(AF_INET6) 2187 if (ipv6_available()) { 2188 static jfieldID ni_indexID; 2189 2190 if (ni_indexID == NULL) { 2191 jclass c = (*env)->FindClass(env, "java/net/NetworkInterface"); 2192 CHECK_NULL(c); 2193 ni_indexID = (*env)->GetFieldID(env, c, "index", "I"); 2194 CHECK_NULL(ni_indexID); 2195 } 2196 2197 mname.imr_multiaddr.s_addr = htonl(getInetAddress_addr(env, iaObj)); 2198 mname.imr_address.s_addr = 0; 2199 mname.imr_ifindex = (*env)->GetIntField(env, niObj, ni_indexID); 2200 mname_len = sizeof(struct ip_mreqn); 2201 } else 2202#endif 2203 { 2204 jobjectArray addrArray = (*env)->GetObjectField(env, niObj, ni_addrsID); 2205 jobject addr; 2206 2207 if ((*env)->GetArrayLength(env, addrArray) < 1) { 2208 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", 2209 "bad argument for IP_ADD_MEMBERSHIP: " 2210 "No IP addresses bound to interface"); 2211 return; 2212 } 2213 addr = (*env)->GetObjectArrayElement(env, addrArray, 0); 2214 2215 mname.imr_multiaddr.s_addr = htonl(getInetAddress_addr(env, iaObj)); 2216#ifdef __linux__ 2217 mname.imr_address.s_addr = htonl(getInetAddress_addr(env, addr)); 2218#else 2219 mname.imr_interface.s_addr = htonl(getInetAddress_addr(env, addr)); 2220#endif 2221 mname_len = sizeof(struct ip_mreq); 2222 } 2223 } 2224 2225 2226 /* 2227 * joinGroup(InetAddress) implementation :- 2228 * 2229 * Linux/IPv6: use ip_mreqn structure populated with multicast 2230 * address and interface index. index obtained 2231 * from cached value or IPV6_MULTICAST_IF. 2232 * 2233 * IPv4: use ip_mreq structure populated with multicast 2234 * address and local address obtained from 2235 * IP_MULTICAST_IF. On Linux IP_MULTICAST_IF 2236 * returns different structure depending on 2237 * kernel. 2238 */ 2239 2240 if (niObj == NULL) { 2241 2242#if defined(__linux__) && defined(AF_INET6) 2243 if (ipv6_available()) { 2244 2245 int index; 2246 int len = sizeof(index); 2247 2248 if (isOldKernel) { 2249 index = (*env)->GetIntField(env, this, pdsi_multicastInterfaceID); 2250 } else { 2251 if (JVM_GetSockOpt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, 2252 (char*)&index, &len) < 0) { 2253 NET_ThrowCurrent(env, "getsockopt IPV6_MULTICAST_IF failed"); 2254 return; 2255 } 2256 } 2257 2258 mname.imr_multiaddr.s_addr = htonl(getInetAddress_addr(env, iaObj)); 2259 mname.imr_address.s_addr = 0 ; 2260 mname.imr_ifindex = index; 2261 mname_len = sizeof(struct ip_mreqn); 2262 } else 2263#endif 2264 { 2265 struct in_addr in; 2266 struct in_addr *inP = ∈ 2267 socklen_t len = sizeof(struct in_addr); 2268 2269#ifdef __linux__ 2270 struct ip_mreqn mreqn; 2271 if (isOldKernel) { 2272 inP = (struct in_addr *)&mreqn; 2273 len = sizeof(struct ip_mreqn); 2274 } 2275#endif 2276 if (getsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, (char *)inP, &len) < 0) { 2277 NET_ThrowCurrent(env, "getsockopt IP_MULTICAST_IF failed"); 2278 return; 2279 } 2280 2281#ifdef __linux__ 2282 mname.imr_address.s_addr = 2283 (isOldKernel ? mreqn.imr_address.s_addr : in.s_addr); 2284 2285#else 2286 mname.imr_interface.s_addr = in.s_addr; 2287#endif 2288 mname.imr_multiaddr.s_addr = htonl(getInetAddress_addr(env, iaObj)); 2289 mname_len = sizeof(struct ip_mreq); 2290 } 2291 } 2292 2293 2294 /* 2295 * Join the multicast group. 2296 */ 2297 if (JVM_SetSockOpt(fd, IPPROTO_IP, (join ? IP_ADD_MEMBERSHIP:IP_DROP_MEMBERSHIP), 2298 (char *) &mname, mname_len) < 0) { 2299 2300 /* 2301 * If IP_ADD_MEMBERSHIP returns ENOPROTOOPT on Linux and we've got 2302 * IPv6 enabled then it's possible that the kernel has been fixed 2303 * so we switch to IPV6_ADD_MEMBERSHIP socket option. 2304 * As of 2.4.7 kernel IPV6_ADD_MEMERSHIP can't handle IPv4-mapped 2305 * addresses so we have to use IP_ADD_MEMERSHIP for IPv4 multicast 2306 * groups. However if the socket is an IPv6 socket then then setsockopt 2307 * should reurn ENOPROTOOPT. We assume this will be fixed in Linux 2308 * at some stage. 2309 */ 2310#if defined(__linux__) && defined(AF_INET6) 2311 if (errno == ENOPROTOOPT) { 2312 if (ipv6_available()) { 2313 ipv6_join_leave = JNI_TRUE; 2314 errno = 0; 2315 } else { 2316 errno = ENOPROTOOPT; /* errno can be changed by ipv6_available */ 2317 } 2318 } 2319#endif 2320 if (errno) { 2321 if (join) { 2322 NET_ThrowCurrent(env, "setsockopt IP_ADD_MEMBERSHIP failed"); 2323 } else { 2324 if (errno == ENOENT) 2325 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", 2326 "Not a member of the multicast group"); 2327 else 2328 NET_ThrowCurrent(env, "setsockopt IP_DROP_MEMBERSHIP failed"); 2329 } 2330 } 2331 } 2332 2333 /* 2334 * If we haven't switched to IPv6 socket option then we're done. 2335 */ 2336 if (!ipv6_join_leave) { 2337 return; 2338 } 2339 } 2340 2341 2342 /* 2343 * IPv6 join. If it's an IPv4 multicast group then we use an IPv4-mapped 2344 * address. 2345 */ 2346#ifdef AF_INET6 2347 { 2348 struct ipv6_mreq mname6; 2349 jbyteArray ipaddress; 2350 jbyte caddr[16]; 2351 jint family; 2352 jint address; 2353 family = getInetAddress_family(env, iaObj) == IPv4? AF_INET : AF_INET6; 2354 if (family == AF_INET) { /* will convert to IPv4-mapped address */ 2355 memset((char *) caddr, 0, 16); 2356 address = getInetAddress_addr(env, iaObj); 2357 2358 caddr[10] = 0xff; 2359 caddr[11] = 0xff; 2360 2361 caddr[12] = ((address >> 24) & 0xff); 2362 caddr[13] = ((address >> 16) & 0xff); 2363 caddr[14] = ((address >> 8) & 0xff); 2364 caddr[15] = (address & 0xff); 2365 } else { 2366 ipaddress = (*env)->GetObjectField(env, iaObj, ia6_ipaddressID); 2367 (*env)->GetByteArrayRegion(env, ipaddress, 0, 16, caddr); 2368 } 2369 2370 memcpy((void *)&(mname6.ipv6mr_multiaddr), caddr, sizeof(struct in6_addr)); 2371 if (IS_NULL(niObj)) { 2372 int index; 2373 int len = sizeof(index); 2374 2375#ifdef __linux__ 2376 /* 2377 * 2.2 kernel doens't support IPV6_MULTICAST_IF socket option 2378 */ 2379 if (isOldKernel) { 2380 index = (*env)->GetIntField(env, this, pdsi_multicastInterfaceID); 2381 } else 2382#endif 2383 { 2384 if (JVM_GetSockOpt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, 2385 (char*)&index, &len) < 0) { 2386 NET_ThrowCurrent(env, "getsockopt IPV6_MULTICAST_IF failed"); 2387 return; 2388 } 2389 } 2390 2391#ifdef __linux__ 2392 /* 2393 * On 2.4.8+ if we join a group with the interface set to 0 2394 * then the kernel records the interface it decides. This causes 2395 * subsequent leave groups to fail as there is no match. Thus we 2396 * pick the interface if there is a matching route. 2397 */ 2398 if (index == 0 && !isOldKernel) { 2399 int rt_index = getDefaultIPv6Interface(&(mname6.ipv6mr_multiaddr)); 2400 if (rt_index > 0) { 2401 index = rt_index; 2402 } 2403 } 2404#endif 2405#ifdef MACOSX 2406 if (family == AF_INET6 && index == 0) { 2407 index = getDefaultScopeID(env); 2408 } 2409#endif 2410 mname6.ipv6mr_interface = index; 2411 } else { 2412 jint idx = (*env)->GetIntField(env, niObj, ni_indexID); 2413 mname6.ipv6mr_interface = idx; 2414 } 2415 2416#if defined(_ALLBSD_SOURCE) 2417#define ADD_MEMBERSHIP IPV6_JOIN_GROUP 2418#define DRP_MEMBERSHIP IPV6_LEAVE_GROUP 2419#define S_ADD_MEMBERSHIP "IPV6_JOIN_GROUP" 2420#define S_DRP_MEMBERSHIP "IPV6_LEAVE_GROUP" 2421#else 2422#define ADD_MEMBERSHIP IPV6_ADD_MEMBERSHIP 2423#define DRP_MEMBERSHIP IPV6_DROP_MEMBERSHIP 2424#define S_ADD_MEMBERSHIP "IPV6_ADD_MEMBERSHIP" 2425#define S_DRP_MEMBERSHIP "IPV6_DROP_MEMBERSHIP" 2426#endif 2427 2428 /* Join the multicast group */ 2429 if (JVM_SetSockOpt(fd, IPPROTO_IPV6, (join ? ADD_MEMBERSHIP : DRP_MEMBERSHIP), 2430 (char *) &mname6, sizeof (mname6)) < 0) { 2431 2432 if (join) { 2433 NET_ThrowCurrent(env, "setsockopt " S_ADD_MEMBERSHIP " failed"); 2434 } else { 2435 if (errno == ENOENT) { 2436 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", 2437 "Not a member of the multicast group"); 2438 } else { 2439 NET_ThrowCurrent(env, "setsockopt " S_DRP_MEMBERSHIP " failed"); 2440 } 2441 } 2442 } 2443 } 2444#endif 2445} 2446 2447/* 2448 * Class: java_net_PlainDatagramSocketImpl 2449 * Method: join 2450 * Signature: (Ljava/net/InetAddress;)V 2451 */ 2452JNIEXPORT void JNICALL 2453PlainDatagramSocketImpl_join(JNIEnv *env, jobject this, 2454 jobject iaObj, jobject niObj) 2455{ 2456 mcast_join_leave(env, this, iaObj, niObj, JNI_TRUE); 2457} 2458 2459/* 2460 * Class: java_net_PlainDatagramSocketImpl 2461 * Method: leave 2462 * Signature: (Ljava/net/InetAddress;)V 2463 */ 2464JNIEXPORT void JNICALL 2465PlainDatagramSocketImpl_leave(JNIEnv *env, jobject this, 2466 jobject iaObj, jobject niObj) 2467{ 2468 mcast_join_leave(env, this, iaObj, niObj, JNI_FALSE); 2469} 2470 2471static JNINativeMethod gMethods[] = { 2472 NATIVE_METHOD(PlainDatagramSocketImpl, leave, "(Ljava/net/InetAddress;Ljava/net/NetworkInterface;)V"), 2473 NATIVE_METHOD(PlainDatagramSocketImpl, join, "(Ljava/net/InetAddress;Ljava/net/NetworkInterface;)V"), 2474 NATIVE_METHOD(PlainDatagramSocketImpl, getTimeToLive, "()I"), 2475 NATIVE_METHOD(PlainDatagramSocketImpl, getTTL, "()B"), 2476 NATIVE_METHOD(PlainDatagramSocketImpl, setTimeToLive, "(I)V"), 2477 NATIVE_METHOD(PlainDatagramSocketImpl, setTTL, "(B)V"), 2478 NATIVE_METHOD(PlainDatagramSocketImpl, socketGetOption, "(I)Ljava/lang/Object;"), 2479 NATIVE_METHOD(PlainDatagramSocketImpl, socketSetOption, "(ILjava/lang/Object;)V"), 2480 NATIVE_METHOD(PlainDatagramSocketImpl, datagramSocketClose, "()V"), 2481 NATIVE_METHOD(PlainDatagramSocketImpl, datagramSocketCreate, "()V"), 2482 NATIVE_METHOD(PlainDatagramSocketImpl, receive0, "(Ljava/net/DatagramPacket;)V"), 2483 NATIVE_METHOD(PlainDatagramSocketImpl, peekData, "(Ljava/net/DatagramPacket;)I"), 2484 NATIVE_METHOD(PlainDatagramSocketImpl, peek, "(Ljava/net/InetAddress;)I"), 2485 NATIVE_METHOD(PlainDatagramSocketImpl, send, "(Ljava/net/DatagramPacket;)V"), 2486 NATIVE_METHOD(PlainDatagramSocketImpl, disconnect0, "(I)V"), 2487 NATIVE_METHOD(PlainDatagramSocketImpl, connect0, "(Ljava/net/InetAddress;I)V"), 2488 NATIVE_METHOD(PlainDatagramSocketImpl, bind0, "(ILjava/net/InetAddress;)V"), 2489 NATIVE_METHOD(PlainDatagramSocketImpl, init, "()V"), 2490}; 2491 2492void register_java_net_PlainDatagramSocketImpl(JNIEnv* env) { 2493 jniRegisterNativeMethods(env, "java/net/PlainDatagramSocketImpl", gMethods, NELEM(gMethods)); 2494} 2495