Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/ikvm-fork.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjfrijters <jfrijters>2010-09-09 10:42:59 +0400
committerjfrijters <jfrijters>2010-09-09 10:42:59 +0400
commit0720c24a212c240d952a1d20f5595ed15f7fabf3 (patch)
tree6c920fba4502ce93512292003bb767b32f3ad707 /openjdk/java
parenta514d1e0abbe5023d4f4d9d00639c4c3b23b45ea (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.java2524
-rw-r--r--openjdk/java/net/TwoStacksPlainSocketImpl_c.java1173
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 = &in;
+ 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;
+ }
+}