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:
Diffstat (limited to 'openjdk/java/net/TwoStacksPlainSocketImpl_c.java')
-rw-r--r--openjdk/java/net/TwoStacksPlainSocketImpl_c.java1173
1 files changed, 1173 insertions, 0 deletions
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;
+ }
+}