jdk/src/java.base/windows/native/libnet/TwoStacksPlainSocketImpl.c
changeset 25859 3317bb8137f4
parent 25551 62ad60d30644
child 26458 d6a5aed9204b
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/windows/native/libnet/TwoStacksPlainSocketImpl.c	Sun Aug 17 15:54:13 2014 +0100
@@ -0,0 +1,1171 @@
+/*
+ * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#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_InetAddress.h"
+#include "java_io_FileDescriptor.h"
+#include "java_lang_Integer.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 {
+            /* Set socket attribute so it is not passed to any child process */
+            SetHandleInformation((HANDLE)(UINT_PTR)fd1, HANDLE_FLAG_INHERIT, FALSE);
+            (*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;
+    memset((char *)&him, 0, sizeof(him));
+
+    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,
+                                         jboolean exclBind) {
+
+    /* fdObj is the FileDescriptor field on this */
+    jobject fdObj, fd1Obj;
+    /* fd is an int field on fdObj */
+    int fd, fd1, len = 0;
+    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 = getInetAddress_family(env, iaObj);
+
+    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, exclBind);
+        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_WinBind(fd, (struct sockaddr *)&him, len, exclBind);
+    }
+
+    if (rv == -1) {
+        NET_ThrowCurrent(env, "NET_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;
+        }
+
+        setInetAddress_addr(env, socketAddressObj, ntohl(him.him4.sin_addr.s_addr));
+        setInetAddress_family(env, socketAddressObj, IPv4);
+        (*env)->SetObjectField(env, socket, psi_addressID, socketAddressObj);
+    } else {
+        /* 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;
+        }
+        setInet6Address_ipaddress(env, socketAddressObj, (char *)&him.him6.sin6_addr);
+        setInetAddress_family(env, socketAddressObj, IPv6);
+        setInet6Address_scopeid(env, socketAddressObj, 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:    socketNativeSetOption
+ * Signature: (IZLjava/lang/Object;)V
+ */
+JNIEXPORT void JNICALL
+Java_java_net_TwoStacksPlainSocketImpl_socketNativeSetOption(JNIEnv *env,
+                                              jobject this,
+                                              jint cmd, jboolean on,
+                                              jobject value) {
+    int fd, fd1;
+    int level = 0, optname = 0, optlen = 0;
+    union {
+        int i;
+        struct linger ling;
+    } optval;
+
+    memset((char *)&optval, 0, sizeof(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 =
+                        (unsigned short)(*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 = 0, optname = 0, optlen = 0;
+    union {
+        int i;
+        struct linger ling;
+    } optval;
+    /*
+     * Get SOCKET and check it hasn't been closed
+     */
+    fd = getFD(env, this);
+    fd1 = getFD1(env, this);
+    memset((char *)&optval, 0, sizeof(optval));
+
+    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) {
+        SOCKETADDRESS him;
+        int len;
+        int port;
+        jobject iaObj;
+        jclass iaCntrClass;
+        jfieldID iaFieldID;
+
+        len = sizeof(him);
+        memset((char *)&him, 0, len);
+
+        if (fd == -1) {
+            /* must be an IPV6 only socket. Case where both sockets are != -1
+             * is handled in java
+             */
+            fd = getFD1 (env, this);
+        }
+
+        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 == -1) {
+        NET_ThrowCurrent(env, "send");
+        return;
+    }
+}