/* * Copyright (c) 1997, 2007, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package java.net; import java.io.FileDescriptor; import static ikvm.internal.JNI.*; import static ikvm.internal.Winsock.*; import static java.net.net_util_md.*; import static java.net.InetAddress.IPv4; import static java.net.InetAddress.IPv6; final class TwoStacksPlainDatagramSocketImpl_c { static final int ni_class = 0; static final int JVM_IO_ERR = -1; static final int JVM_IO_INTR = -2; static final int java_net_SocketOptions_SO_BINDADDR = SocketOptions.SO_BINDADDR; static final int java_net_SocketOptions_SO_SNDBUF = SocketOptions.SO_SNDBUF; static final int java_net_SocketOptions_SO_RCVBUF = SocketOptions.SO_RCVBUF; static final int java_net_SocketOptions_IP_TOS = SocketOptions.IP_TOS; static final int java_net_SocketOptions_SO_REUSEADDR = SocketOptions.SO_REUSEADDR; static final int java_net_SocketOptions_SO_BROADCAST = SocketOptions.SO_BROADCAST; static final int java_net_SocketOptions_IP_MULTICAST_LOOP = SocketOptions.IP_MULTICAST_LOOP; static final int java_net_SocketOptions_IP_MULTICAST_IF = SocketOptions.IP_MULTICAST_IF; static final int java_net_SocketOptions_IP_MULTICAST_IF2 = SocketOptions.IP_MULTICAST_IF2; /* #include <windows.h> #include <winsock2.h> #include <ws2tcpip.h> #include <ctype.h> #include <stdio.h> #include <stdlib.h> #include <malloc.h> #include <sys/types.h> #ifndef IPTOS_TOS_MASK #define IPTOS_TOS_MASK 0x1e #endif #ifndef IPTOS_PREC_MASK #define IPTOS_PREC_MASK 0xe0 #endif #include "java_net_TwoStacksPlainDatagramSocketImpl.h" #include "java_net_SocketOptions.h" #include "java_net_NetworkInterface.h" #include "jvm.h" #include "jni_util.h" #include "net_util.h" #define IN_CLASSD(i) (((long)(i) & 0xf0000000) == 0xe0000000) #define IN_MULTICAST(i) IN_CLASSD(i) */ static boolean IN_MULTICAST(int ipv4address) { return ((ipv4address >> 24) & 0xf0) == 0xe0; } /************************************************************************ * TwoStacksPlainDatagramSocketImpl */ /* static jfieldID IO_fd_fdID; static jfieldID pdsi_trafficClassID; jfieldID pdsi_fdID; jfieldID pdsi_fd1ID; jfieldID pdsi_fduseID; jfieldID pdsi_lastfdID; jfieldID pdsi_timeoutID; jfieldID pdsi_localPortID; jfieldID pdsi_connected; static jclass ia4_clazz; static jmethodID ia4_ctor; static CRITICAL_SECTION sizeCheckLock; */ /* Windows OS version is XP or better */ static final boolean xp_or_later = true; /* Windows OS version is Windows 2000 or better */ //static int w2k_or_later = 0; /* * Notes about UDP/IPV6 on Windows (XP and 2003 server): * * fd always points to the IPv4 fd, and fd1 points to the IPv6 fd. * Both fds are used when we bind to a wild-card address. When a specific * address is used, only one of them is used. */ /* * Returns a java.lang.Integer based on 'i' */ /* jobject createInteger(JNIEnv *env, int i) { static jclass i_class; static jmethodID i_ctrID; static jfieldID i_valueID; if (i_class == NULL) { jclass c = (*env)->FindClass(env, "java/lang/Integer"); CHECK_NULL_RETURN(c, NULL); i_ctrID = (*env)->GetMethodID(env, c, "<init>", "(I)V"); CHECK_NULL_RETURN(i_ctrID, NULL); i_class = (*env)->NewGlobalRef(env, c); CHECK_NULL_RETURN(i_class, NULL); } return ( (*env)->NewObject(env, i_class, i_ctrID, i) ); } */ /* * Returns a java.lang.Boolean based on 'b' */ /* jobject createBoolean(JNIEnv *env, int b) { static jclass b_class; static jmethodID b_ctrID; static jfieldID b_valueID; if (b_class == NULL) { jclass c = (*env)->FindClass(env, "java/lang/Boolean"); CHECK_NULL_RETURN(c, NULL); b_ctrID = (*env)->GetMethodID(env, c, "<init>", "(Z)V"); CHECK_NULL_RETURN(b_ctrID, NULL); b_class = (*env)->NewGlobalRef(env, c); CHECK_NULL_RETURN(b_class, NULL); } return( (*env)->NewObject(env, b_class, b_ctrID, (jboolean)(b!=0)) ); } */ static cli.System.Net.Sockets.Socket getFD(JNIEnv env, TwoStacksPlainDatagramSocketImpl _this) { FileDescriptor fdObj = _this.fd; if (fdObj == NULL) { return null; } return fdObj.getSocket(); } static cli.System.Net.Sockets.Socket getFD1(JNIEnv env, TwoStacksPlainDatagramSocketImpl _this) { FileDescriptor fdObj = _this.fd1; if (fdObj == NULL) { return null; } return fdObj.getSocket(); } /* * This function returns JNI_TRUE if the datagram size exceeds the underlying * provider's ability to send to the target address. The following OS * oddies have been observed :- * * 1. On Windows 95/98 if we try to send a datagram > 12k to an application * on the same machine then the send will fail silently. * * 2. On Windows ME if we try to send a datagram > supported by underlying * provider then send will not return an error. * * 3. On Windows NT/2000 if we exceeds the maximum size then send will fail * with WSAEADDRNOTAVAIL. * * 4. On Windows 95/98 if we exceed the maximum size when sending to * another machine then WSAEINVAL is returned. * */ /* jboolean exceedSizeLimit(JNIEnv *env, jint fd, jint addr, jint size) { #define DEFAULT_MSG_SIZE 65527 static jboolean initDone; static jboolean is95or98; static int maxmsg; typedef struct _netaddr { /* Windows 95/98 only *-/ unsigned long addr; struct _netaddr *next; } netaddr; static netaddr *addrList; netaddr *curr; /* * First time we are called we must determine which OS this is and also * get the maximum size supported by the underlying provider. * * In addition on 95/98 we must enumerate our IP addresses. *-/ if (!initDone) { EnterCriticalSection(&sizeCheckLock); if (initDone) { /* another thread got there first *-/ LeaveCriticalSection(&sizeCheckLock); } else { OSVERSIONINFO ver; int len; /* * Step 1: Determine which OS this is. *-/ ver.dwOSVersionInfoSize = sizeof(ver); GetVersionEx(&ver); is95or98 = JNI_FALSE; if (ver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS && ver.dwMajorVersion == 4 && (ver.dwMinorVersion == 0 || ver.dwMinorVersion == 10)) { is95or98 = JNI_TRUE; } /* * Step 2: Determine the maximum datagram supported by the * underlying provider. On Windows 95 if winsock hasn't been * upgraded (ie: unsupported configuration) then we assume * the default 64k limit. *-/ len = sizeof(maxmsg); if (NET_GetSockOpt(fd, SOL_SOCKET, SO_MAX_MSG_SIZE, (char *)&maxmsg, &len) < 0) { maxmsg = DEFAULT_MSG_SIZE; } /* * Step 3: On Windows 95/98 then enumerate the IP addresses on * this machine. This is necesary because we need to check if the * datagram is being sent to an application on the same machine. *-/ if (is95or98) { char hostname[255]; struct hostent *hp; if (gethostname(hostname, sizeof(hostname)) == -1) { LeaveCriticalSection(&sizeCheckLock); JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Unable to obtain hostname"); return JNI_TRUE; } hp = (struct hostent *)gethostbyname(hostname); if (hp != NULL) { struct in_addr **addrp = (struct in_addr **) hp->h_addr_list; while (*addrp != (struct in_addr *) 0) { curr = (netaddr *)malloc(sizeof(netaddr)); if (curr == NULL) { while (addrList != NULL) { curr = addrList->next; free(addrList); addrList = curr; } LeaveCriticalSection(&sizeCheckLock); JNU_ThrowOutOfMemoryError(env, "heap allocation failed"); return JNI_TRUE; } curr->addr = htonl((*addrp)->S_un.S_addr); curr->next = addrList; addrList = curr; addrp++; } } } /* * Step 4: initialization is done so set flag and unlock cs *-/ initDone = JNI_TRUE; LeaveCriticalSection(&sizeCheckLock); } } /* * Now examine the size of the datagram :- * * (a) If exceeds size of service provider return 'false' to indicate that * we exceed the limit. * (b) If not 95/98 then return 'true' to indicate that the size is okay. * (c) On 95/98 if the size is <12k we are okay. * (d) On 95/98 if size > 12k then check if the destination is the current * machine. *-/ if (size > maxmsg) { /* step (a) *-/ return JNI_TRUE; } if (!is95or98) { /* step (b) *-/ return JNI_FALSE; } if (size <= 12280) { /* step (c) *-/ return JNI_FALSE; } /* step (d) *-/ if ((addr & 0x7f000000) == 0x7f000000) { return JNI_TRUE; } curr = addrList; while (curr != NULL) { if (curr->addr == addr) { return JNI_TRUE; } curr = curr->next; } return JNI_FALSE; } */ /* * Return JNI_TRUE if this Windows edition supports ICMP Port Unreachable */ static boolean supportPortUnreachable() { // we don't support anything pre-Win2K anyway return true; } /* * This function "purges" all outstanding ICMP port unreachable packets * outstanding on a socket and returns JNI_TRUE if any ICMP messages * have been purged. The rational for purging is to emulate normal BSD * behaviour whereby receiving a "connection reset" status resets the * socket. */ static boolean purgeOutstandingICMP(cli.System.Net.Sockets.Socket fd) { boolean got_icmp = false; byte[] buf = new byte[1]; fd_set tbl = new fd_set(); timeval t = new timeval(); SOCKETADDRESS rmtaddr = null; /* * A no-op if this OS doesn't support it. */ if (!supportPortUnreachable()) { return JNI_FALSE; } /* * Peek at the queue to see if there is an ICMP port unreachable. If there * is then receive it. */ FD_ZERO(tbl); FD_SET(fd, tbl); while(true) { if (select(tbl, null, null, t) <= 0) { break; } if (recvfrom(fd, buf, 1, MSG_PEEK, rmtaddr) != JVM_IO_ERR) { break; } if (WSAGetLastError() != WSAECONNRESET) { /* some other error - we don't care here */ break; } recvfrom(fd, buf, 1, 0, rmtaddr); got_icmp = JNI_TRUE; } return got_icmp; } /* * Class: java_net_TwoStacksPlainDatagramSocketImpl * Method: init * Signature: ()V */ /* JNIEXPORT void JNICALL Java_java_net_TwoStacksPlainDatagramSocketImpl_init(JNIEnv *env, jclass cls) { OSVERSIONINFO ver; int version; ver.dwOSVersionInfoSize = sizeof(ver); GetVersionEx(&ver); version = ver.dwMajorVersion * 10 + ver.dwMinorVersion; xp_or_later = (ver.dwPlatformId == VER_PLATFORM_WIN32_NT) && (version >= 51); w2k_or_later = (ver.dwPlatformId == VER_PLATFORM_WIN32_NT) && (version >= 50); /* get fieldIDs *-/ pdsi_fdID = (*env)->GetFieldID(env, cls, "fd", "Ljava/io/FileDescriptor;"); CHECK_NULL(pdsi_fdID); pdsi_fd1ID = (*env)->GetFieldID(env, cls, "fd1", "Ljava/io/FileDescriptor;"); CHECK_NULL(pdsi_fd1ID); pdsi_timeoutID = (*env)->GetFieldID(env, cls, "timeout", "I"); CHECK_NULL(pdsi_timeoutID); pdsi_fduseID = (*env)->GetFieldID(env, cls, "fduse", "I"); CHECK_NULL(pdsi_fduseID); pdsi_lastfdID = (*env)->GetFieldID(env, cls, "lastfd", "I"); CHECK_NULL(pdsi_lastfdID); pdsi_trafficClassID = (*env)->GetFieldID(env, cls, "trafficClass", "I"); CHECK_NULL(pdsi_trafficClassID); pdsi_localPortID = (*env)->GetFieldID(env, cls, "localPort", "I"); CHECK_NULL(pdsi_localPortID); pdsi_connected = (*env)->GetFieldID(env, cls, "connected", "Z"); CHECK_NULL(pdsi_connected); cls = (*env)->FindClass(env, "java/io/FileDescriptor"); CHECK_NULL(cls); IO_fd_fdID = NET_GetFileDescriptorID(env); CHECK_NULL(IO_fd_fdID); ia4_clazz = (*env)->FindClass(env, "java/net/Inet4Address"); CHECK_NULL(ia4_clazz); ia4_clazz = (*env)->NewGlobalRef(env, ia4_clazz); CHECK_NULL(ia4_clazz); ia4_ctor = (*env)->GetMethodID(env, ia4_clazz, "<init>", "()V"); CHECK_NULL(ia4_ctor); InitializeCriticalSection(&sizeCheckLock); } */ static void bind0(JNIEnv env, TwoStacksPlainDatagramSocketImpl _this, int port, InetAddress addressObj) { FileDescriptor fdObj = _this.fd; FileDescriptor fd1Obj = _this.fd1; cli.System.Net.Sockets.Socket fd = null; cli.System.Net.Sockets.Socket fd1 = null; int family; boolean ipv6_supported = ipv6_available(); SOCKETADDRESS lcladdr; lcladdr = new SOCKETADDRESS(); if (IS_NULL(addressObj)) { JNU_ThrowNullPointerException(env, "argument address"); return; } family = addressObj.family; if (family == IPv6 && !ipv6_supported) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "Protocol family not supported"); return; } if (IS_NULL(fdObj) || (ipv6_supported && IS_NULL(fd1Obj))) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "socket closed"); return; } else { fd = fdObj.getSocket(); if (ipv6_supported) { fd1 = fd1Obj.getSocket(); } } if (IS_NULL(addressObj)) { JNU_ThrowNullPointerException(env, "argument address"); return; } if (NET_InetAddressToSockaddr(env, addressObj, port, lcladdr, JNI_FALSE) != 0) { return; } if (ipv6_supported) { ipv6bind v6bind = new ipv6bind(); v6bind.addr = lcladdr; v6bind.ipv4_fd = fd; v6bind.ipv6_fd = fd1; if (NET_BindV6(v6bind) != -1) { /* check if the fds have changed */ if (v6bind.ipv4_fd != fd) { fd = v6bind.ipv4_fd; if (fd == null) { /* socket is closed. */ _this.fd = null; } else { /* socket was re-created */ fdObj.setSocket(fd); } } if (v6bind.ipv6_fd != fd1) { fd1 = v6bind.ipv6_fd; if (fd1 == null) { /* socket is closed. */ _this.fd1 = null; } else { /* socket was re-created */ fd1Obj.setSocket(fd1); } } } else { NET_ThrowCurrent (env, "Cannot bind"); return; } } else { if (bind(fd, lcladdr) == -1) { if (WSAGetLastError() == WSAEACCES) { WSASetLastError(WSAEADDRINUSE); } NET_ThrowCurrent(env, "Cannot bind"); return; } } if (port == 0) { if (fd == null) { /* must be an IPV6 only socket. */ fd = fd1; } if (getsockname(fd, lcladdr) == -1) { NET_ThrowCurrent(env, "JVM_GetSockName"); return; } port = ntohs(GET_PORT (lcladdr)); } _this.localPort = port; } /* * Class: java_net_TwoStacksPlainDatagramSocketImpl * Method: connect0 * Signature: (Ljava/net/InetAddress;I)V */ static void connect0(JNIEnv env, TwoStacksPlainDatagramSocketImpl _this, InetAddress address, int port) { /* The object's field */ FileDescriptor fdObj = _this.fd; FileDescriptor fd1Obj = _this.fd1; /* The fdObj'fd */ cli.System.Net.Sockets.Socket fd = null; cli.System.Net.Sockets.Socket fd1 = null; cli.System.Net.Sockets.Socket fdc; /* The packetAddress address, family and port */ int addr, family; SOCKETADDRESS rmtaddr; rmtaddr = new SOCKETADDRESS(); boolean ipv6_supported = ipv6_available(); if (IS_NULL(fdObj) && IS_NULL(fd1Obj)) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "Socket closed"); return; } if (!IS_NULL(fdObj)) { fd = fdObj.getSocket(); } if (!IS_NULL(fd1Obj)) { fd1 = fd1Obj.getSocket(); } if (IS_NULL(address)) { JNU_ThrowNullPointerException(env, "address"); return; } addr = address.address; family = address.family; if (family == IPv6 && !ipv6_supported) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "Protocol family not supported"); return; } fdc = family == IPv4? fd: fd1; if (xp_or_later) { /* SIO_UDP_CONNRESET fixes a bug introduced in Windows 2000, which * returns connection reset errors un connected UDP sockets (as well * as connected sockets. The solution is to only enable this feature * when the socket is connected */ WSAIoctl(fdc, SIO_UDP_CONNRESET, true); } if (NET_InetAddressToSockaddr(env, address, port, rmtaddr, JNI_FALSE) != 0) { return; } if (connect(fdc, rmtaddr) == -1) { NET_ThrowCurrent(env, "connect"); return; } } /* * Class: java_net_TwoStacksPlainDatagramSocketImpl * Method: disconnect0 * Signature: ()V */ static void disconnect0(TwoStacksPlainDatagramSocketImpl _this, int family) { /* The object's field */ FileDescriptor fdObj; /* The fdObj'fd */ cli.System.Net.Sockets.Socket fd; SOCKETADDRESS addr; addr = new SOCKETADDRESS(); if (family == IPv4) { fdObj = _this.fd; } else { fdObj = _this.fd1; } if (IS_NULL(fdObj)) { /* disconnect doesn't throw any exceptions */ return; } fd = fdObj.getSocket(); connect(fd, addr); /* * use SIO_UDP_CONNRESET * to disable ICMP port unreachable handling here. */ if (xp_or_later) { WSAIoctl(fd,SIO_UDP_CONNRESET,false); } } /* * Class: java_net_TwoStacksPlainDatagramSocketImpl * Method: send * Signature: (Ljava/net/DatagramPacket;)V */ static void send(JNIEnv env, TwoStacksPlainDatagramSocketImpl _this, DatagramPacket packet) { FileDescriptor fdObj; cli.System.Net.Sockets.Socket fd; InetAddress iaObj; int address; int family; int packetBufferOffset, packetBufferLen, packetPort; byte[] packetBuffer; boolean connected; SOCKETADDRESS rmtaddr; rmtaddr = new SOCKETADDRESS(); if (IS_NULL(packet)) { JNU_ThrowNullPointerException(env, "null packet"); return; } iaObj = packet.address; packetPort = packet.port; packetBufferOffset = packet.offset; packetBuffer = packet.buf; connected = _this.connected; if (IS_NULL(iaObj) || IS_NULL(packetBuffer)) { JNU_ThrowNullPointerException(env, "null address || null buffer"); return; } family = iaObj.family; if (family == IPv4) { fdObj = _this.fd; } else { if (!ipv6_available()) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "Protocol not allowed"); return; } fdObj = _this.fd1; } if (IS_NULL(fdObj)) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "Socket closed"); return; } fd = fdObj.getSocket(); packetBufferLen = packet.length; if (connected) { rmtaddr = null; } else { if (NET_InetAddressToSockaddr(env, iaObj, packetPort, rmtaddr, JNI_FALSE) != 0) { return; } } /* if (packetBufferLen > MAX_BUFFER_LEN) { /* * On 95/98 if we try to send a datagram >12k to an application * on the same machine then this will fail silently. Thus we * catch this situation here so that we can throw an exception * when this arises. * On ME if we try to send a datagram with a size greater than * that supported by the service provider then no error is * returned. *-/ if (!w2k_or_later) { /* avoid this check on Win 2K or better. Does not work with IPv6. * Check is not necessary on these OSes *-/ if (connected) { address = (*env)->GetIntField(env, iaObj, ia_addressID); } else { address = ntohl(rmtaddr.him4.sin_addr.s_addr); } if (exceedSizeLimit(env, fd, address, packetBufferLen)) { if (!((*env)->ExceptionOccurred(env))) { NET_ThrowNew(env, WSAEMSGSIZE, "Datagram send failed"); } return; } } /* When JNI-ifying the JDK's IO routines, we turned * read's and write's of byte arrays of size greater * than 2048 bytes into several operations of size 2048. * This saves a malloc()/memcpy()/free() for big * buffers. This is OK for file IO and TCP, but that * strategy violates the semantics of a datagram protocol. * (one big send) != (several smaller sends). So here * we *must* alloc the buffer. Note it needn't be bigger * than 65,536 (0xFFFF) the max size of an IP packet. * anything bigger is truncated anyway. *-/ fullPacket = (char *)malloc(packetBufferLen); if (!fullPacket) { JNU_ThrowOutOfMemoryError(env, "heap allocation failed"); return; } } else { fullPacket = &(BUF[0]); } */ switch (sendto(fd, packetBuffer, packetBufferOffset, packetBufferLen, 0, rmtaddr)) { case JVM_IO_ERR: NET_ThrowCurrent(env, "Datagram send failed"); break; case JVM_IO_INTR: JNU_ThrowByName(env, JNU_JAVAIOPKG+"InterruptedIOException", "operation interrupted"); } } /* * check which socket was last serviced when there was data on both sockets. * Only call this if sure that there is data on both sockets. */ private static cli.System.Net.Sockets.Socket checkLastFD (TwoStacksPlainDatagramSocketImpl _this, cli.System.Net.Sockets.Socket fd, cli.System.Net.Sockets.Socket fd1) { cli.System.Net.Sockets.Socket nextfd, lastfd = _this.lastfd; if (lastfd == null) { /* arbitrary. Choose fd */ _this.lastfd = fd; return fd; } else { if (lastfd == fd) { nextfd = fd1; } else { nextfd = fd; } _this.lastfd = nextfd; return nextfd; } } /* * Class: java_net_TwoStacksPlainDatagramSocketImpl * Method: peek * Signature: (Ljava/net/InetAddress;)I */ static int peek(JNIEnv env, TwoStacksPlainDatagramSocketImpl _this, InetAddress addressObj) { FileDescriptor fdObj = _this.fd; int timeout = _this.timeout; cli.System.Net.Sockets.Socket fd; /* The address and family fields of addressObj */ int address, family; int n; SOCKETADDRESS remote_addr = new SOCKETADDRESS(); byte[] buf = new byte[1]; boolean retry; long prevTime = 0; if (IS_NULL(fdObj)) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "Socket closed"); return -1; } else { fd = fdObj.getSocket(); if (fd == null) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "socket closed"); return -1; } } if (IS_NULL(addressObj)) { JNU_ThrowNullPointerException(env, "Null address in peek()"); return -1; } else { address = addressObj.address; /* We only handle IPv4 for now. Will support IPv6 once its in the os */ family = AF_INET; } do { retry = FALSE; /* * If a timeout has been specified then we select on the socket * waiting for a read event or a timeout. */ if (timeout != 0) { int ret; prevTime = JVM_CurrentTimeMillis(env, 0); ret = NET_Timeout (fd, timeout); if (ret == 0) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketTimeoutException", "Peek timed out"); return ret; } else if (ret == JVM_IO_ERR) { NET_ThrowCurrent(env, "timeout in datagram socket peek"); return ret; } else if (ret == JVM_IO_INTR) { JNU_ThrowByName(env, JNU_JAVAIOPKG+"InterruptedIOException", "operation interrupted"); return ret; } } /* now try the peek */ n = recvfrom(fd, buf, 1, MSG_PEEK, remote_addr); if (n == JVM_IO_ERR) { if (WSAGetLastError() == WSAECONNRESET) { boolean connected; /* * An icmp port unreachable - we must receive this as Windows * does not reset the state of the socket until this has been * received. */ purgeOutstandingICMP(fd); connected = _this.connected; if (connected) { JNU_ThrowByName(env, JNU_JAVANETPKG+"PortUnreachableException", "ICMP Port Unreachable"); return 0; } /* * If a timeout was specified then we need to adjust it because * we may have used up some of the timeout befor the icmp port * unreachable arrived. */ if (timeout != 0) { long newTime = JVM_CurrentTimeMillis(env, 0); timeout -= (newTime - prevTime); if (timeout <= 0) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketTimeoutException", "Receive timed out"); return 0; } prevTime = newTime; } /* Need to retry the recv */ retry = TRUE; } } } while (retry); if (n == JVM_IO_ERR && WSAGetLastError() != WSAEMSGSIZE) { NET_ThrowCurrent(env, "Datagram peek failed"); return 0; } if (n == JVM_IO_INTR) { JNU_ThrowByName(env, JNU_JAVAIOPKG+"InterruptedIOException", null); return 0; } addressObj.address = ntohl(remote_addr.sin_addr.s_addr); addressObj.family = IPv4; /* return port */ return ntohs(remote_addr.sin_port); } static int peekData(JNIEnv env, TwoStacksPlainDatagramSocketImpl _this, DatagramPacket packet) { FileDescriptor fdObj = _this.fd; FileDescriptor fd1Obj = _this.fd1; int timeout = _this.timeout; byte[] packetBuffer; int packetBufferOffset, packetBufferLen; cli.System.Net.Sockets.Socket fd = null, fd1 = null, fduse = null; int nsockets=0, errorCode; int port; byte[] data; boolean checkBoth = false; int datalen; int n; SOCKETADDRESS remote_addr; remote_addr = new SOCKETADDRESS(); boolean retry; long prevTime = 0; if (!IS_NULL(fdObj)) { fd = fdObj.getSocket(); if (fd == null) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "socket closed"); return -1; } nsockets = 1; } if (!IS_NULL(fd1Obj)) { fd1 = fd1Obj.getSocket(); if (fd1 == null) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "socket closed"); return -1; } nsockets ++; } switch (nsockets) { case 0: JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "socket closed"); return -1; case 1: if (!IS_NULL(fdObj)) { fduse = fd; } else { fduse = fd1; } break; case 2: checkBoth = TRUE; break; } if (IS_NULL(packet)) { JNU_ThrowNullPointerException(env, "packet"); return -1; } packetBuffer = packet.buf; if (IS_NULL(packetBuffer)) { JNU_ThrowNullPointerException(env, "packet buffer"); return -1; } packetBufferOffset = packet.offset; packetBufferLen = packet.bufLength; /* if (packetBufferLen > MAX_BUFFER_LEN) { /* When JNI-ifying the JDK's IO routines, we turned * read's and write's of byte arrays of size greater * than 2048 bytes into several operations of size 2048. * This saves a malloc()/memcpy()/free() for big * buffers. This is OK for file IO and TCP, but that * strategy violates the semantics of a datagram protocol. * (one big send) != (several smaller sends). So here * we *must* alloc the buffer. Note it needn't be bigger * than 65,536 (0xFFFF) the max size of an IP packet. * anything bigger is truncated anyway. *-/ fullPacket = (char *)malloc(packetBufferLen); if (!fullPacket) { JNU_ThrowOutOfMemoryError(env, "heap allocation failed"); return -1; } } else { fullPacket = &(BUF[0]); } */ do { int ret; retry = FALSE; /* * If a timeout has been specified then we select on the socket * waiting for a read event or a timeout. */ if (checkBoth) { int t = timeout == 0 ? -1: timeout; prevTime = JVM_CurrentTimeMillis(env, 0); cli.System.Net.Sockets.Socket[] tmp = new cli.System.Net.Sockets.Socket[] { fduse }; ret = NET_Timeout2 (fd, fd1, t, tmp); fduse = tmp[0]; /* all subsequent calls to recv() or select() will use the same fd * for this call to peek() */ if (ret <= 0) { if (ret == 0) { JNU_ThrowByName(env,JNU_JAVANETPKG+"SocketTimeoutException", "Peek timed out"); } else if (ret == JVM_IO_ERR) { NET_ThrowCurrent(env, "timeout in datagram socket peek"); } else if (ret == JVM_IO_INTR) { JNU_ThrowByName(env, JNU_JAVAIOPKG+"InterruptedIOException", "operation interrupted"); } return -1; } if (ret == 2) { fduse = checkLastFD (_this, fd, fd1); } checkBoth = FALSE; } else if (timeout != 0) { if (prevTime == 0) { prevTime = JVM_CurrentTimeMillis(env, 0); } ret = NET_Timeout (fduse, timeout); if (ret <= 0) { if (ret == 0) { JNU_ThrowByName(env,JNU_JAVANETPKG+"SocketTimeoutException", "Receive timed out"); } else if (ret == JVM_IO_ERR) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "Socket closed"); } else if (ret == JVM_IO_INTR) { JNU_ThrowByName(env, JNU_JAVAIOPKG+"InterruptedIOException", "operation interrupted"); } return -1; } } /* receive the packet */ n = recvfrom(fduse, packetBuffer, packetBufferOffset, packetBufferLen, MSG_PEEK, remote_addr); port = ntohs (GET_PORT(remote_addr)); if (n == JVM_IO_ERR) { if (WSAGetLastError() == WSAECONNRESET) { boolean connected; /* * An icmp port unreachable - we must receive this as Windows * does not reset the state of the socket until this has been * received. */ purgeOutstandingICMP(fduse); connected = _this.connected; if (connected) { JNU_ThrowByName(env, JNU_JAVANETPKG+"PortUnreachableException", "ICMP Port Unreachable"); return -1; } /* * If a timeout was specified then we need to adjust it because * we may have used up some of the timeout befor the icmp port * unreachable arrived. */ if (timeout != 0) { long newTime = JVM_CurrentTimeMillis(env, 0); timeout -= (newTime - prevTime); if (timeout <= 0) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketTimeoutException", "Receive timed out"); return -1; } prevTime = newTime; } retry = TRUE; } } } while (retry); if (n < 0) { errorCode = WSAGetLastError(); /* check to see if it's because the buffer was too small */ if (errorCode == WSAEMSGSIZE) { /* it is because the buffer is too small. It's UDP, it's * unreliable, it's all good. discard the rest of the * data.. */ n = packetBufferLen; } else { /* failure */ packet.length = 0; } } if (n == -1) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "socket closed"); } else if (n == -2) { JNU_ThrowByName(env, JNU_JAVAIOPKG+"InterruptedIOException", "operation interrupted"); } else if (n < 0) { NET_ThrowCurrent(env, "Datagram receive failed"); } else { InetAddress packetAddress; /* * Check if there is an InetAddress already associated with this * packet. If so we check if it is the same source address. We * can't update any existing InetAddress because it is immutable */ packetAddress = packet.address; if (packetAddress != NULL) { if (!NET_SockaddrEqualsInetAddress(remote_addr, packetAddress)) { /* force a new InetAddress to be created */ packetAddress = null; } } if (packetAddress == NULL) { int[] tmp = { port }; packetAddress = NET_SockaddrToInetAddress(remote_addr, tmp); port = tmp[0]; /* stuff the new Inetaddress in the packet */ packet.address = packetAddress; } /* populate the packet */ packet.port = port; packet.length = n; } /* make sure receive() picks up the right fd */ _this.fduse = fduse; return port; } /* * Class: java_net_TwoStacksPlainDatagramSocketImpl * Method: receive * Signature: (Ljava/net/DatagramPacket;)V */ static void receive0(JNIEnv env, TwoStacksPlainDatagramSocketImpl _this, DatagramPacket packet) { FileDescriptor fdObj = _this.fd; FileDescriptor fd1Obj = _this.fd1; int timeout = _this.timeout; byte[] packetBuffer; int packetBufferOffset, packetBufferLen; boolean ipv6_supported = ipv6_available(); /* as a result of the changes for ipv6, peek() or peekData() * must be called prior to receive() so that fduse can be set. */ cli.System.Net.Sockets.Socket fd = null; cli.System.Net.Sockets.Socket fd1 = null; cli.System.Net.Sockets.Socket fduse = null; int errorCode; int n, nsockets=0; SOCKETADDRESS remote_addr; remote_addr = new SOCKETADDRESS(); boolean retry; long prevTime = 0, selectTime=0; boolean connected; if (IS_NULL(fdObj) && IS_NULL(fd1Obj)) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "Socket closed"); return; } if (!IS_NULL(fdObj)) { fd = fdObj.getSocket(); nsockets ++; } if (!IS_NULL(fd1Obj)) { fd1 = fd1Obj.getSocket(); nsockets ++; } if (nsockets == 2) { /* need to choose one of them */ /* was fduse set in peek? */ fduse = _this.fduse; if (fduse == null) { /* not set in peek(), must select on both sockets */ int ret, t = (timeout == 0) ? -1: timeout; cli.System.Net.Sockets.Socket[] tmp = new cli.System.Net.Sockets.Socket[] { fduse }; ret = NET_Timeout2 (fd, fd1, t, tmp); fduse = tmp[0]; if (ret == 2) { fduse = checkLastFD (_this, fd, fd1); } else if (ret <= 0) { if (ret == 0) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketTimeoutException", "Receive timed out"); } else if (ret == JVM_IO_ERR) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "Socket closed"); } else if (ret == JVM_IO_INTR) { JNU_ThrowByName(env, JNU_JAVAIOPKG+"InterruptedIOException", "operation interrupted"); } return; } } } else if (!ipv6_supported) { fduse = fd; } else if (IS_NULL(fdObj)) { /* ipv6 supported: and this socket bound to an IPV6 only address */ fduse = fd1; } else { /* ipv6 supported: and this socket bound to an IPV4 only address */ fduse = fd; } if (IS_NULL(packet)) { JNU_ThrowNullPointerException(env, "packet"); return; } packetBuffer = packet.buf; if (IS_NULL(packetBuffer)) { JNU_ThrowNullPointerException(env, "packet buffer"); return; } packetBufferOffset = packet.offset; packetBufferLen = packet.bufLength; /* if (packetBufferLen > MAX_BUFFER_LEN) { /* When JNI-ifying the JDK's IO routines, we turned * read's and write's of byte arrays of size greater * than 2048 bytes into several operations of size 2048. * This saves a malloc()/memcpy()/free() for big * buffers. This is OK for file IO and TCP, but that * strategy violates the semantics of a datagram protocol. * (one big send) != (several smaller sends). So here * we *must* alloc the buffer. Note it needn't be bigger * than 65,536 (0xFFFF) the max size of an IP packet. * anything bigger is truncated anyway. *-/ fullPacket = (char *)malloc(packetBufferLen); if (!fullPacket) { JNU_ThrowOutOfMemoryError(env, "heap allocation failed"); return; } } else { fullPacket = &(BUF[0]); } */ /* * If this Windows edition supports ICMP port unreachable and if we * are not connected then we need to know if a timeout has been specified * and if so we need to pick up the current time. These are required in * order to implement the semantics of timeout, viz :- * timeout set to t1 but ICMP port unreachable arrives in t2 where * t2 < t1. In this case we must discard the ICMP packets and then * wait for the next packet up to a maximum of t1 minus t2. */ connected = _this.connected; if (supportPortUnreachable() && !connected && timeout != 0 &&!ipv6_supported) { prevTime = JVM_CurrentTimeMillis(env, 0); } if (timeout != 0 && nsockets == 1) { int ret; ret = NET_Timeout(fduse, timeout); if (ret <= 0) { if (ret == 0) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketTimeoutException", "Receive timed out"); } else if (ret == JVM_IO_ERR) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "Socket closed"); } else if (ret == JVM_IO_INTR) { JNU_ThrowByName(env, JNU_JAVAIOPKG+"InterruptedIOException", "operation interrupted"); } return; } } /* * Loop only if we discarding ICMP port unreachable packets */ do { retry = FALSE; /* receive the packet */ n = recvfrom(fduse, packetBuffer, packetBufferOffset, packetBufferLen, 0, remote_addr); if (n == JVM_IO_ERR) { if (WSAGetLastError() == WSAECONNRESET) { /* * An icmp port unreachable has been received - consume any other * outstanding packets. */ purgeOutstandingICMP(fduse); /* * If connected throw a PortUnreachableException */ if (connected) { JNU_ThrowByName(env, JNU_JAVANETPKG+"PortUnreachableException", "ICMP Port Unreachable"); return; } /* * If a timeout was specified then we need to adjust it because * we may have used up some of the timeout before the icmp port * unreachable arrived. */ if (timeout != 0) { int ret; long newTime = JVM_CurrentTimeMillis(env, 0); timeout -= (newTime - prevTime); prevTime = newTime; if (timeout <= 0) { ret = 0; } else { ret = NET_Timeout(fduse, timeout); } if (ret <= 0) { if (ret == 0) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketTimeoutException", "Receive timed out"); } else if (ret == JVM_IO_ERR) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "Socket closed"); } else if (ret == JVM_IO_INTR) { JNU_ThrowByName(env, JNU_JAVAIOPKG+"InterruptedIOException", "operation interrupted"); } return; } } /* * An ICMP port unreachable was received but we are * not connected so ignore it. */ retry = TRUE; } } } while (retry); if (n < 0) { errorCode = WSAGetLastError(); /* check to see if it's because the buffer was too small */ if (errorCode == WSAEMSGSIZE) { /* it is because the buffer is too small. It's UDP, it's * unreliable, it's all good. discard the rest of the * data.. */ n = packetBufferLen; } else { /* failure */ packet.length = 0; } } if (n == -1) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "socket closed"); } else if (n == -2) { JNU_ThrowByName(env, JNU_JAVAIOPKG+"InterruptedIOException", "operation interrupted"); } else if (n < 0) { NET_ThrowCurrent(env, "Datagram receive failed"); } else { int port; InetAddress packetAddress; /* * Check if there is an InetAddress already associated with this * packet. If so we check if it is the same source address. We * can't update any existing InetAddress because it is immutable */ packetAddress = packet.address; if (packetAddress != NULL) { if (!NET_SockaddrEqualsInetAddress(remote_addr, packetAddress)) { /* force a new InetAddress to be created */ packetAddress = null; } } if (packetAddress == NULL) { int[] tmp = { 0 }; packetAddress = NET_SockaddrToInetAddress(remote_addr, tmp); port = tmp[0]; /* stuff the new Inetaddress in the packet */ packet.address = packetAddress; } else { /* only get the new port number */ port = NET_GetPortFromSockaddr(remote_addr); } /* populate the packet */ packet.port = port; packet.length = n; } } /* * Class: java_net_TwoStacksPlainDatagramSocketImpl * Method: datagramSocketCreate * Signature: ()V */ static void datagramSocketCreate(JNIEnv env, TwoStacksPlainDatagramSocketImpl _this) { FileDescriptor fdObj = _this.fd; FileDescriptor fd1Obj = _this.fd1; cli.System.Net.Sockets.Socket fd = null; cli.System.Net.Sockets.Socket fd1 = null; boolean ipv6_supported = ipv6_available(); if (IS_NULL(fdObj) || (ipv6_supported && IS_NULL(fd1Obj))) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "Socket closed"); return; } else { fd = socket (AF_INET, SOCK_DGRAM, 0); } if (fd == INVALID_SOCKET) { NET_ThrowCurrent(env, "Socket creation failed"); return; } fdObj.setSocket(fd); NET_SetSockOpt(fd, SOL_SOCKET, SO_BROADCAST, true); if (ipv6_supported) { /* SIO_UDP_CONNRESET fixes a bug introduced in Windows 2000, which * returns connection reset errors un connected UDP sockets (as well * as connected sockets. The solution is to only enable this feature * when the socket is connected */ WSAIoctl(fd,SIO_UDP_CONNRESET,false); fd1 = socket (AF_INET6, SOCK_DGRAM, 0); if (fd1 == INVALID_SOCKET) { NET_ThrowCurrent(env, "Socket creation failed"); return; } NET_SetSockOpt(fd1, SOL_SOCKET, SO_BROADCAST, true); WSAIoctl(fd1,SIO_UDP_CONNRESET,false); fd1Obj.setSocket(fd1); } else { /* drop the second fd */ _this.fd1 = null; } } /* * Class: java_net_TwoStacksPlainDatagramSocketImpl * Method: datagramSocketClose * Signature: ()V */ static void datagramSocketClose(TwoStacksPlainDatagramSocketImpl _this) { /* * REMIND: PUT A LOCK AROUND THIS CODE */ FileDescriptor fdObj = _this.fd; FileDescriptor fd1Obj = _this.fd1; boolean ipv6_supported = ipv6_available(); cli.System.Net.Sockets.Socket fd = null, fd1 = null; if (IS_NULL(fdObj) && (!ipv6_supported || IS_NULL(fd1Obj))) { return; } if (!IS_NULL(fdObj)) { fd = fdObj.getSocket(); if (fd != null) { fdObj.setSocket(null); NET_SocketClose(fd); } } if (ipv6_supported && fd1Obj != NULL) { fd1 = fd1Obj.getSocket(); if (fd1 == null) { return; } fd1Obj.setSocket(null); NET_SocketClose(fd1); } } /* * check the addresses attached to the NetworkInterface object * and return the first one (of the requested family Ipv4 or Ipv6) * in *iaddr */ private static int getInetAddrFromIf (JNIEnv env, int family, NetworkInterface nif, InetAddress[] iaddr) { InetAddress[] addrArray; int len; InetAddress addr; int i; addrArray = getNetworkInterfaceAddresses(nif); len = addrArray.length; /* * Check that there is at least one address bound to this * interface. */ if (len < 1) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "bad argument for IP_MULTICAST_IF2: No IP addresses bound to interface"); return -1; } for (i=0; i<len; i++) { int fam; addr = addrArray[i]; fam = addr.family; if (fam == family) { iaddr[0] = addr; return 0; } } return -1; } private static int getInet4AddrFromIf (JNIEnv env, NetworkInterface nif, in_addr iaddr) { InetAddress[] addr = new InetAddress[1]; int ret = getInetAddrFromIf (env, IPv4, nif, addr); if (ret == -1) { return -1; } iaddr.s_addr = htonl(addr[0].address); return 0; } /* Get the multicasting index from the interface */ private static int getIndexFromIf (JNIEnv env, NetworkInterface nif) { return nif.getIndex(); } private static InetAddress[] getNetworkInterfaceAddresses(final NetworkInterface nif) { // [IKVM] this is IKVM specific, because I don't want to use reflection (or map.xml hacks) to access the "addrs" member of NetworkInterface return java.security.AccessController.doPrivileged(new java.security.PrivilegedAction<InetAddress[]>() { public InetAddress[] run() { java.util.ArrayList<InetAddress> list = new java.util.ArrayList<InetAddress>(); for (java.util.Enumeration<InetAddress> e = nif.getInetAddresses(); e.hasMoreElements(); ) { list.add(e.nextElement()); } return list.toArray(new InetAddress[list.size()]); } }); } private static NetworkInterface Java_java_net_NetworkInterface_getByIndex(JNIEnv env, int ni_class, int index) { try { return NetworkInterface.getByIndex(index); } catch (Exception x) { env.Throw(x); return null; } } private static NetworkInterface Java_java_net_NetworkInterface_getByInetAddress0(JNIEnv env, int ni_class, Object address) { try { return NetworkInterface.getByInetAddress((InetAddress)address); } catch (Exception x) { env.Throw(x); return null; } } /* * Sets the multicast interface. * * SocketOptions.IP_MULTICAST_IF (argument is an InetAddress) :- * IPv4: set outgoing multicast interface using * IPPROTO_IP/IP_MULTICAST_IF * * IPv6: Get the interface to which the * InetAddress is bound * and do same as SockOptions.IF_MULTICAST_IF2 * * SockOptions.IF_MULTICAST_IF2 (argument is a NetworkInterface ) :- * For each stack: * IPv4: Obtain IP address bound to network interface * (NetworkInterface.addres[0]) * set outgoing multicast interface using * IPPROTO_IP/IP_MULTICAST_IF * * IPv6: Obtain NetworkInterface.index * Set outgoing multicast interface using * IPPROTO_IPV6/IPV6_MULTICAST_IF * */ private static void setMulticastInterface(JNIEnv env, TwoStacksPlainDatagramSocketImpl _this, cli.System.Net.Sockets.Socket fd, cli.System.Net.Sockets.Socket fd1, int opt, Object value) { boolean ipv6_supported = ipv6_available(); if (opt == java_net_SocketOptions_IP_MULTICAST_IF) { /* * value is an InetAddress. * On IPv4 system use IP_MULTICAST_IF socket option * On IPv6 system get the NetworkInterface that this IP * address is bound to and use the IPV6_MULTICAST_IF * option instead of IP_MULTICAST_IF */ if (ipv6_supported) { value = Java_java_net_NetworkInterface_getByInetAddress0(env, ni_class, value); if (value == NULL) { if (env.ExceptionOccurred() == null) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "bad argument for IP_MULTICAST_IF" +": address not bound to any interface"); } return; } opt = java_net_SocketOptions_IP_MULTICAST_IF2; } else { in_addr in = new in_addr(); in.s_addr = htonl(((InetAddress)value).address); if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, in) < 0) { NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG+"SocketException", "Error setting socket option"); } return; } } if (opt == java_net_SocketOptions_IP_MULTICAST_IF2) { /* * value is a NetworkInterface. * On IPv6 system get the index of the interface and use the * IPV6_MULTICAST_IF socket option * On IPv4 system extract addr[0] and use the IP_MULTICAST_IF * option. For IPv6 both must be done. */ if (ipv6_supported) { in_addr in = new in_addr(); int index; index = ((NetworkInterface)value).getIndex(); if (setsockopt(fd1, IPPROTO_IPV6, IPV6_MULTICAST_IF, index) < 0) { if (WSAGetLastError() == WSAEINVAL && index > 0) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "IPV6_MULTICAST_IF failed (interface has IPv4 " +"address only?)"); } else { NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG+"SocketException", "Error setting socket option"); } return; } /* If there are any IPv4 addresses on this interface then * repeat the operation on the IPv4 fd */ if (getInet4AddrFromIf (env, (NetworkInterface)value, in) < 0) { return; } if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, in) < 0) { NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG+"SocketException", "Error setting socket option"); } return; } else { in_addr in = new in_addr(); if (getInet4AddrFromIf (env, (NetworkInterface)value, in) < 0) { if (env.ExceptionOccurred() != null) { return; } JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "no InetAddress instances of requested type"); return; } if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, in) < 0) { NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG+"SocketException", "Error setting socket option"); } return; } } } /* * Class: java_net_TwoStacksPlainDatagramSocketImpl * Method: socketSetOption * Signature: (ILjava/lang/Object;)V */ static void socketSetOption(JNIEnv env, TwoStacksPlainDatagramSocketImpl _this, int opt, Object value) { cli.System.Net.Sockets.Socket fd = null; cli.System.Net.Sockets.Socket fd1 = null; int[] levelv4 = new int[1]; int[] levelv6 = new int[1]; int[] optnamev4 = new int[1]; int[] optnamev6 = new int[1]; Object optval; boolean ipv6_supported = ipv6_available(); fd = getFD(env, _this); if (ipv6_supported) { fd1 = getFD1(env, _this); } if (fd == null && fd1 == null) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "socket closed"); return; } if ((opt == java_net_SocketOptions_IP_MULTICAST_IF) || (opt == java_net_SocketOptions_IP_MULTICAST_IF2)) { setMulticastInterface(env, _this, fd, fd1, opt, value); return; } /* * Map the Java level socket option to the platform specific * level(s) and option name(s). */ if (fd1 != null) { if (NET_MapSocketOptionV6(opt, levelv6, optnamev6) != 0) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "Invalid option"); return; } } if (fd != null) { if (NET_MapSocketOption(opt, levelv4, optnamev4) != 0) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "Invalid option"); return; } } switch (opt) { case java_net_SocketOptions_SO_SNDBUF : case java_net_SocketOptions_SO_RCVBUF : case java_net_SocketOptions_IP_TOS : optval = ((Integer)value).intValue(); break; case java_net_SocketOptions_SO_REUSEADDR: case java_net_SocketOptions_SO_BROADCAST: case java_net_SocketOptions_IP_MULTICAST_LOOP: { boolean on = ((Boolean)value).booleanValue(); optval = on; /* * setLoopbackMode (true) disables IP_MULTICAST_LOOP rather * than enabling it. */ if (opt == java_net_SocketOptions_IP_MULTICAST_LOOP) { optval = !on; } } break; default : JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "Socket option not supported by PlainDatagramSocketImp"); return; } if (fd1 != null) { if (NET_SetSockOpt(fd1, levelv6[0], optnamev6[0], optval) < 0) { NET_ThrowCurrent(env, "setsockopt IPv6"); return; } } if (fd != null) { if (NET_SetSockOpt(fd, levelv4[0], optnamev4[0], optval) < 0) { NET_ThrowCurrent(env, "setsockopt"); return; } } } /* * Return the multicast interface: * * SocketOptions.IP_MULTICAST_IF * IPv4: Query IPPROTO_IP/IP_MULTICAST_IF * Create InetAddress * IP_MULTICAST_IF returns struct ip_mreqn on 2.2 * kernel but struct in_addr on 2.4 kernel * IPv6: Query IPPROTO_IPV6 / IPV6_MULTICAST_IF or * obtain from impl is Linux 2.2 kernel * If index == 0 return InetAddress representing * anyLocalAddress. * If index > 0 query NetworkInterface by index * and returns addrs[0] * * SocketOptions.IP_MULTICAST_IF2 * IPv4: Query IPPROTO_IP/IP_MULTICAST_IF * Query NetworkInterface by IP address and * return the NetworkInterface that the address * is bound too. * IPv6: Query IPPROTO_IPV6 / IPV6_MULTICAST_IF * (except Linux .2 kernel) * Query NetworkInterface by index and * return NetworkInterface. */ private static Object getMulticastInterface(JNIEnv env, TwoStacksPlainDatagramSocketImpl _this, cli.System.Net.Sockets.Socket fd, cli.System.Net.Sockets.Socket fd1, int opt) { boolean isIPV4 = !ipv6_available() || fd1 == null; /* * IPv4 implementation */ if (isIPV4) { Inet4Address addr; in_addr in = new in_addr(); if (getsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, in) < 0) { NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG+"SocketException", "Error getting socket option"); return NULL; } /* * Construct and populate an Inet4Address */ addr = new Inet4Address(); addr.address = ntohl(in.s_addr); /* * For IP_MULTICAST_IF return InetAddress */ if (opt == java_net_SocketOptions_IP_MULTICAST_IF) { return addr; } NetworkInterface ni; ni = Java_java_net_NetworkInterface_getByInetAddress0(env, ni_class, addr); if (ni != null) { return ni; } /* * The address doesn't appear to be bound at any known * NetworkInterface. Therefore we construct a NetworkInterface * with this address. */ return new NetworkInterface(null, -1, new InetAddress[] { addr }); } /* * IPv6 implementation */ if ((opt == java_net_SocketOptions_IP_MULTICAST_IF) || (opt == java_net_SocketOptions_IP_MULTICAST_IF2)) { int index; InetAddress[] addrArray; InetAddress addr; NetworkInterface ni; { int[] tmp = { 0 }; if (getsockopt(fd1, IPPROTO_IPV6, IPV6_MULTICAST_IF, tmp) < 0) { NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG+"SocketException", "Error getting socket option"); return NULL; } index = tmp[0]; } /* * If multicast to a specific interface then return the * interface (for IF2) or the any address on that interface * (for IF). */ if (index > 0) { ni = Java_java_net_NetworkInterface_getByIndex(env, ni_class, index); if (ni == NULL) { String errmsg = "IPV6_MULTICAST_IF returned index to unrecognized interface: " + index; JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", errmsg); return NULL; } /* * For IP_MULTICAST_IF2 return the NetworkInterface */ if (opt == java_net_SocketOptions_IP_MULTICAST_IF2) { return ni; } /* * For IP_MULTICAST_IF return addrs[0] */ addrArray = getNetworkInterfaceAddresses(ni); if (addrArray.length < 1) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "IPV6_MULTICAST_IF returned interface without IP bindings"); return NULL; } addr = addrArray[0]; return addr; } /* * Multicast to any address - return anyLocalAddress * or a NetworkInterface with addrs[0] set to anyLocalAddress */ addr = InetAddress.anyLocalAddress(); if (opt == java_net_SocketOptions_IP_MULTICAST_IF) { return addr; } return new NetworkInterface(null, -1, new InetAddress[] { addr }); } return NULL; } /* * Returns relevant info as a jint. * * Class: java_net_TwoStacksPlainDatagramSocketImpl * Method: socketGetOption * Signature: (I)Ljava/lang/Object; */ static Object socketGetOption(JNIEnv env, TwoStacksPlainDatagramSocketImpl _this, int opt) { cli.System.Net.Sockets.Socket fd = null; cli.System.Net.Sockets.Socket fd1 = null; int[] level = new int[1]; int[] optname = new int[1]; int[] optval = new int[1]; boolean ipv6_supported = ipv6_available(); fd = getFD(env, _this); if (ipv6_supported) { fd1 = getFD1(env, _this); } if (fd == null && fd1 == null) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "Socket closed"); return NULL; } /* * Handle IP_MULTICAST_IF separately */ if (opt == java_net_SocketOptions_IP_MULTICAST_IF || opt == java_net_SocketOptions_IP_MULTICAST_IF2) { return getMulticastInterface(env, _this, fd, fd1, opt); } if (opt == java_net_SocketOptions_SO_BINDADDR) { /* find out local IP address */ SOCKETADDRESS him; him = new SOCKETADDRESS(); InetAddress iaObj; if (fd == null) { fd = fd1; /* must be IPv6 only */ } if (getsockname(fd, him) == -1) { NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG+"SocketException", "Error getting socket name"); return NULL; } iaObj = NET_SockaddrToInetAddress(him, new int[1]); return iaObj; } /* * Map the Java level socket option to the platform specific * level and option name. */ if (NET_MapSocketOption(opt, level, optname) != 0) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "Invalid option"); return NULL; } if (fd == null) { if (NET_MapSocketOptionV6(opt, level, optname) != 0) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "Invalid option"); return NULL; } fd = fd1; /* must be IPv6 only */ } if (NET_GetSockOpt(fd, level[0], optname[0], optval) < 0) { String errmsg = "error getting socket option: " + WSAGetLastError(); JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", errmsg); return NULL; } switch (opt) { case java_net_SocketOptions_SO_BROADCAST: case java_net_SocketOptions_SO_REUSEADDR: return optval[0] != 0; case java_net_SocketOptions_IP_MULTICAST_LOOP: /* getLoopbackMode() returns true if IP_MULTICAST_LOOP is disabled */ return optval[0] == 0; case java_net_SocketOptions_SO_SNDBUF: case java_net_SocketOptions_SO_RCVBUF: case java_net_SocketOptions_IP_TOS: return optval[0]; default : JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "Socket option not supported by TwoStacksPlainDatagramSocketImpl"); return NULL; } } /* * Class: java_net_TwoStacksPlainDatagramSocketImpl * Method: setTimeToLive * Signature: (I)V */ static void setTimeToLive(JNIEnv env, TwoStacksPlainDatagramSocketImpl _this, int ttl) { FileDescriptor fdObj = _this.fd; FileDescriptor fd1Obj = _this.fd1; cli.System.Net.Sockets.Socket fd = null; cli.System.Net.Sockets.Socket fd1 = null; if (IS_NULL(fdObj) && IS_NULL(fd1Obj)) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "Socket closed"); return; } else { if (!IS_NULL(fdObj)) { fd = fdObj.getSocket(); } if (!IS_NULL(fd1Obj)) { fd1 = fd1Obj.getSocket(); } } /* setsockopt to be correct ttl */ if (fd != null) { if (NET_SetSockOpt(fd, IPPROTO_IP, IP_MULTICAST_TTL, ttl) < 0) { NET_ThrowCurrent(env, "set IP_MULTICAST_TTL failed"); } } if (fd1 != null) { if (NET_SetSockOpt(fd1, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, ttl) <0) { NET_ThrowCurrent(env, "set IPV6_MULTICAST_HOPS failed"); } } } /* * Class: java_net_TwoStacksPlainDatagramSocketImpl * Method: setTTL * Signature: (B)V */ static void setTTL(JNIEnv env, TwoStacksPlainDatagramSocketImpl _this, byte ttl) { setTimeToLive(env, _this, ttl & 0xFF); } /* * Class: java_net_TwoStacksPlainDatagramSocketImpl * Method: getTimeToLive * Signature: ()I */ static int getTimeToLive(JNIEnv env, TwoStacksPlainDatagramSocketImpl _this) { FileDescriptor fdObj = _this.fd; FileDescriptor fd1Obj = _this.fd1; cli.System.Net.Sockets.Socket fd = null; cli.System.Net.Sockets.Socket fd1 = null; int[] ttl = new int[1]; if (IS_NULL(fdObj) && IS_NULL(fd1Obj)) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "Socket closed"); return -1; } else { if (!IS_NULL(fdObj)) { fd = fdObj.getSocket(); } if (!IS_NULL(fd1Obj)) { fd1 = fd1Obj.getSocket(); } } /* getsockopt of ttl */ if (fd != null) { if (NET_GetSockOpt(fd, IPPROTO_IP, IP_MULTICAST_TTL, ttl) < 0) { NET_ThrowCurrent(env, "get IP_MULTICAST_TTL failed"); return -1; } return ttl[0]; } if (fd1 != null) { if (NET_GetSockOpt(fd1, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, ttl) < 0) { NET_ThrowCurrent(env, "get IP_MULTICAST_TTL failed"); return -1; } return ttl[0]; } return -1; } /* * Class: java_net_TwoStacksPlainDatagramSocketImpl * Method: getTTL * Signature: ()B */ static byte getTTL(JNIEnv env, TwoStacksPlainDatagramSocketImpl _this) { int result = getTimeToLive(env, _this); return (byte)result; } /* join/leave the named group on the named interface, or if no interface specified * then the interface set with setInterfac(), or the default interface otherwise */ private static void mcast_join_leave(JNIEnv env, TwoStacksPlainDatagramSocketImpl _this, InetAddress iaObj, NetworkInterface niObj, boolean join) { FileDescriptor fdObj = _this.fd; FileDescriptor fd1Obj = _this.fd1; cli.System.Net.Sockets.Socket fd = null; cli.System.Net.Sockets.Socket fd1 = null; SOCKETADDRESS name; name = new SOCKETADDRESS(); ip_mreq mname = new ip_mreq(); ipv6_mreq mname6 = new ipv6_mreq(); in_addr in = new in_addr(); int ifindex; int family; boolean ipv6_supported = ipv6_available(); int cmd ; if (IS_NULL(fdObj) && IS_NULL(fd1Obj)) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "Socket closed"); return; } if (!IS_NULL(fdObj)) { fd = fdObj.getSocket(); } if (ipv6_supported && !IS_NULL(fd1Obj)) { fd1 = fd1Obj.getSocket(); } if (IS_NULL(iaObj)) { JNU_ThrowNullPointerException(env, "address"); return; } if (NET_InetAddressToSockaddr(env, iaObj, 0, name, JNI_FALSE) != 0) { return; } /* Set the multicast group address in the ip_mreq field * eventually this check should be done by the security manager */ family = name.him.sa_family; if (family == AF_INET) { int address = name.him4.sin_addr.s_addr; if (!IN_MULTICAST(ntohl(address))) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "not in multicast"); return; } mname.imr_multiaddr.s_addr = address; if (fd == null) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "Can't join an IPv4 group on an IPv6 only socket"); return; } if (IS_NULL(niObj)) { if (NET_GetSockOpt(fd, IPPROTO_IP, IP_MULTICAST_IF, in) < 0) { NET_ThrowCurrent(env, "get IP_MULTICAST_IF failed"); return; } mname.imr_interface.s_addr = in.s_addr; } else { if (getInet4AddrFromIf (env, niObj, mname.imr_interface) != 0) { NET_ThrowCurrent(env, "no Inet4Address associated with interface"); return; } } cmd = join ? IP_ADD_MEMBERSHIP: IP_DROP_MEMBERSHIP; /* Join the multicast group */ if (NET_SetSockOpt(fd, IPPROTO_IP, cmd, mname) < 0) { if (WSAGetLastError() == WSAENOBUFS) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "IP_ADD_MEMBERSHIP failed (out of hardware filters?)"); } else { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException","error setting options"); } } } else /* AF_INET6 */ { if (ipv6_supported) { in6_addr address; address = in6_addr.FromSockAddr(name); if (!IN6_IS_ADDR_MULTICAST(address)) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "not in6 multicast"); return; } mname6.ipv6mr_multiaddr = address; } else { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "IPv6 not supported"); return; } if (fd1 == null) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "Can't join an IPv6 group on a IPv4 socket"); return; } if (IS_NULL(niObj)) { int[] tmp = { 0 }; if (NET_GetSockOpt(fd1, IPPROTO_IPV6, IPV6_MULTICAST_IF, tmp) < 0) { NET_ThrowCurrent(env, "get IPV6_MULTICAST_IF failed"); return; } ifindex = tmp[0]; } else { ifindex = getIndexFromIf (env, niObj); if (ifindex == -1) { NET_ThrowCurrent(env, "get ifindex failed"); return; } } mname6.ipv6mr_interface = ifindex; cmd = join ? IPV6_ADD_MEMBERSHIP: IPV6_DROP_MEMBERSHIP; /* Join the multicast group */ if (NET_SetSockOpt(fd1, IPPROTO_IPV6, cmd, mname6) < 0) { if (WSAGetLastError() == WSAENOBUFS) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "IP_ADD_MEMBERSHIP failed (out of hardware filters?)"); } else { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException","error setting options"); } } } return; } /* * Class: java_net_TwoStacksPlainDatagramSocketImpl * Method: join * Signature: (Ljava/net/InetAddress;)V */ static void join(JNIEnv env, TwoStacksPlainDatagramSocketImpl _this, InetAddress inetaddr, NetworkInterface netIf) { mcast_join_leave(env, _this, inetaddr, netIf, true); } /* * Class: java_net_TwoStacksPlainDatagramSocketImpl * Method: leave * Signature: (Ljava/net/InetAddress;)V */ static void leave(JNIEnv env, TwoStacksPlainDatagramSocketImpl _this, InetAddress inetaddr, NetworkInterface netIf) { mcast_join_leave(env, _this, inetaddr, netIf, false); } }