diff options
author | jfrijters <jfrijters> | 2010-09-09 10:42:59 +0400 |
---|---|---|
committer | jfrijters <jfrijters> | 2010-09-09 10:42:59 +0400 |
commit | 0720c24a212c240d952a1d20f5595ed15f7fabf3 (patch) | |
tree | 6c920fba4502ce93512292003bb767b32f3ad707 /openjdk/java | |
parent | a514d1e0abbe5023d4f4d9d00639c4c3b23b45ea (diff) |
Prepare for forking. Note that these are .c files renamed to .java, they will be ported to Java in an as straightforward as possible way.
Diffstat (limited to 'openjdk/java')
-rw-r--r-- | openjdk/java/net/TwoStacksPlainDatagramSocketImpl_c.java | 2524 | ||||
-rw-r--r-- | openjdk/java/net/TwoStacksPlainSocketImpl_c.java | 1173 |
2 files changed, 3697 insertions, 0 deletions
diff --git a/openjdk/java/net/TwoStacksPlainDatagramSocketImpl_c.java b/openjdk/java/net/TwoStacksPlainDatagramSocketImpl_c.java new file mode 100644 index 00000000..8e73b352 --- /dev/null +++ b/openjdk/java/net/TwoStacksPlainDatagramSocketImpl_c.java @@ -0,0 +1,2524 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +#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) + +/************************************************************************ + * 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 int xp_or_later = 0; +/* 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 int getFD(JNIEnv *env, jobject this) { + jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID); + + if (fdObj == NULL) { + return -1; + } + return (*env)->GetIntField(env, fdObj, IO_fd_fdID); +} + +static int getFD1(JNIEnv *env, jobject this) { + jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fd1ID); + + if (fdObj == NULL) { + return -1; + } + return (*env)->GetIntField(env, fdObj, IO_fd_fdID); +} + +/* + * 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 + */ +__inline static jboolean supportPortUnreachable() { + static jboolean initDone; + static jboolean portUnreachableSupported; + + if (!initDone) { + OSVERSIONINFO ver; + ver.dwOSVersionInfoSize = sizeof(ver); + GetVersionEx(&ver); + if (ver.dwPlatformId == VER_PLATFORM_WIN32_NT && ver.dwMajorVersion >= 5) { + portUnreachableSupported = JNI_TRUE; + } else { + portUnreachableSupported = JNI_FALSE; + } + initDone = JNI_TRUE; + } + return portUnreachableSupported; +} + +/* + * 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 jboolean purgeOutstandingICMP(JNIEnv *env, jobject this, jint fd) +{ + jboolean got_icmp = JNI_FALSE; + char buf[1]; + fd_set tbl; + struct timeval t = { 0, 0 }; + struct sockaddr_in rmtaddr; + int addrlen = sizeof(rmtaddr); + + /* + * 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(1) { + if (select(/*ignored*/fd+1, &tbl, 0, 0, &t) <= 0) { + break; + } + if (recvfrom(fd, buf, 1, MSG_PEEK, + (struct sockaddr *)&rmtaddr, &addrlen) != JVM_IO_ERR) { + break; + } + if (WSAGetLastError() != WSAECONNRESET) { + /* some other error - we don't care here */ + break; + } + + recvfrom(fd, buf, 1, 0, (struct sockaddr *)&rmtaddr, &addrlen); + 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); +} + +JNIEXPORT void JNICALL +Java_java_net_TwoStacksPlainDatagramSocketImpl_bind0(JNIEnv *env, jobject this, + jint port, jobject addressObj) { + jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID); + jobject fd1Obj = (*env)->GetObjectField(env, this, pdsi_fd1ID); + + int fd, fd1, family; + int ipv6_supported = ipv6_available(); + + SOCKETADDRESS lcladdr; + int lcladdrlen; + int address; + + family = (*env)->GetIntField(env, addressObj, ia_familyID); + 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 = (*env)->GetIntField(env, fdObj, IO_fd_fdID); + if (ipv6_supported) { + fd1 = (*env)->GetIntField(env, fd1Obj, IO_fd_fdID); + } + } + if (IS_NULL(addressObj)) { + JNU_ThrowNullPointerException(env, "argument address"); + return; + } else { + address = (*env)->GetIntField(env, addressObj, ia_addressID); + } + + if (NET_InetAddressToSockaddr(env, addressObj, port, (struct sockaddr *)&lcladdr, &lcladdrlen, JNI_FALSE) != 0) { + return; + } + + if (ipv6_supported) { + struct ipv6bind v6bind; + 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 == -1) { + /* socket is closed. */ + (*env)->SetObjectField(env, this, pdsi_fdID, NULL); + } else { + /* socket was re-created */ + (*env)->SetIntField(env, fdObj, IO_fd_fdID, fd); + } + } + if (v6bind.ipv6_fd != fd1) { + fd1 = v6bind.ipv6_fd; + if (fd1 == -1) { + /* socket is closed. */ + (*env)->SetObjectField(env, this, pdsi_fd1ID, NULL); + } else { + /* socket was re-created */ + (*env)->SetIntField(env, fd1Obj, IO_fd_fdID, fd1); + } + } + } else { + NET_ThrowCurrent (env, "Cannot bind"); + return; + } + } else { + if (bind(fd, (struct sockaddr *)&lcladdr, lcladdrlen) == -1) { + if (WSAGetLastError() == WSAEACCES) { + WSASetLastError(WSAEADDRINUSE); + } + NET_ThrowCurrent(env, "Cannot bind"); + return; + } + } + + if (port == 0) { + if (fd == -1) { + /* must be an IPV6 only socket. */ + fd = fd1; + } + if (getsockname(fd, (struct sockaddr *)&lcladdr, &lcladdrlen) == -1) { + NET_ThrowCurrent(env, "JVM_GetSockName"); + return; + } + port = ntohs((u_short) GET_PORT (&lcladdr)); + } + (*env)->SetIntField(env, this, pdsi_localPortID, port); +} + + +/* + * Class: java_net_TwoStacksPlainDatagramSocketImpl + * Method: connect0 + * Signature: (Ljava/net/InetAddress;I)V + */ + +JNIEXPORT void JNICALL +Java_java_net_TwoStacksPlainDatagramSocketImpl_connect0(JNIEnv *env, jobject this, + jobject address, jint port) { + /* The object's field */ + jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID); + jobject fd1Obj = (*env)->GetObjectField(env, this, pdsi_fd1ID); + /* The fdObj'fd */ + jint fd=-1, fd1=-1, fdc; + /* The packetAddress address, family and port */ + jint addr, family; + SOCKETADDRESS rmtaddr; + int rmtaddrlen; + int 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 = (*env)->GetIntField(env, fdObj, IO_fd_fdID); + } + if (!IS_NULL(fd1Obj)) { + fd1 = (*env)->GetIntField(env, fd1Obj, IO_fd_fdID); + } + + if (IS_NULL(address)) { + JNU_ThrowNullPointerException(env, "address"); + return; + } + + addr = (*env)->GetIntField(env, address, ia_addressID); + + family = (*env)->GetIntField(env, address, ia_familyID); + 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 + */ + DWORD x1, x2; /* ignored result codes */ + int res, t = TRUE; + res = WSAIoctl(fdc,SIO_UDP_CONNRESET,&t,sizeof(t),&x1,sizeof(x1),&x2,0,0); + } + + if (NET_InetAddressToSockaddr(env, address, port,(struct sockaddr *)&rmtaddr, &rmtaddrlen, JNI_FALSE) != 0) { + return; + } + + if (connect(fdc, (struct sockaddr *)&rmtaddr, sizeof(rmtaddr)) == -1) { + NET_ThrowCurrent(env, "connect"); + return; + } +} + +/* + * Class: java_net_TwoStacksPlainDatagramSocketImpl + * Method: disconnect0 + * Signature: ()V + */ + +JNIEXPORT void JNICALL +Java_java_net_TwoStacksPlainDatagramSocketImpl_disconnect0(JNIEnv *env, jobject this, jint family) { + /* The object's field */ + jobject fdObj; + /* The fdObj'fd */ + jint fd, len; + SOCKETADDRESS addr; + + if (family == IPv4) { + fdObj = (*env)->GetObjectField(env, this, pdsi_fdID); + len = sizeof (struct sockaddr_in); + } else { + fdObj = (*env)->GetObjectField(env, this, pdsi_fd1ID); + len = sizeof (struct SOCKADDR_IN6); + } + + if (IS_NULL(fdObj)) { + /* disconnect doesn't throw any exceptions */ + return; + } + fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID); + + memset(&addr, 0, len); + connect(fd, (struct sockaddr *)&addr, len); + + /* + * use SIO_UDP_CONNRESET + * to disable ICMP port unreachable handling here. + */ + if (xp_or_later) { + DWORD x1, x2; /* ignored result codes */ + int t = FALSE; + WSAIoctl(fd,SIO_UDP_CONNRESET,&t,sizeof(t),&x1,sizeof(x1),&x2,0,0); + } +} + +/* + * Class: java_net_TwoStacksPlainDatagramSocketImpl + * Method: send + * Signature: (Ljava/net/DatagramPacket;)V + */ +JNIEXPORT void JNICALL +Java_java_net_TwoStacksPlainDatagramSocketImpl_send(JNIEnv *env, jobject this, + jobject packet) { + + char BUF[MAX_BUFFER_LEN]; + char *fullPacket; + jobject fdObj; + jint fd; + + jobject iaObj; + jint address; + jint family; + + jint packetBufferOffset, packetBufferLen, packetPort; + jbyteArray packetBuffer; + jboolean connected; + + SOCKETADDRESS rmtaddr; + SOCKETADDRESS *addrp = &rmtaddr; + int addrlen; + int x; /* DELETE ME */ + + + if (IS_NULL(packet)) { + JNU_ThrowNullPointerException(env, "null packet"); + return; + } + + iaObj = (*env)->GetObjectField(env, packet, dp_addressID); + + packetPort = (*env)->GetIntField(env, packet, dp_portID); + packetBufferOffset = (*env)->GetIntField(env, packet, dp_offsetID); + packetBuffer = (jbyteArray)(*env)->GetObjectField(env, packet, dp_bufID); + connected = (*env)->GetBooleanField(env, this, pdsi_connected); + + if (IS_NULL(iaObj) || IS_NULL(packetBuffer)) { + JNU_ThrowNullPointerException(env, "null address || null buffer"); + return; + } + + family = (*env)->GetIntField(env, iaObj, ia_familyID); + if (family == IPv4) { + fdObj = (*env)->GetObjectField(env, this, pdsi_fdID); + } else { + if (!ipv6_available()) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", + "Protocol not allowed"); + return; + } + fdObj = (*env)->GetObjectField(env, this, pdsi_fd1ID); + } + + if (IS_NULL(fdObj)) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", + "Socket closed"); + return; + } + fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID); + + packetBufferLen = (*env)->GetIntField(env, packet, dp_lengthID); + + if (connected) { + addrp = 0; /* arg to JVM_Sendto () null in this case */ + addrlen = 0; + } else { + if (NET_InetAddressToSockaddr(env, iaObj, packetPort, (struct sockaddr *)&rmtaddr, &addrlen, 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]); + } + + (*env)->GetByteArrayRegion(env, packetBuffer, packetBufferOffset, packetBufferLen, + (jbyte *)fullPacket); + switch (sendto(fd, fullPacket, packetBufferLen, 0, + (struct sockaddr *)addrp, addrlen)) { + case JVM_IO_ERR: + NET_ThrowCurrent(env, "Datagram send failed"); + break; + + case JVM_IO_INTR: + JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException", + "operation interrupted"); + } + + if (packetBufferLen > MAX_BUFFER_LEN) { + free(fullPacket); + } +} + +/* + * 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. + */ +static int checkLastFD (JNIEnv *env, jobject this, int fd, int fd1) { + int nextfd, lastfd = (*env)->GetIntField(env, this, pdsi_lastfdID); + if (lastfd == -1) { + /* arbitrary. Choose fd */ + (*env)->SetIntField(env, this, pdsi_lastfdID, fd); + return fd; + } else { + if (lastfd == fd) { + nextfd = fd1; + } else { + nextfd = fd; + } + (*env)->SetIntField(env, this, pdsi_lastfdID, nextfd); + return nextfd; + } +} + +/* + * Class: java_net_TwoStacksPlainDatagramSocketImpl + * Method: peek + * Signature: (Ljava/net/InetAddress;)I + */ +JNIEXPORT jint JNICALL +Java_java_net_TwoStacksPlainDatagramSocketImpl_peek(JNIEnv *env, jobject this, + jobject addressObj) { + + jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID); + jint timeout = (*env)->GetIntField(env, this, pdsi_timeoutID); + jint fd; + + /* The address and family fields of addressObj */ + jint address, family; + + int n; + struct sockaddr_in remote_addr; + jint remote_addrsize = sizeof (remote_addr); + char buf[1]; + BOOL retry; + jlong prevTime = 0; + + if (IS_NULL(fdObj)) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed"); + return -1; + } else { + fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID); + if (fd < 0) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", + "socket closed"); + return -1; + } + } + if (IS_NULL(addressObj)) { + JNU_ThrowNullPointerException(env, "Null address in peek()"); + } else { + address = (*env)->GetIntField(env, addressObj, ia_addressID); + /* 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) { + 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, + (struct sockaddr *)&remote_addr, &remote_addrsize); + + if (n == JVM_IO_ERR) { + if (WSAGetLastError() == WSAECONNRESET) { + jboolean 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(env, this, fd); + + connected = (*env)->GetBooleanField(env, this, pdsi_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) { + jlong 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", 0); + return 0; + } + (*env)->SetIntField(env, addressObj, ia_addressID, + ntohl(remote_addr.sin_addr.s_addr)); + (*env)->SetIntField(env, addressObj, ia_familyID, IPv4); + + /* return port */ + return ntohs(remote_addr.sin_port); +} + +JNIEXPORT jint JNICALL +Java_java_net_TwoStacksPlainDatagramSocketImpl_peekData(JNIEnv *env, jobject this, + jobject packet) { + + char BUF[MAX_BUFFER_LEN]; + char *fullPacket; + jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID); + jobject fd1Obj = (*env)->GetObjectField(env, this, pdsi_fd1ID); + jint timeout = (*env)->GetIntField(env, this, pdsi_timeoutID); + + jbyteArray packetBuffer; + jint packetBufferOffset, packetBufferLen; + + int fd, fd1, fduse, nsockets=0, errorCode; + int port; + jbyteArray data; + + int checkBoth = 0, datalen; + int n; + SOCKETADDRESS remote_addr; + jint remote_addrsize=sizeof(remote_addr); + BOOL retry; + jlong prevTime = 0; + + if (!IS_NULL(fdObj)) { + fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID); + if (fd < 0) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", + "socket closed"); + return -1; + } + nsockets = 1; + } + + if (!IS_NULL(fd1Obj)) { + fd1 = (*env)->GetIntField(env, fd1Obj, IO_fd_fdID); + if (fd1 < 0) { + 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 = (*env)->GetObjectField(env, packet, dp_bufID); + + if (IS_NULL(packetBuffer)) { + JNU_ThrowNullPointerException(env, "packet buffer"); + return -1; + } + + packetBufferOffset = (*env)->GetIntField(env, packet, dp_offsetID); + packetBufferLen = (*env)->GetIntField(env, packet, dp_bufLengthID); + + 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); + ret = NET_Timeout2 (fd, fd1, t, &fduse); + /* 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"); + } + if (packetBufferLen > MAX_BUFFER_LEN) { + free(fullPacket); + } + return -1; + } + if (ret == 2) { + fduse = checkLastFD (env, this, fd, fd1); + } + checkBoth = FALSE; + } else if (timeout) { + 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"); + } + if (packetBufferLen > MAX_BUFFER_LEN) { + free(fullPacket); + } + return -1; + } + } + + /* receive the packet */ + n = recvfrom(fduse, fullPacket, packetBufferLen, MSG_PEEK, + (struct sockaddr *)&remote_addr, &remote_addrsize); + port = (int) ntohs ((u_short) GET_PORT((SOCKETADDRESS *)&remote_addr)); + if (n == JVM_IO_ERR) { + if (WSAGetLastError() == WSAECONNRESET) { + jboolean 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(env, this, fduse); + + connected = (*env)->GetBooleanField(env, this, pdsi_connected); + if (connected) { + JNU_ThrowByName(env, JNU_JAVANETPKG "PortUnreachableException", + "ICMP Port Unreachable"); + + if (packetBufferLen > MAX_BUFFER_LEN) { + free(fullPacket); + } + 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) { + jlong newTime = JVM_CurrentTimeMillis(env, 0); + timeout -= (newTime - prevTime); + if (timeout <= 0) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException", + "Receive timed out"); + if (packetBufferLen > MAX_BUFFER_LEN) { + free(fullPacket); + } + return -1; + } + prevTime = newTime; + } + retry = TRUE; + } + } + } while (retry); + + /* truncate the data if the packet's length is too small */ + if (n > packetBufferLen) { + n = packetBufferLen; + } + 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 */ + (*env)->SetIntField(env, packet, dp_lengthID, 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 { + jobject 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 = (*env)->GetObjectField(env, packet, dp_addressID); + if (packetAddress != NULL) { + if (!NET_SockaddrEqualsInetAddress(env, (struct sockaddr *) + &remote_addr, packetAddress)) { + /* force a new InetAddress to be created */ + packetAddress = NULL; + } + } + if (packetAddress == NULL) { + packetAddress = NET_SockaddrToInetAddress(env, (struct sockaddr *) + &remote_addr, &port); + /* stuff the new Inetaddress in the packet */ + (*env)->SetObjectField(env, packet, dp_addressID, packetAddress); + } + + /* populate the packet */ + (*env)->SetByteArrayRegion(env, packetBuffer, packetBufferOffset, n, + (jbyte *)fullPacket); + (*env)->SetIntField(env, packet, dp_portID, port); + (*env)->SetIntField(env, packet, dp_lengthID, n); + } + + /* make sure receive() picks up the right fd */ + (*env)->SetIntField(env, this, pdsi_fduseID, fduse); + + if (packetBufferLen > MAX_BUFFER_LEN) { + free(fullPacket); + } + return port; +} + +/* + * Class: java_net_TwoStacksPlainDatagramSocketImpl + * Method: receive + * Signature: (Ljava/net/DatagramPacket;)V + */ +JNIEXPORT void JNICALL +Java_java_net_TwoStacksPlainDatagramSocketImpl_receive0(JNIEnv *env, jobject this, + jobject packet) { + + char BUF[MAX_BUFFER_LEN]; + char *fullPacket; + jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID); + jobject fd1Obj = (*env)->GetObjectField(env, this, pdsi_fd1ID); + jint timeout = (*env)->GetIntField(env, this, pdsi_timeoutID); + jbyteArray packetBuffer; + jint packetBufferOffset, packetBufferLen; + int 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. + */ + int fd, fd1, fduse, errorCode; + jbyteArray data; + + int datalen; + int n, nsockets=0; + SOCKETADDRESS remote_addr; + jint remote_addrsize=sizeof(remote_addr); + BOOL retry; + jlong prevTime = 0, selectTime=0; + jboolean connected; + + if (IS_NULL(fdObj) && IS_NULL(fd1Obj)) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", + "Socket closed"); + return; + } + + if (!IS_NULL(fdObj)) { + fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID); + nsockets ++; + } + if (!IS_NULL(fd1Obj)) { + fd1 = (*env)->GetIntField(env, fd1Obj, IO_fd_fdID); + nsockets ++; + } + + if (nsockets == 2) { /* need to choose one of them */ + /* was fduse set in peek? */ + fduse = (*env)->GetIntField(env, this, pdsi_fduseID); + if (fduse == -1) { + /* not set in peek(), must select on both sockets */ + int ret, t = (timeout == 0) ? -1: timeout; + ret = NET_Timeout2 (fd, fd1, t, &fduse); + if (ret == 2) { + fduse = checkLastFD (env, 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 = (*env)->GetObjectField(env, packet, dp_bufID); + + if (IS_NULL(packetBuffer)) { + JNU_ThrowNullPointerException(env, "packet buffer"); + return; + } + + packetBufferOffset = (*env)->GetIntField(env, packet, dp_offsetID); + packetBufferLen = (*env)->GetIntField(env, packet, dp_bufLengthID); + + 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 = (*env)->GetBooleanField(env, this, pdsi_connected); + if (supportPortUnreachable() && !connected && timeout &&!ipv6_supported) { + prevTime = JVM_CurrentTimeMillis(env, 0); + } + + if (timeout && 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"); + } + if (packetBufferLen > MAX_BUFFER_LEN) { + free(fullPacket); + } + return; + } + } + + /* + * Loop only if we discarding ICMP port unreachable packets + */ + do { + retry = FALSE; + + /* receive the packet */ + n = recvfrom(fduse, fullPacket, packetBufferLen, 0, + (struct sockaddr *)&remote_addr, &remote_addrsize); + + if (n == JVM_IO_ERR) { + if (WSAGetLastError() == WSAECONNRESET) { + /* + * An icmp port unreachable has been received - consume any other + * outstanding packets. + */ + purgeOutstandingICMP(env, this, fduse); + + /* + * If connected throw a PortUnreachableException + */ + + if (connected) { + JNU_ThrowByName(env, JNU_JAVANETPKG "PortUnreachableException", + "ICMP Port Unreachable"); + + if (packetBufferLen > MAX_BUFFER_LEN) { + free(fullPacket); + } + + 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) { + int ret; + jlong 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"); + } + if (packetBufferLen > MAX_BUFFER_LEN) { + free(fullPacket); + } + return; + } + } + + /* + * An ICMP port unreachable was received but we are + * not connected so ignore it. + */ + retry = TRUE; + } + } + } while (retry); + + /* truncate the data if the packet's length is too small */ + if (n > packetBufferLen) { + n = packetBufferLen; + } + 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 */ + (*env)->SetIntField(env, packet, dp_lengthID, 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; + jobject 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 = (*env)->GetObjectField(env, packet, dp_addressID); + + if (packetAddress != NULL) { + if (!NET_SockaddrEqualsInetAddress(env, (struct sockaddr *)&remote_addr, packetAddress)) { + /* force a new InetAddress to be created */ + packetAddress = NULL; + } + } + if (packetAddress == NULL) { + packetAddress = NET_SockaddrToInetAddress(env, (struct sockaddr *)&remote_addr, &port); + /* stuff the new Inetaddress in the packet */ + (*env)->SetObjectField(env, packet, dp_addressID, packetAddress); + } else { + /* only get the new port number */ + port = NET_GetPortFromSockaddr((struct sockaddr *)&remote_addr); + } + /* populate the packet */ + (*env)->SetByteArrayRegion(env, packetBuffer, packetBufferOffset, n, + (jbyte *)fullPacket); + (*env)->SetIntField(env, packet, dp_portID, port); + (*env)->SetIntField(env, packet, dp_lengthID, n); + } + if (packetBufferLen > MAX_BUFFER_LEN) { + free(fullPacket); + } +} + +/* + * Class: java_net_TwoStacksPlainDatagramSocketImpl + * Method: datagramSocketCreate + * Signature: ()V + */ +JNIEXPORT void JNICALL +Java_java_net_TwoStacksPlainDatagramSocketImpl_datagramSocketCreate(JNIEnv *env, + jobject this) { + jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID); + jobject fd1Obj = (*env)->GetObjectField(env, this, pdsi_fd1ID); + + int fd, fd1; + int t = TRUE; + DWORD x1, x2; /* ignored result codes */ + int ipv6_supported = ipv6_available(); + + int arg = -1; + + if (IS_NULL(fdObj) || (ipv6_supported && IS_NULL(fd1Obj))) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed"); + return; + } else { + fd = (int) socket (AF_INET, SOCK_DGRAM, 0); + } + if (fd == JVM_IO_ERR) { + NET_ThrowCurrent(env, "Socket creation failed"); + return; + } + SetHandleInformation((HANDLE)(UINT_PTR)fd, HANDLE_FLAG_INHERIT, FALSE); + (*env)->SetIntField(env, fdObj, IO_fd_fdID, fd); + NET_SetSockOpt(fd, SOL_SOCKET, SO_BROADCAST, (char*)&t, sizeof(BOOL)); + + 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 + */ + t = FALSE; + WSAIoctl(fd,SIO_UDP_CONNRESET,&t,sizeof(t),&x1,sizeof(x1),&x2,0,0); + t = TRUE; + fd1 = socket (AF_INET6, SOCK_DGRAM, 0); + if (fd1 == JVM_IO_ERR) { + NET_ThrowCurrent(env, "Socket creation failed"); + return; + } + NET_SetSockOpt(fd1, SOL_SOCKET, SO_BROADCAST, (char*)&t, sizeof(BOOL)); + t = FALSE; + WSAIoctl(fd1,SIO_UDP_CONNRESET,&t,sizeof(t),&x1,sizeof(x1),&x2,0,0); + (*env)->SetIntField(env, fd1Obj, IO_fd_fdID, fd1); + SetHandleInformation((HANDLE)(UINT_PTR)fd1, HANDLE_FLAG_INHERIT, FALSE); + } else { + /* drop the second fd */ + (*env)->SetObjectField(env, this, pdsi_fd1ID, NULL); + } +} + +/* + * Class: java_net_TwoStacksPlainDatagramSocketImpl + * Method: datagramSocketClose + * Signature: ()V + */ +JNIEXPORT void JNICALL +Java_java_net_TwoStacksPlainDatagramSocketImpl_datagramSocketClose(JNIEnv *env, + jobject this) { + /* + * REMIND: PUT A LOCK AROUND THIS CODE + */ + jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID); + jobject fd1Obj = (*env)->GetObjectField(env, this, pdsi_fd1ID); + int ipv6_supported = ipv6_available(); + int fd=-1, fd1=-1; + + if (IS_NULL(fdObj) && (!ipv6_supported || IS_NULL(fd1Obj))) { + return; + } + + if (!IS_NULL(fdObj)) { + fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID); + if (fd != -1) { + (*env)->SetIntField(env, fdObj, IO_fd_fdID, -1); + NET_SocketClose(fd); + } + } + + if (ipv6_supported && fd1Obj != NULL) { + fd1 = (*env)->GetIntField(env, fd1Obj, IO_fd_fdID); + if (fd1 == -1) { + return; + } + (*env)->SetIntField(env, fd1Obj, IO_fd_fdID, -1); + 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 + */ + +static int getInetAddrFromIf (JNIEnv *env, int family, jobject nif, jobject *iaddr) +{ + jobjectArray addrArray; + static jfieldID ni_addrsID=0; + static jfieldID ia_familyID=0; + jsize len; + jobject addr; + int i; + + if (ni_addrsID == NULL) { + jclass c = (*env)->FindClass(env, "java/net/NetworkInterface"); + CHECK_NULL_RETURN (c, -1); + ni_addrsID = (*env)->GetFieldID(env, c, "addrs", + "[Ljava/net/InetAddress;"); + CHECK_NULL_RETURN (ni_addrsID, -1); + c = (*env)->FindClass(env,"java/net/InetAddress"); + CHECK_NULL_RETURN (c, -1); + ia_familyID = (*env)->GetFieldID(env, c, "family", "I"); + CHECK_NULL_RETURN (ia_familyID, -1); + } + + addrArray = (*env)->GetObjectField(env, nif, ni_addrsID); + len = (*env)->GetArrayLength(env, addrArray); + + /* + * 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 = (*env)->GetObjectArrayElement(env, addrArray, i); + fam = (*env)->GetIntField(env, addr, ia_familyID); + if (fam == family) { + *iaddr = addr; + return 0; + } + } + return -1; +} + +static int getInet4AddrFromIf (JNIEnv *env, jobject nif, struct in_addr *iaddr) +{ + jobject addr; + static jfieldID ia_addressID; + + int ret = getInetAddrFromIf (env, IPv4, nif, &addr); + if (ret == -1) { + return -1; + } + + if (ia_addressID == 0) { + jclass c = (*env)->FindClass(env,"java/net/InetAddress"); + CHECK_NULL_RETURN (c, -1); + ia_addressID = (*env)->GetFieldID(env, c, "address", "I"); + CHECK_NULL_RETURN (ia_addressID, -1); + } + iaddr->s_addr = htonl((*env)->GetIntField(env, addr, ia_addressID)); + return 0; +} + +/* Get the multicasting index from the interface */ + +static int getIndexFromIf (JNIEnv *env, jobject nif) { + static jfieldID ni_indexID; + + if (ni_indexID == NULL) { + jclass c = (*env)->FindClass(env, "java/net/NetworkInterface"); + CHECK_NULL_RETURN(c, -1); + ni_indexID = (*env)->GetFieldID(env, c, "index", "I"); + CHECK_NULL_RETURN(ni_indexID, -1); + } + + return (*env)->GetIntField(env, nif, ni_indexID); +} + +/* + * 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 + * + */ +static void setMulticastInterface(JNIEnv *env, jobject this, int fd, int fd1, + jint opt, jobject value) +{ + int 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) { + static jclass ni_class; + if (ni_class == NULL) { + jclass c = (*env)->FindClass(env, "java/net/NetworkInterface"); + CHECK_NULL(c); + ni_class = (*env)->NewGlobalRef(env, c); + CHECK_NULL(ni_class); + } + + value = Java_java_net_NetworkInterface_getByInetAddress0(env, ni_class, value); + if (value == NULL) { + if (!(*env)->ExceptionOccurred(env)) { + 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 { + static jfieldID ia_addressID; + struct in_addr in; + + if (ia_addressID == NULL) { + jclass c = (*env)->FindClass(env,"java/net/InetAddress"); + CHECK_NULL(c); + ia_addressID = (*env)->GetFieldID(env, c, "address", "I"); + CHECK_NULL(ia_addressID); + } + + in.s_addr = htonl((*env)->GetIntField(env, value, ia_addressID)); + + if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, + (const char*)&in, sizeof(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) { + static jfieldID ni_indexID; + struct in_addr in; + int index; + + if (ni_indexID == NULL) { + jclass c = (*env)->FindClass(env, "java/net/NetworkInterface"); + CHECK_NULL(c); + ni_indexID = (*env)->GetFieldID(env, c, "index", "I"); + CHECK_NULL(ni_indexID); + } + index = (*env)->GetIntField(env, value, ni_indexID); + + if (setsockopt(fd1, IPPROTO_IPV6, IPV6_MULTICAST_IF, + (const char*)&index, sizeof(index)) < 0) { + if (errno == EINVAL && 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, value, &in) < 0) { + return; + } + if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, + (const char*)&in, sizeof(in)) < 0) { + NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", + "Error setting socket option"); + } + return; + } else { + struct in_addr in; + + if (getInet4AddrFromIf (env, value, &in) < 0) { + if ((*env)->ExceptionOccurred(env)) { + return; + } + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", + "no InetAddress instances of requested type"); + return; + } + + if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, + (const char*)&in, sizeof(in)) < 0) { + NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", + "Error setting socket option"); + } + return; + } + } +} + +/* + * Class: java_net_TwoStacksPlainDatagramSocketImpl + * Method: socketSetOption + * Signature: (ILjava/lang/Object;)V + */ +JNIEXPORT void JNICALL +Java_java_net_TwoStacksPlainDatagramSocketImpl_socketSetOption(JNIEnv *env,jobject this, + jint opt,jobject value) { + + int fd=-1, fd1=-1; + int levelv4, levelv6, optnamev4, optnamev6, optlen; + union { + int i; + char c; + } optval; + int ipv6_supported = ipv6_available(); + + fd = getFD(env, this); + + if (ipv6_supported) { + fd1 = getFD1(env, this); + } + if (fd < 0 && fd1 < 0) { + 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 != -1) { + if (NET_MapSocketOptionV6(opt, &levelv6, &optnamev6)) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Invalid option"); + return; + } + } + if (fd != -1) { + if (NET_MapSocketOption(opt, &levelv4, &optnamev4)) { + 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 : + { + jclass cls; + jfieldID fid; + + cls = (*env)->FindClass(env, "java/lang/Integer"); + CHECK_NULL(cls); + fid = (*env)->GetFieldID(env, cls, "value", "I"); + CHECK_NULL(fid); + + optval.i = (*env)->GetIntField(env, value, fid); + optlen = sizeof(optval.i); + } + break; + + case java_net_SocketOptions_SO_REUSEADDR: + case java_net_SocketOptions_SO_BROADCAST: + case java_net_SocketOptions_IP_MULTICAST_LOOP: + { + jclass cls; + jfieldID fid; + jboolean on; + + cls = (*env)->FindClass(env, "java/lang/Boolean"); + CHECK_NULL(cls); + fid = (*env)->GetFieldID(env, cls, "value", "Z"); + CHECK_NULL(fid); + + on = (*env)->GetBooleanField(env, value, fid); + optval.i = (on ? 1 : 0); + /* + * setLoopbackMode (true) disables IP_MULTICAST_LOOP rather + * than enabling it. + */ + if (opt == java_net_SocketOptions_IP_MULTICAST_LOOP) { + optval.i = !optval.i; + } + optlen = sizeof(optval.i); + } + break; + + default : + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", + "Socket option not supported by PlainDatagramSocketImp"); + break; + + } + + if (fd1 != -1) { + if (NET_SetSockOpt(fd1, levelv6, optnamev6, (void *)&optval, optlen) < 0) { + NET_ThrowCurrent(env, "setsockopt IPv6"); + return; + } + } + if (fd != -1) { + if (NET_SetSockOpt(fd, levelv4, optnamev4, (void *)&optval, optlen) < 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. + */ +jobject getMulticastInterface(JNIEnv *env, jobject this, int fd, int fd1, jint opt) { + jboolean isIPV4 = !ipv6_available() || fd1 == -1; + + /* + * IPv4 implementation + */ + if (isIPV4) { + static jclass inet4_class; + static jmethodID inet4_ctrID; + static jfieldID inet4_addrID; + + static jclass ni_class; + static jmethodID ni_ctrID; + static jfieldID ni_indexID; + static jfieldID ni_addrsID; + + jobjectArray addrArray; + jobject addr; + jobject ni; + + struct in_addr in; + struct in_addr *inP = ∈ + int len = sizeof(struct in_addr); + + if (getsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, + (char *)inP, &len) < 0) { + NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", + "Error getting socket option"); + return NULL; + } + + /* + * Construct and populate an Inet4Address + */ + if (inet4_class == NULL) { + jclass c = (*env)->FindClass(env, "java/net/Inet4Address"); + CHECK_NULL_RETURN(c, NULL); + inet4_ctrID = (*env)->GetMethodID(env, c, "<init>", "()V"); + CHECK_NULL_RETURN(inet4_ctrID, NULL); + inet4_addrID = (*env)->GetFieldID(env, c, "address", "I"); + CHECK_NULL_RETURN(inet4_addrID, NULL); + inet4_class = (*env)->NewGlobalRef(env, c); + CHECK_NULL_RETURN(inet4_class, NULL); + } + addr = (*env)->NewObject(env, inet4_class, inet4_ctrID, 0); + CHECK_NULL_RETURN(addr, NULL); + + (*env)->SetIntField(env, addr, inet4_addrID, ntohl(in.s_addr)); + + /* + * For IP_MULTICAST_IF return InetAddress + */ + if (opt == java_net_SocketOptions_IP_MULTICAST_IF) { + return addr; + } + + /* + * For IP_MULTICAST_IF2 we get the NetworkInterface for + * this address and return it + */ + if (ni_class == NULL) { + jclass c = (*env)->FindClass(env, "java/net/NetworkInterface"); + CHECK_NULL_RETURN(c, NULL); + ni_ctrID = (*env)->GetMethodID(env, c, "<init>", "()V"); + CHECK_NULL_RETURN(ni_ctrID, NULL); + ni_indexID = (*env)->GetFieldID(env, c, "index", "I"); + CHECK_NULL_RETURN(ni_indexID, NULL); + ni_addrsID = (*env)->GetFieldID(env, c, "addrs", + "[Ljava/net/InetAddress;"); + CHECK_NULL_RETURN(ni_addrsID, NULL); + ni_class = (*env)->NewGlobalRef(env, c); + CHECK_NULL_RETURN(ni_class, NULL); + } + ni = Java_java_net_NetworkInterface_getByInetAddress0(env, ni_class, addr); + if (ni) { + return ni; + } + + /* + * The address doesn't appear to be bound at any known + * NetworkInterface. Therefore we construct a NetworkInterface + * with this address. + */ + ni = (*env)->NewObject(env, ni_class, ni_ctrID, 0); + CHECK_NULL_RETURN(ni, NULL); + + (*env)->SetIntField(env, ni, ni_indexID, -1); + addrArray = (*env)->NewObjectArray(env, 1, inet4_class, NULL); + CHECK_NULL_RETURN(addrArray, NULL); + (*env)->SetObjectArrayElement(env, addrArray, 0, addr); + (*env)->SetObjectField(env, ni, ni_addrsID, addrArray); + return ni; + } + + + /* + * IPv6 implementation + */ + if ((opt == java_net_SocketOptions_IP_MULTICAST_IF) || + (opt == java_net_SocketOptions_IP_MULTICAST_IF2)) { + + static jclass ni_class; + static jmethodID ni_ctrID; + static jfieldID ni_indexID; + static jfieldID ni_addrsID; + static jclass ia_class; + static jmethodID ia_anyLocalAddressID; + + int index; + int len = sizeof(index); + + jobjectArray addrArray; + jobject addr; + jobject ni; + + { + if (getsockopt(fd1, IPPROTO_IPV6, IPV6_MULTICAST_IF, + (char*)&index, &len) < 0) { + NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", + "Error getting socket option"); + return NULL; + } + } + + if (ni_class == NULL) { + jclass c = (*env)->FindClass(env, "java/net/NetworkInterface"); + CHECK_NULL_RETURN(c, NULL); + ni_ctrID = (*env)->GetMethodID(env, c, "<init>", "()V"); + CHECK_NULL_RETURN(ni_ctrID, NULL); + ni_indexID = (*env)->GetFieldID(env, c, "index", "I"); + CHECK_NULL_RETURN(ni_indexID, NULL); + ni_addrsID = (*env)->GetFieldID(env, c, "addrs", + "[Ljava/net/InetAddress;"); + CHECK_NULL_RETURN(ni_addrsID, NULL); + + ia_class = (*env)->FindClass(env, "java/net/InetAddress"); + CHECK_NULL_RETURN(ia_class, NULL); + ia_class = (*env)->NewGlobalRef(env, ia_class); + CHECK_NULL_RETURN(ia_class, NULL); + ia_anyLocalAddressID = (*env)->GetStaticMethodID(env, + ia_class, + "anyLocalAddress", + "()Ljava/net/InetAddress;"); + CHECK_NULL_RETURN(ia_anyLocalAddressID, NULL); + ni_class = (*env)->NewGlobalRef(env, c); + CHECK_NULL_RETURN(ni_class, NULL); + } + + /* + * 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) { + char errmsg[255]; + sprintf(errmsg, + "IPV6_MULTICAST_IF returned index to unrecognized interface: %d", + 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 = (*env)->GetObjectField(env, ni, ni_addrsID); + if ((*env)->GetArrayLength(env, addrArray) < 1) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", + "IPV6_MULTICAST_IF returned interface without IP bindings"); + return NULL; + } + + addr = (*env)->GetObjectArrayElement(env, addrArray, 0); + return addr; + } + + /* + * Multicast to any address - return anyLocalAddress + * or a NetworkInterface with addrs[0] set to anyLocalAddress + */ + + addr = (*env)->CallStaticObjectMethod(env, ia_class, ia_anyLocalAddressID, + NULL); + if (opt == java_net_SocketOptions_IP_MULTICAST_IF) { + return addr; + } + + ni = (*env)->NewObject(env, ni_class, ni_ctrID, 0); + CHECK_NULL_RETURN(ni, NULL); + (*env)->SetIntField(env, ni, ni_indexID, -1); + addrArray = (*env)->NewObjectArray(env, 1, ia_class, NULL); + CHECK_NULL_RETURN(addrArray, NULL); + (*env)->SetObjectArrayElement(env, addrArray, 0, addr); + (*env)->SetObjectField(env, ni, ni_addrsID, addrArray); + return ni; + } + return NULL; +} +/* + * Returns relevant info as a jint. + * + * Class: java_net_TwoStacksPlainDatagramSocketImpl + * Method: socketGetOption + * Signature: (I)Ljava/lang/Object; + */ +JNIEXPORT jobject JNICALL +Java_java_net_TwoStacksPlainDatagramSocketImpl_socketGetOption(JNIEnv *env, jobject this, + jint opt) { + + int fd=-1, fd1=-1; + int level, optname, optlen; + union { + int i; + } optval; + int ipv6_supported = ipv6_available(); + + fd = getFD(env, this); + if (ipv6_supported) { + fd1 = getFD1(env, this); + } + + if (fd < 0 && fd1 < 0) { + 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; + int len = 0; + int port; + jobject iaObj; + + len = sizeof (struct sockaddr_in); + + if (fd == -1) { + fd = fd1; /* must be IPv6 only */ + len = sizeof (struct SOCKADDR_IN6); + } + + if (getsockname(fd, (struct sockaddr *)&him, &len) == -1) { + NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", + "Error getting socket name"); + return NULL; + } + iaObj = NET_SockaddrToInetAddress(env, (struct sockaddr *)&him, &port); + + return iaObj; + } + + /* + * Map the Java level socket option to the platform specific + * level and option name. + */ + if (NET_MapSocketOption(opt, &level, &optname)) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Invalid option"); + return NULL; + } + + if (fd == -1) { + if (NET_MapSocketOptionV6(opt, &level, &optname)) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Invalid option"); + return NULL; + } + fd = fd1; /* must be IPv6 only */ + } + + optlen = sizeof(optval.i); + if (NET_GetSockOpt(fd, level, optname, (void *)&optval, &optlen) < 0) { + char errmsg[255]; + sprintf(errmsg, "error getting socket option: %s\n", strerror(errno)); + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", errmsg); + return NULL; + } + + switch (opt) { + case java_net_SocketOptions_SO_BROADCAST: + case java_net_SocketOptions_SO_REUSEADDR: + return createBoolean(env, optval.i); + + case java_net_SocketOptions_IP_MULTICAST_LOOP: + /* getLoopbackMode() returns true if IP_MULTICAST_LOOP is disabled */ + return createBoolean(env, !optval.i); + + case java_net_SocketOptions_SO_SNDBUF: + case java_net_SocketOptions_SO_RCVBUF: + case java_net_SocketOptions_IP_TOS: + return createInteger(env, optval.i); + + default : + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", + "Socket option not supported by TwoStacksPlainDatagramSocketImpl"); + return NULL; + + } +} + +/* + * Class: java_net_TwoStacksPlainDatagramSocketImpl + * Method: setTimeToLive + * Signature: (I)V + */ +JNIEXPORT void JNICALL +Java_java_net_TwoStacksPlainDatagramSocketImpl_setTimeToLive(JNIEnv *env, jobject this, + jint ttl) { + + jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID); + jobject fd1Obj = (*env)->GetObjectField(env, this, pdsi_fd1ID); + int fd = -1, fd1 = -1; + int ittl = (int)ttl; + + if (IS_NULL(fdObj) && IS_NULL(fd1Obj)) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", + "Socket closed"); + return; + } else { + if (!IS_NULL(fdObj)) { + fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID); + } + if (!IS_NULL(fd1Obj)) { + fd1 = (*env)->GetIntField(env, fd1Obj, IO_fd_fdID); + } + } + + /* setsockopt to be correct ttl */ + if (fd >= 0) { + if (NET_SetSockOpt(fd, IPPROTO_IP, IP_MULTICAST_TTL, (char*)&ittl, + sizeof (ittl)) < 0) { + NET_ThrowCurrent(env, "set IP_MULTICAST_TTL failed"); + } + } + + if (fd1 >= 0) { + if (NET_SetSockOpt(fd1, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (char *)&ittl, + sizeof(ittl)) <0) { + NET_ThrowCurrent(env, "set IPV6_MULTICAST_HOPS failed"); + } + } +} + +/* + * Class: java_net_TwoStacksPlainDatagramSocketImpl + * Method: setTTL + * Signature: (B)V + */ +JNIEXPORT void JNICALL +Java_java_net_TwoStacksPlainDatagramSocketImpl_setTTL(JNIEnv *env, jobject this, + jbyte ttl) { + Java_java_net_TwoStacksPlainDatagramSocketImpl_setTimeToLive(env, this, + (jint)ttl & 0xFF); +} + +/* + * Class: java_net_TwoStacksPlainDatagramSocketImpl + * Method: getTimeToLive + * Signature: ()I + */ +JNIEXPORT jint JNICALL +Java_java_net_TwoStacksPlainDatagramSocketImpl_getTimeToLive(JNIEnv *env, jobject this) { + jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID); + jobject fd1Obj = (*env)->GetObjectField(env, this, pdsi_fd1ID); + int fd = -1, fd1 = -1; + int ttl = 0; + int len = sizeof(ttl); + + if (IS_NULL(fdObj) && IS_NULL(fd1Obj)) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", + "Socket closed"); + return -1; + } else { + if (!IS_NULL(fdObj)) { + fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID); + } + if (!IS_NULL(fd1Obj)) { + fd1 = (*env)->GetIntField(env, fd1Obj, IO_fd_fdID); + } + } + + /* getsockopt of ttl */ + if (fd >= 0) { + if (NET_GetSockOpt(fd, IPPROTO_IP, IP_MULTICAST_TTL, (char*)&ttl, &len) < 0) { + NET_ThrowCurrent(env, "get IP_MULTICAST_TTL failed"); + return -1; + } + return (jint)ttl; + } + if (fd1 >= 0) { + if (NET_GetSockOpt(fd1, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (char*)&ttl, &len) < 0) { + NET_ThrowCurrent(env, "get IP_MULTICAST_TTL failed"); + return -1; + } + return (jint)ttl; + } + return -1; +} + +/* + * Class: java_net_TwoStacksPlainDatagramSocketImpl + * Method: getTTL + * Signature: ()B + */ +JNIEXPORT jbyte JNICALL +Java_java_net_TwoStacksPlainDatagramSocketImpl_getTTL(JNIEnv *env, jobject this) { + int result = Java_java_net_TwoStacksPlainDatagramSocketImpl_getTimeToLive(env, this); + + return (jbyte)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 */ + +static void mcast_join_leave(JNIEnv *env, jobject this, + jobject iaObj, jobject niObj, + jboolean join) +{ + jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID); + jobject fd1Obj = (*env)->GetObjectField(env, this, pdsi_fd1ID); + jint fd = -1, fd1 = -1; + + SOCKETADDRESS name; + struct ip_mreq mname; + struct ipv6_mreq mname6; + + struct in_addr in; + DWORD ifindex; + + int len, family; + int 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 = (*env)->GetIntField(env, fdObj, IO_fd_fdID); + } + if (ipv6_supported && !IS_NULL(fd1Obj)) { + fd1 = (*env)->GetIntField(env, fd1Obj, IO_fd_fdID); + } + + if (IS_NULL(iaObj)) { + JNU_ThrowNullPointerException(env, "address"); + return; + } + + if (NET_InetAddressToSockaddr(env, iaObj, 0, (struct sockaddr *)&name, &len, 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 < 0) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Can't join an IPv4 group on an IPv6 only socket"); + return; + } + if (IS_NULL(niObj)) { + len = sizeof (in); + if (NET_GetSockOpt(fd, IPPROTO_IP, IP_MULTICAST_IF, + (char *)&in, &len) < 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, (char *) &mname, sizeof (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) { + struct in6_addr *address; + address = &name.him6.sin6_addr; + 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 < 0) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Can't join an IPv6 group on a IPv4 socket"); + return; + } + if (IS_NULL(niObj)) { + len = sizeof (ifindex); + if (NET_GetSockOpt(fd1, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifindex, &len) < 0) { + NET_ThrowCurrent(env, "get IPV6_MULTICAST_IF failed"); + return; + } + } 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, (char *) &mname6, sizeof (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 + */ +JNIEXPORT void JNICALL +Java_java_net_TwoStacksPlainDatagramSocketImpl_join(JNIEnv *env, jobject this, + jobject iaObj, jobject niObj) +{ + mcast_join_leave (env, this, iaObj, niObj, JNI_TRUE); +} + +/* + * Class: java_net_TwoStacksPlainDatagramSocketImpl + * Method: leave + * Signature: (Ljava/net/InetAddress;)V + */ +JNIEXPORT void JNICALL +Java_java_net_TwoStacksPlainDatagramSocketImpl_leave(JNIEnv *env, jobject this, + jobject iaObj, jobject niObj) +{ + mcast_join_leave (env, this, iaObj, niObj, JNI_FALSE); +} diff --git a/openjdk/java/net/TwoStacksPlainSocketImpl_c.java b/openjdk/java/net/TwoStacksPlainSocketImpl_c.java new file mode 100644 index 00000000..cd694933 --- /dev/null +++ b/openjdk/java/net/TwoStacksPlainSocketImpl_c.java @@ -0,0 +1,1173 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +#include <windows.h> +#include <winsock2.h> +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <malloc.h> +#include <sys/types.h> + +#include "java_net_SocketOptions.h" +#include "java_net_TwoStacksPlainSocketImpl.h" +#include "java_net_SocketImpl.h" +#include "java_net_InetAddress.h" +#include "java_io_FileDescriptor.h" +#include "java_lang_Integer.h" + +#include "jvm.h" +#include "net_util.h" +#include "jni_util.h" + +/************************************************************************ + * TwoStacksPlainSocketImpl + */ + +static jfieldID IO_fd_fdID; + +jfieldID psi_fdID; +jfieldID psi_fd1ID; +jfieldID psi_addressID; +jfieldID psi_portID; +jfieldID psi_localportID; +jfieldID psi_timeoutID; +jfieldID psi_trafficClassID; +jfieldID psi_serverSocketID; +jfieldID psi_lastfdID; + +/* + * the level of the TCP protocol for setsockopt and getsockopt + * we only want to look this up once, from the static initializer + * of TwoStacksPlainSocketImpl + */ +static int tcp_level = -1; + +static int getFD(JNIEnv *env, jobject this) { + jobject fdObj = (*env)->GetObjectField(env, this, psi_fdID); + + if (fdObj == NULL) { + return -1; + } + return (*env)->GetIntField(env, fdObj, IO_fd_fdID); +} + +static int getFD1(JNIEnv *env, jobject this) { + jobject fdObj = (*env)->GetObjectField(env, this, psi_fd1ID); + + if (fdObj == NULL) { + return -1; + } + return (*env)->GetIntField(env, fdObj, IO_fd_fdID); +} + + +/* + * The initProto function is called whenever TwoStacksPlainSocketImpl is + * loaded, to cache fieldIds for efficiency. This is called everytime + * the Java class is loaded. + * + * Class: java_net_TwoStacksPlainSocketImpl + * Method: initProto + + * Signature: ()V + */ +JNIEXPORT void JNICALL +Java_java_net_TwoStacksPlainSocketImpl_initProto(JNIEnv *env, jclass cls) { + + struct protoent *proto = getprotobyname("TCP"); + tcp_level = (proto == 0 ? IPPROTO_TCP: proto->p_proto); + + psi_fdID = (*env)->GetFieldID(env, cls , "fd", "Ljava/io/FileDescriptor;"); + CHECK_NULL(psi_fdID); + psi_fd1ID =(*env)->GetFieldID(env, cls , "fd1", "Ljava/io/FileDescriptor;"); + CHECK_NULL(psi_fd1ID); + psi_addressID = (*env)->GetFieldID(env, cls, "address", + "Ljava/net/InetAddress;"); + CHECK_NULL(psi_addressID); + psi_portID = (*env)->GetFieldID(env, cls, "port", "I"); + CHECK_NULL(psi_portID); + psi_lastfdID = (*env)->GetFieldID(env, cls, "lastfd", "I"); + CHECK_NULL(psi_portID); + psi_localportID = (*env)->GetFieldID(env, cls, "localport", "I"); + CHECK_NULL(psi_localportID); + psi_timeoutID = (*env)->GetFieldID(env, cls, "timeout", "I"); + CHECK_NULL(psi_timeoutID); + psi_trafficClassID = (*env)->GetFieldID(env, cls, "trafficClass", "I"); + CHECK_NULL(psi_trafficClassID); + psi_serverSocketID = (*env)->GetFieldID(env, cls, "serverSocket", + "Ljava/net/ServerSocket;"); + CHECK_NULL(psi_serverSocketID); + IO_fd_fdID = NET_GetFileDescriptorID(env); + CHECK_NULL(IO_fd_fdID); +} + +/* + * Class: java_net_TwoStacksPlainSocketImpl + * Method: socketCreate + * Signature: (Z)V + */ +JNIEXPORT void JNICALL +Java_java_net_TwoStacksPlainSocketImpl_socketCreate(JNIEnv *env, jobject this, + jboolean stream) { + jobject fdObj, fd1Obj; + int fd, fd1; + + fdObj = (*env)->GetObjectField(env, this, psi_fdID); + + if (IS_NULL(fdObj)) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", + "null fd object"); + return; + } + fd = socket(AF_INET, (stream ? SOCK_STREAM: SOCK_DGRAM), 0); + if (fd == -1) { + NET_ThrowCurrent(env, "create"); + return; + } else { + /* Set socket attribute so it is not passed to any child process */ + SetHandleInformation((HANDLE)(UINT_PTR)fd, HANDLE_FLAG_INHERIT, FALSE); + (*env)->SetIntField(env, fdObj, IO_fd_fdID, (int)fd); + } + if (ipv6_available()) { + fd1Obj = (*env)->GetObjectField(env, this, psi_fd1ID); + + if (IS_NULL(fd1Obj)) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", + "null fd1 object"); + (*env)->SetIntField(env, fdObj, IO_fd_fdID, -1); + NET_SocketClose(fd); + return; + } + fd1 = socket(AF_INET6, (stream ? SOCK_STREAM: SOCK_DGRAM), 0); + if (fd1 == -1) { + NET_ThrowCurrent(env, "create"); + (*env)->SetIntField(env, fdObj, IO_fd_fdID, -1); + NET_SocketClose(fd); + return; + } else { + (*env)->SetIntField(env, fd1Obj, IO_fd_fdID, fd1); + } + } else { + (*env)->SetObjectField(env, this, psi_fd1ID, NULL); + } +} + +/* + * inetAddress is the address object passed to the socket connect + * call. + * + * Class: java_net_TwoStacksPlainSocketImpl + * Method: socketConnect + * Signature: (Ljava/net/InetAddress;I)V + */ +JNIEXPORT void JNICALL +Java_java_net_TwoStacksPlainSocketImpl_socketConnect(JNIEnv *env, jobject this, + jobject iaObj, jint port, + jint timeout) +{ + jint localport = (*env)->GetIntField(env, this, psi_localportID); + + /* family and localport are int fields of iaObj */ + int family; + jint fd, fd1=-1; + jint len; + int ipv6_supported = ipv6_available(); + + /* fd initially points to the IPv4 socket and fd1 to the IPv6 socket + * If we want to connect to IPv6 then we swap the two sockets/objects + * This way, fd is always the connected socket, and fd1 always gets closed. + */ + jobject fdObj = (*env)->GetObjectField(env, this, psi_fdID); + jobject fd1Obj = (*env)->GetObjectField(env, this, psi_fd1ID); + + SOCKETADDRESS him; + + /* The result of the connection */ + int connect_res; + + if (!IS_NULL(fdObj)) { + fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID); + } + + if (ipv6_supported && !IS_NULL(fd1Obj)) { + fd1 = (*env)->GetIntField(env, fd1Obj, IO_fd_fdID); + } + + if (IS_NULL(iaObj)) { + JNU_ThrowNullPointerException(env, "inet address argument is null."); + return; + } + + if (NET_InetAddressToSockaddr(env, iaObj, port, (struct sockaddr *)&him, &len, JNI_FALSE) != 0) { + return; + } + + family = him.him.sa_family; + if (family == AF_INET6) { + if (!ipv6_supported) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", + "Protocol family not supported"); + return; + } else { + if (fd1 == -1) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", + "Destination unreachable"); + return; + } + /* close the v4 socket, and set fd to be the v6 socket */ + (*env)->SetObjectField(env, this, psi_fdID, fd1Obj); + (*env)->SetObjectField(env, this, psi_fd1ID, NULL); + NET_SocketClose(fd); + fd = fd1; fdObj = fd1Obj; + } + } else { + if (fd1 != -1) { + (*env)->SetIntField(env, fd1Obj, IO_fd_fdID, -1); + NET_SocketClose(fd1); + } + if (fd == -1) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", + "Destination unreachable"); + return; + } + } + (*env)->SetObjectField(env, this, psi_fd1ID, NULL); + + if (timeout <= 0) { + connect_res = connect(fd, (struct sockaddr *) &him, SOCKETADDRESS_LEN(&him)); + if (connect_res == SOCKET_ERROR) { + connect_res = WSAGetLastError(); + } + } else { + int optval; + int optlen = sizeof(optval); + + /* make socket non-blocking */ + optval = 1; + ioctlsocket( fd, FIONBIO, &optval ); + + /* initiate the connect */ + connect_res = connect(fd, (struct sockaddr *) &him, SOCKETADDRESS_LEN(&him)); + if (connect_res == SOCKET_ERROR) { + if (WSAGetLastError() != WSAEWOULDBLOCK) { + connect_res = WSAGetLastError(); + } else { + fd_set wr, ex; + struct timeval t; + + FD_ZERO(&wr); + FD_ZERO(&ex); + FD_SET(fd, &wr); + FD_SET(fd, &ex); + t.tv_sec = timeout / 1000; + t.tv_usec = (timeout % 1000) * 1000; + + /* + * Wait for timout, connection established or + * connection failed. + */ + connect_res = select(fd+1, 0, &wr, &ex, &t); + + /* + * Timeout before connection is established/failed so + * we throw exception and shutdown input/output to prevent + * socket from being used. + * The socket should be closed immediately by the caller. + */ + if (connect_res == 0) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException", + "connect timed out"); + shutdown( fd, SD_BOTH ); + + /* make socket blocking again - just in case */ + optval = 0; + ioctlsocket( fd, FIONBIO, &optval ); + return; + } + + /* + * We must now determine if the connection has been established + * or if it has failed. The logic here is designed to work around + * bug on Windows NT whereby using getsockopt to obtain the + * last error (SO_ERROR) indicates there is no error. The workaround + * on NT is to allow winsock to be scheduled and this is done by + * yielding and retrying. As yielding is problematic in heavy + * load conditions we attempt up to 3 times to get the error reason. + */ + if (!FD_ISSET(fd, &ex)) { + connect_res = 0; + } else { + int retry; + for (retry=0; retry<3; retry++) { + NET_GetSockOpt(fd, SOL_SOCKET, SO_ERROR, + (char*)&connect_res, &optlen); + if (connect_res) { + break; + } + Sleep(0); + } + + if (connect_res == 0) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", + "Unable to establish connection"); + return; + } + } + } + } + + /* make socket blocking again */ + optval = 0; + ioctlsocket(fd, FIONBIO, &optval); + } + + if (connect_res) { + if (connect_res == WSAEADDRNOTAVAIL) { + JNU_ThrowByName(env, JNU_JAVANETPKG "ConnectException", + "connect: Address is invalid on local machine, or port is not valid on remote machine"); + } else { + NET_ThrowNew(env, connect_res, "connect"); + } + return; + } + + (*env)->SetIntField(env, fdObj, IO_fd_fdID, (int)fd); + + /* set the remote peer address and port */ + (*env)->SetObjectField(env, this, psi_addressID, iaObj); + (*env)->SetIntField(env, this, psi_portID, port); + + /* + * we need to initialize the local port field if bind was called + * previously to the connect (by the client) then localport field + * will already be initialized + */ + if (localport == 0) { + /* Now that we're a connected socket, let's extract the port number + * that the system chose for us and store it in the Socket object. + */ + u_short port; + int len = SOCKETADDRESS_LEN(&him); + if (getsockname(fd, (struct sockaddr *)&him, &len) == -1) { + + if (WSAGetLastError() == WSAENOTSOCK) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", + "Socket closed"); + } else { + NET_ThrowCurrent(env, "getsockname failed"); + } + return; + } + port = ntohs ((u_short)GET_PORT(&him)); + (*env)->SetIntField(env, this, psi_localportID, (int) port); + } +} + +/* + * Class: java_net_TwoStacksPlainSocketImpl + * Method: socketBind + * Signature: (Ljava/net/InetAddress;I)V + */ +JNIEXPORT void JNICALL +Java_java_net_TwoStacksPlainSocketImpl_socketBind(JNIEnv *env, jobject this, + jobject iaObj, jint localport) { + + /* fdObj is the FileDescriptor field on this */ + jobject fdObj, fd1Obj; + /* fd is an int field on fdObj */ + int fd, fd1, len; + int ipv6_supported = ipv6_available(); + + /* family is an int field of iaObj */ + int family; + int rv; + + SOCKETADDRESS him; + + fdObj = (*env)->GetObjectField(env, this, psi_fdID); + fd1Obj = (*env)->GetObjectField(env, this, psi_fd1ID); + + family = (*env)->GetIntField(env, iaObj, ia_familyID); + + 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 = (*env)->GetIntField(env, fdObj, IO_fd_fdID); + if (ipv6_supported) { + fd1 = (*env)->GetIntField(env, fd1Obj, IO_fd_fdID); + } + } + if (IS_NULL(iaObj)) { + JNU_ThrowNullPointerException(env, "inet address argument"); + return; + } + + if (NET_InetAddressToSockaddr(env, iaObj, localport, + (struct sockaddr *)&him, &len, JNI_FALSE) != 0) { + return; + } + + if (ipv6_supported) { + struct ipv6bind v6bind; + v6bind.addr = &him; + v6bind.ipv4_fd = fd; + v6bind.ipv6_fd = fd1; + rv = NET_BindV6(&v6bind); + if (rv != -1) { + /* check if the fds have changed */ + if (v6bind.ipv4_fd != fd) { + fd = v6bind.ipv4_fd; + if (fd == -1) { + /* socket is closed. */ + (*env)->SetObjectField(env, this, psi_fdID, NULL); + } else { + /* socket was re-created */ + (*env)->SetIntField(env, fdObj, IO_fd_fdID, fd); + } + } + if (v6bind.ipv6_fd != fd1) { + fd1 = v6bind.ipv6_fd; + if (fd1 == -1) { + /* socket is closed. */ + (*env)->SetObjectField(env, this, psi_fd1ID, NULL); + } else { + /* socket was re-created */ + (*env)->SetIntField(env, fd1Obj, IO_fd_fdID, fd1); + } + } + } + } else { + rv = NET_Bind(fd, (struct sockaddr *)&him, len); + } + + if (rv == -1) { + NET_ThrowCurrent(env, "JVM_Bind"); + return; + } + + /* set the address */ + (*env)->SetObjectField(env, this, psi_addressID, iaObj); + + /* intialize the local port */ + if (localport == 0) { + /* Now that we're a bound socket, let's extract the port number + * that the system chose for us and store it in the Socket object. + */ + int len = SOCKETADDRESS_LEN(&him); + u_short port; + fd = him.him.sa_family == AF_INET? fd: fd1; + + if (getsockname(fd, (struct sockaddr *)&him, &len) == -1) { + NET_ThrowCurrent(env, "getsockname in plain socketBind"); + return; + } + port = ntohs ((u_short) GET_PORT (&him)); + + (*env)->SetIntField(env, this, psi_localportID, (int) port); + } else { + (*env)->SetIntField(env, this, psi_localportID, localport); + } +} + +/* + * Class: java_net_TwoStacksPlainSocketImpl + * Method: socketListen + * Signature: (I)V + */ +JNIEXPORT void JNICALL +Java_java_net_TwoStacksPlainSocketImpl_socketListen (JNIEnv *env, jobject this, + jint count) +{ + /* this FileDescriptor fd field */ + jobject fdObj = (*env)->GetObjectField(env, this, psi_fdID); + jobject fd1Obj = (*env)->GetObjectField(env, this, psi_fd1ID); + jobject address; + /* fdObj's int fd field */ + int fd, fd1; + SOCKETADDRESS addr; int addrlen; + + if (IS_NULL(fdObj) && IS_NULL(fd1Obj)) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", + "socket closed"); + return; + } + + if (!IS_NULL(fdObj)) { + fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID); + } + /* Listen on V4 if address type is v4 or if v6 and address is ::0. + * Listen on V6 if address type is v6 or if v4 and address is 0.0.0.0. + * In cases, where we listen on one space only, we close the other socket. + */ + address = (*env)->GetObjectField(env, this, psi_addressID); + if (IS_NULL(address)) { + JNU_ThrowNullPointerException(env, "socket address"); + return; + } + if (NET_InetAddressToSockaddr(env, address, 0, (struct sockaddr *)&addr, + &addrlen, JNI_FALSE) != 0) { + return; + } + + if (addr.him.sa_family == AF_INET || IN6ADDR_ISANY(&addr.him6)) { + /* listen on v4 */ + if (listen(fd, count) == -1) { + NET_ThrowCurrent(env, "listen failed"); + } + } else { + NET_SocketClose (fd); + (*env)->SetObjectField(env, this, psi_fdID, NULL); + } + if (ipv6_available() && !IS_NULL(fd1Obj)) { + fd1 = (*env)->GetIntField(env, fd1Obj, IO_fd_fdID); + if (addr.him.sa_family == AF_INET6 || addr.him4.sin_addr.s_addr == INADDR_ANY) { + /* listen on v6 */ + if (listen(fd1, count) == -1) { + NET_ThrowCurrent(env, "listen failed"); + } + } else { + NET_SocketClose (fd1); + (*env)->SetObjectField(env, this, psi_fd1ID, NULL); + } + } +} + +/* + * Class: java_net_TwoStacksPlainSocketImpl + * Method: socketAccept + * Signature: (Ljava/net/SocketImpl;)V + */ +JNIEXPORT void JNICALL +Java_java_net_TwoStacksPlainSocketImpl_socketAccept(JNIEnv *env, jobject this, + jobject socket) +{ + /* fields on this */ + jint port; + jint timeout = (*env)->GetIntField(env, this, psi_timeoutID); + jobject fdObj = (*env)->GetObjectField(env, this, psi_fdID); + jobject fd1Obj = (*env)->GetObjectField(env, this, psi_fd1ID); + + /* the FileDescriptor field on socket */ + jobject socketFdObj; + + /* cache the Inet4/6Address classes */ + static jclass inet4Cls; + static jclass inet6Cls; + + /* the InetAddress field on socket */ + jobject socketAddressObj; + + /* the fd int field on fdObj */ + jint fd=-1, fd1=-1; + + SOCKETADDRESS him; + jint len; + + if (IS_NULL(fdObj) && IS_NULL(fd1Obj)) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", + "Socket closed"); + return; + } + if (!IS_NULL(fdObj)) { + fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID); + } + if (!IS_NULL(fd1Obj)) { + fd1 = (*env)->GetIntField(env, fd1Obj, IO_fd_fdID); + } + if (IS_NULL(socket)) { + JNU_ThrowNullPointerException(env, "socket is null"); + return; + } else { + socketFdObj = (*env)->GetObjectField(env, socket, psi_fdID); + socketAddressObj = (*env)->GetObjectField(env, socket, psi_addressID); + } + if ((IS_NULL(socketAddressObj)) || (IS_NULL(socketFdObj))) { + JNU_ThrowNullPointerException(env, "socket address or fd obj"); + return; + } + if (fd != -1 && fd1 != -1) { + fd_set rfds; + struct timeval t, *tP=&t; + int lastfd, res, fd2; + FD_ZERO(&rfds); + FD_SET(fd,&rfds); + FD_SET(fd1,&rfds); + if (timeout) { + t.tv_sec = timeout/1000; + t.tv_usec = (timeout%1000)*1000; + } else { + tP = NULL; + } + res = select (fd, &rfds, NULL, NULL, tP); + if (res == 0) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException", + "Accept timed out"); + return; + } else if (res == 1) { + fd2 = FD_ISSET(fd, &rfds)? fd: fd1; + } else if (res == 2) { + /* avoid starvation */ + lastfd = (*env)->GetIntField(env, this, psi_lastfdID); + if (lastfd != -1) { + fd2 = lastfd==fd? fd1: fd; + } else { + fd2 = fd; + } + (*env)->SetIntField(env, this, psi_lastfdID, fd2); + } else { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", + "select failed"); + return; + } + if (fd2 == fd) { /* v4 */ + len = sizeof (struct sockaddr_in); + } else { + len = sizeof (struct SOCKADDR_IN6); + } + fd = fd2; + } else { + int ret; + if (fd1 != -1) { + fd = fd1; + len = sizeof (struct SOCKADDR_IN6); + } else { + len = sizeof (struct sockaddr_in); + } + if (timeout) { + ret = NET_Timeout(fd, timeout); + if (ret == 0) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException", + "Accept timed out"); + return; + } else if (ret == -1) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "socket closed"); + /* REMIND: SOCKET CLOSED PROBLEM */ + /* NET_ThrowCurrent(env, "Accept failed"); */ + return; + } else if (ret == -2) { + JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException", + "operation interrupted"); + return; + } + } + } + fd = accept(fd, (struct sockaddr *)&him, &len); + if (fd < 0) { + /* REMIND: SOCKET CLOSED PROBLEM */ + if (fd == -2) { + JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException", + "operation interrupted"); + } else { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", + "socket closed"); + } + return; + } + (*env)->SetIntField(env, socketFdObj, IO_fd_fdID, fd); + + if (him.him.sa_family == AF_INET) { + if (inet4Cls == NULL) { + jclass c = (*env)->FindClass(env, "java/net/Inet4Address"); + if (c != NULL) { + inet4Cls = (*env)->NewGlobalRef(env, c); + (*env)->DeleteLocalRef(env, c); + } + } + + /* + * fill up the remote peer port and address in the new socket structure + */ + if (inet4Cls != NULL) { + socketAddressObj = (*env)->NewObject(env, inet4Cls, ia4_ctrID); + } else { + socketAddressObj = NULL; + } + if (socketAddressObj == NULL) { + /* + * FindClass or NewObject failed so close connection and + * exist (there will be a pending exception). + */ + NET_SocketClose(fd); + return; + } + + (*env)->SetIntField(env, socketAddressObj, ia_addressID, + ntohl(him.him4.sin_addr.s_addr)); + (*env)->SetIntField(env, socketAddressObj, ia_familyID, IPv4); + (*env)->SetObjectField(env, socket, psi_addressID, socketAddressObj); + } else { + jbyteArray addr; + /* AF_INET6 -> Inet6Address */ + if (inet6Cls == 0) { + jclass c = (*env)->FindClass(env, "java/net/Inet6Address"); + if (c != NULL) { + inet6Cls = (*env)->NewGlobalRef(env, c); + (*env)->DeleteLocalRef(env, c); + } + } + + if (inet6Cls != NULL) { + socketAddressObj = (*env)->NewObject(env, inet6Cls, ia6_ctrID); + } else { + socketAddressObj = NULL; + } + if (socketAddressObj == NULL) { + /* + * FindClass or NewObject failed so close connection and + * exist (there will be a pending exception). + */ + NET_SocketClose(fd); + return; + } + addr = (*env)->GetObjectField (env, socketAddressObj, ia6_ipaddressID); + (*env)->SetByteArrayRegion (env, addr, 0, 16, (const char *)&him.him6.sin6_addr); + (*env)->SetIntField(env, socketAddressObj, ia_familyID, IPv6); + (*env)->SetIntField(env, socketAddressObj, ia6_scopeidID, him.him6.sin6_scope_id); + } + /* fields common to AF_INET and AF_INET6 */ + + port = ntohs ((u_short) GET_PORT (&him)); + (*env)->SetIntField(env, socket, psi_portID, (int)port); + port = (*env)->GetIntField(env, this, psi_localportID); + (*env)->SetIntField(env, socket, psi_localportID, port); + (*env)->SetObjectField(env, socket, psi_addressID, socketAddressObj); +} + +/* + * Class: java_net_TwoStacksPlainSocketImpl + * Method: socketAvailable + * Signature: ()I + */ +JNIEXPORT jint JNICALL +Java_java_net_TwoStacksPlainSocketImpl_socketAvailable(JNIEnv *env, jobject this) { + + jint available = -1; + jint res; + jobject fdObj = (*env)->GetObjectField(env, this, psi_fdID); + jint fd; + + if (IS_NULL(fdObj)) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed"); + return -1; + } else { + fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID); + } + res = ioctlsocket(fd, FIONREAD, &available); + /* if result isn't 0, it means an error */ + if (res != 0) { + NET_ThrowNew(env, res, "socket available"); + } + return available; +} + +/* + * Class: java_net_TwoStacksPlainSocketImpl + * Method: socketClose + * Signature: ()V + */ +JNIEXPORT void JNICALL +Java_java_net_TwoStacksPlainSocketImpl_socketClose0(JNIEnv *env, jobject this, + jboolean useDeferredClose) { + + jobject fdObj = (*env)->GetObjectField(env, this, psi_fdID); + jobject fd1Obj = (*env)->GetObjectField(env, this, psi_fd1ID); + jint fd=-1, fd1=-1; + + if (IS_NULL(fdObj) && IS_NULL(fd1Obj)) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", + "socket already closed"); + return; + } + if (!IS_NULL(fdObj)) { + fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID); + } + if (!IS_NULL(fd1Obj)) { + fd1 = (*env)->GetIntField(env, fd1Obj, IO_fd_fdID); + } + if (fd != -1) { + (*env)->SetIntField(env, fdObj, IO_fd_fdID, -1); + NET_SocketClose(fd); + } + if (fd1 != -1) { + (*env)->SetIntField(env, fd1Obj, IO_fd_fdID, -1); + NET_SocketClose(fd1); + } +} + +/* + * Socket options for plainsocketImpl + * + * + * Class: java_net_TwoStacksPlainSocketImpl + * Method: socketSetOption + * Signature: (IZLjava/lang/Object;)V + */ +JNIEXPORT void JNICALL +Java_java_net_TwoStacksPlainSocketImpl_socketSetOption(JNIEnv *env, jobject this, + jint cmd, jboolean on, + jobject value) { + int fd, fd1; + int level, optname, optlen; + union { + int i; + struct linger ling; + } optval; + + /* + * Get SOCKET and check that it hasn't been closed + */ + fd = getFD(env, this); + fd1 = getFD1(env, this); + if (fd < 0 && fd1 < 0) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed"); + return; + } + + /* + * SO_TIMEOUT is the socket option used to specify the timeout + * for ServerSocket.accept and Socket.getInputStream().read. + * It does not typically map to a native level socket option. + * For Windows we special-case this and use the SOL_SOCKET/SO_RCVTIMEO + * socket option to specify a receive timeout on the socket. This + * receive timeout is applicable to Socket only and the socket + * option should not be set on ServerSocket. + */ + if (cmd == java_net_SocketOptions_SO_TIMEOUT) { + + /* + * Don't enable the socket option on ServerSocket as it's + * meaningless (we don't receive on a ServerSocket). + */ + jobject ssObj = (*env)->GetObjectField(env, this, psi_serverSocketID); + if (ssObj != NULL) { + return; + } + + /* + * SO_RCVTIMEO is only supported on Microsoft's implementation + * of Windows Sockets so if WSAENOPROTOOPT returned then + * reset flag and timeout will be implemented using + * select() -- see SocketInputStream.socketRead. + */ + if (isRcvTimeoutSupported) { + jclass iCls = (*env)->FindClass(env, "java/lang/Integer"); + jfieldID i_valueID; + jint timeout; + + CHECK_NULL(iCls); + i_valueID = (*env)->GetFieldID(env, iCls, "value", "I"); + CHECK_NULL(i_valueID); + timeout = (*env)->GetIntField(env, value, i_valueID); + + /* + * Disable SO_RCVTIMEO if timeout is <= 5 second. + */ + if (timeout <= 5000) { + timeout = 0; + } + + if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, + sizeof(timeout)) < 0) { + if (WSAGetLastError() == WSAENOPROTOOPT) { + isRcvTimeoutSupported = JNI_FALSE; + } else { + NET_ThrowCurrent(env, "setsockopt SO_RCVTIMEO"); + } + } + if (fd1 != -1) { + if (setsockopt(fd1, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, + sizeof(timeout)) < 0) { + NET_ThrowCurrent(env, "setsockopt SO_RCVTIMEO"); + } + } + } + return; + } + + /* + * Map the Java level socket option to the platform specific + * level + */ + if (NET_MapSocketOption(cmd, &level, &optname)) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", + "Invalid option"); + return; + } + + switch (cmd) { + + case java_net_SocketOptions_TCP_NODELAY : + case java_net_SocketOptions_SO_OOBINLINE : + case java_net_SocketOptions_SO_KEEPALIVE : + case java_net_SocketOptions_SO_REUSEADDR : + optval.i = (on ? 1 : 0); + optlen = sizeof(optval.i); + break; + + case java_net_SocketOptions_SO_SNDBUF : + case java_net_SocketOptions_SO_RCVBUF : + case java_net_SocketOptions_IP_TOS : + { + jclass cls; + jfieldID fid; + + cls = (*env)->FindClass(env, "java/lang/Integer"); + CHECK_NULL(cls); + fid = (*env)->GetFieldID(env, cls, "value", "I"); + CHECK_NULL(fid); + + optval.i = (*env)->GetIntField(env, value, fid); + optlen = sizeof(optval.i); + } + break; + + case java_net_SocketOptions_SO_LINGER : + { + jclass cls; + jfieldID fid; + + cls = (*env)->FindClass(env, "java/lang/Integer"); + CHECK_NULL(cls); + fid = (*env)->GetFieldID(env, cls, "value", "I"); + CHECK_NULL(fid); + + if (on) { + optval.ling.l_onoff = 1; + optval.ling.l_linger = (*env)->GetIntField(env, value, fid); + } else { + optval.ling.l_onoff = 0; + optval.ling.l_linger = 0; + } + optlen = sizeof(optval.ling); + } + break; + + default: /* shouldn't get here */ + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", + "Option not supported by TwoStacksPlainSocketImpl"); + return; + } + + if (fd != -1) { + if (NET_SetSockOpt(fd, level, optname, (void *)&optval, optlen) < 0) { + NET_ThrowCurrent(env, "setsockopt"); + } + } + + if (fd1 != -1) { + if (NET_SetSockOpt(fd1, level, optname, (void *)&optval, optlen) < 0) { + NET_ThrowCurrent(env, "setsockopt"); + } + } +} + + +/* + * Class: java_net_TwoStacksPlainSocketImpl + * Method: socketGetOption + * Signature: (I)I + */ +JNIEXPORT jint JNICALL +Java_java_net_TwoStacksPlainSocketImpl_socketGetOption(JNIEnv *env, jobject this, + jint opt, jobject iaContainerObj) { + + int fd, fd1; + int level, optname, optlen; + union { + int i; + struct linger ling; + } optval; + + /* + * Get SOCKET and check it hasn't been closed + */ + fd = getFD(env, this); + fd1 = getFD1(env, this); + + if (fd < 0 && fd1 < 0) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed"); + return -1; + } + if (fd < 0) { + fd = fd1; + } + + /* For IPv6, we assume both sockets have the same setting always */ + + /* + * SO_BINDADDR isn't a socket option + */ + if (opt == java_net_SocketOptions_SO_BINDADDR) { + SOCKET_ADDRESS him; + int len; + int port; + jobject iaObj; + jclass iaCntrClass; + jfieldID iaFieldID; + + len = sizeof(struct sockaddr_in); + + if (fd == -1) { + /* must be an IPV6 only socket. Case where both sockets are != -1 + * is handled in java + */ + fd = getFD1 (env, this); + len = sizeof(struct SOCKADDR_IN6); + } + + if (getsockname(fd, (struct sockaddr *)&him, &len) < 0) { + NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", + "Error getting socket name"); + return -1; + } + iaObj = NET_SockaddrToInetAddress(env, (struct sockaddr *)&him, &port); + CHECK_NULL_RETURN(iaObj, -1); + + iaCntrClass = (*env)->GetObjectClass(env, iaContainerObj); + iaFieldID = (*env)->GetFieldID(env, iaCntrClass, "addr", "Ljava/net/InetAddress;"); + CHECK_NULL_RETURN(iaFieldID, -1); + (*env)->SetObjectField(env, iaContainerObj, iaFieldID, iaObj); + return 0; /* notice change from before */ + } + + /* + * Map the Java level socket option to the platform specific + * level and option name. + */ + if (NET_MapSocketOption(opt, &level, &optname)) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Invalid option"); + return -1; + } + + /* + * Args are int except for SO_LINGER + */ + if (opt == java_net_SocketOptions_SO_LINGER) { + optlen = sizeof(optval.ling); + } else { + optlen = sizeof(optval.i); + optval.i = 0; + } + + if (NET_GetSockOpt(fd, level, optname, (void *)&optval, &optlen) < 0) { + NET_ThrowCurrent(env, "getsockopt"); + return -1; + } + + switch (opt) { + case java_net_SocketOptions_SO_LINGER: + return (optval.ling.l_onoff ? optval.ling.l_linger: -1); + + case java_net_SocketOptions_SO_SNDBUF: + case java_net_SocketOptions_SO_RCVBUF: + case java_net_SocketOptions_IP_TOS: + return optval.i; + + case java_net_SocketOptions_TCP_NODELAY : + case java_net_SocketOptions_SO_OOBINLINE : + case java_net_SocketOptions_SO_KEEPALIVE : + case java_net_SocketOptions_SO_REUSEADDR : + return (optval.i == 0) ? -1 : 1; + + default: /* shouldn't get here */ + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", + "Option not supported by TwoStacksPlainSocketImpl"); + return -1; + } +} + +/* + * Class: java_net_TwoStacksPlainSocketImpl + * Method: socketShutdown + * Signature: (I)V + */ +JNIEXPORT void JNICALL +Java_java_net_TwoStacksPlainSocketImpl_socketShutdown(JNIEnv *env, jobject this, + jint howto) +{ + + jobject fdObj = (*env)->GetObjectField(env, this, psi_fdID); + jint fd; + + /* + * WARNING: THIS NEEDS LOCKING. ALSO: SHOULD WE CHECK for fd being + * -1 already? + */ + if (IS_NULL(fdObj)) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", + "socket already closed"); + return; + } else { + fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID); + } + shutdown(fd, howto); +} + +/* + * Class: java_net_TwoStacksPlainSocketImpl + * Method: socketSendUrgentData + * Signature: (B)V + */ +JNIEXPORT void JNICALL +Java_java_net_TwoStacksPlainSocketImpl_socketSendUrgentData(JNIEnv *env, jobject this, + jint data) { + /* The fd field */ + jobject fdObj = (*env)->GetObjectField(env, this, psi_fdID); + int n, fd; + unsigned char d = data & 0xff; + + if (IS_NULL(fdObj)) { + JNU_ThrowByName(env, "java/net/SocketException", "Socket closed"); + return; + } else { + fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID); + /* Bug 4086704 - If the Socket associated with this file descriptor + * was closed (sysCloseFD), the the file descriptor is set to -1. + */ + if (fd == -1) { + JNU_ThrowByName(env, "java/net/SocketException", "Socket closed"); + return; + } + + } + n = send(fd, (char *)&data, 1, MSG_OOB); + if (n == JVM_IO_ERR) { + NET_ThrowCurrent(env, "send"); + return; + } + if (n == JVM_IO_INTR) { + JNU_ThrowByName(env, "java/io/InterruptedIOException", 0); + return; + } +} |