jdk/src/java.base/windows/native/libnet/DualStackPlainSocketImpl.c
changeset 25859 3317bb8137f4
parent 25551 62ad60d30644
child 28663 5bedcbb25c5f
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/windows/native/libnet/DualStackPlainSocketImpl.c	Sun Aug 17 15:54:13 2014 +0100
@@ -0,0 +1,479 @@
+/*
+ * Copyright (c) 2007, 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 "jni.h"
+#include "net_util.h"
+#include "java_net_DualStackPlainSocketImpl.h"
+
+#define SET_BLOCKING 0
+#define SET_NONBLOCKING 1
+
+static jclass isa_class;        /* java.net.InetSocketAddress */
+static jmethodID isa_ctorID;    /* InetSocketAddress(InetAddress, int) */
+
+/*
+ * Class:     java_net_DualStackPlainSocketImpl
+ * Method:    initIDs
+ * Signature: ()V
+ */
+JNIEXPORT void JNICALL Java_java_net_DualStackPlainSocketImpl_initIDs
+  (JNIEnv *env, jclass clazz) {
+
+    jclass cls = (*env)->FindClass(env, "java/net/InetSocketAddress");
+    CHECK_NULL(cls);
+    isa_class = (*env)->NewGlobalRef(env, cls);
+    isa_ctorID = (*env)->GetMethodID(env, cls, "<init>",
+                                     "(Ljava/net/InetAddress;I)V");
+
+    // implement read timeout with select.
+    isRcvTimeoutSupported = 0;
+}
+
+/*
+ * Class:     java_net_DualStackPlainSocketImpl
+ * Method:    socket0
+ * Signature: (ZZ)I
+ */
+JNIEXPORT jint JNICALL Java_java_net_DualStackPlainSocketImpl_socket0
+  (JNIEnv *env, jclass clazz, jboolean stream, jboolean v6Only /*unused*/) {
+    int fd, rv, opt=0;
+
+    fd = NET_Socket(AF_INET6, (stream ? SOCK_STREAM : SOCK_DGRAM), 0);
+    if (fd == INVALID_SOCKET) {
+        NET_ThrowNew(env, WSAGetLastError(), "create");
+        return -1;
+    }
+
+    rv = setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &opt, sizeof(opt));
+    if (rv == SOCKET_ERROR) {
+        NET_ThrowNew(env, WSAGetLastError(), "create");
+    }
+
+    SetHandleInformation((HANDLE)(UINT_PTR)fd, HANDLE_FLAG_INHERIT, FALSE);
+
+    return fd;
+}
+
+/*
+ * Class:     java_net_DualStackPlainSocketImpl
+ * Method:    bind0
+ * Signature: (ILjava/net/InetAddress;I)V
+ */
+JNIEXPORT void JNICALL Java_java_net_DualStackPlainSocketImpl_bind0
+  (JNIEnv *env, jclass clazz, jint fd, jobject iaObj, jint port,
+   jboolean exclBind)
+{
+    SOCKETADDRESS sa;
+    int rv;
+    int sa_len = sizeof(sa);
+
+    if (NET_InetAddressToSockaddr(env, iaObj, port, (struct sockaddr *)&sa,
+                                 &sa_len, JNI_TRUE) != 0) {
+      return;
+    }
+
+    rv = NET_WinBind(fd, (struct sockaddr *)&sa, sa_len, exclBind);
+
+    if (rv == SOCKET_ERROR)
+        NET_ThrowNew(env, WSAGetLastError(), "NET_Bind");
+}
+
+/*
+ * Class:     java_net_DualStackPlainSocketImpl
+ * Method:    connect0
+ * Signature: (ILjava/net/InetAddress;I)I
+ */
+JNIEXPORT jint JNICALL Java_java_net_DualStackPlainSocketImpl_connect0
+  (JNIEnv *env, jclass clazz, jint fd, jobject iaObj, jint port) {
+    SOCKETADDRESS sa;
+    int rv;
+    int sa_len = sizeof(sa);
+
+    if (NET_InetAddressToSockaddr(env, iaObj, port, (struct sockaddr *)&sa,
+                                 &sa_len, JNI_TRUE) != 0) {
+      return -1;
+    }
+
+    rv = connect(fd, (struct sockaddr *)&sa, sa_len);
+    if (rv == SOCKET_ERROR) {
+        int err = WSAGetLastError();
+        if (err == WSAEWOULDBLOCK) {
+            return java_net_DualStackPlainSocketImpl_WOULDBLOCK;
+        } else if (err == 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, err, "connect");
+        }
+        return -1;  // return value not important.
+    }
+    return rv;
+}
+
+/*
+ * Class:     java_net_DualStackPlainSocketImpl
+ * Method:    waitForConnect
+ * Signature: (II)V
+ */
+JNIEXPORT void JNICALL Java_java_net_DualStackPlainSocketImpl_waitForConnect
+  (JNIEnv *env, jclass clazz, jint fd, jint timeout) {
+    int rv, retry;
+    int optlen = sizeof(rv);
+    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 timeout, connection established or
+     * connection failed.
+     */
+    rv = 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 (rv == 0) {
+        JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException",
+                        "connect timed out");
+        shutdown( fd, SD_BOTH );
+        return;
+    }
+
+    /*
+     * Socket is writable or error occurred. On some Windows editions
+     * the socket will appear writable when the connect fails so we
+     * check for error rather than writable.
+     */
+    if (!FD_ISSET(fd, &ex)) {
+        return;         /* connection established */
+    }
+
+    /*
+     * Connection 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.
+     */
+    for (retry=0; retry<3; retry++) {
+        NET_GetSockOpt(fd, SOL_SOCKET, SO_ERROR,
+                       (char*)&rv, &optlen);
+        if (rv) {
+            break;
+        }
+        Sleep(0);
+    }
+
+    if (rv == 0) {
+        JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
+                        "Unable to establish connection");
+    } else {
+        NET_ThrowNew(env, rv, "connect");
+    }
+}
+
+/*
+ * Class:     java_net_DualStackPlainSocketImpl
+ * Method:    localPort0
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL Java_java_net_DualStackPlainSocketImpl_localPort0
+  (JNIEnv *env, jclass clazz, jint fd) {
+    SOCKETADDRESS sa;
+    int len = sizeof(sa);
+
+    if (getsockname(fd, (struct sockaddr *)&sa, &len) == SOCKET_ERROR) {
+        if (WSAGetLastError() == WSAENOTSOCK) {
+            JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
+                    "Socket closed");
+        } else {
+            NET_ThrowNew(env, WSAGetLastError(), "getsockname failed");
+        }
+        return -1;
+    }
+    return (int) ntohs((u_short)GET_PORT(&sa));
+}
+
+/*
+ * Class:     java_net_DualStackPlainSocketImpl
+ * Method:    localAddress
+ * Signature: (ILjava/net/InetAddressContainer;)V
+ */
+JNIEXPORT void JNICALL Java_java_net_DualStackPlainSocketImpl_localAddress
+  (JNIEnv *env, jclass clazz, jint fd, jobject iaContainerObj) {
+    int port;
+    SOCKETADDRESS sa;
+    int len = sizeof(sa);
+    jobject iaObj;
+    jclass iaContainerClass;
+    jfieldID iaFieldID;
+
+    if (getsockname(fd, (struct sockaddr *)&sa, &len) == SOCKET_ERROR) {
+        NET_ThrowNew(env, WSAGetLastError(), "Error getting socket name");
+        return;
+    }
+    iaObj = NET_SockaddrToInetAddress(env, (struct sockaddr *)&sa, &port);
+    CHECK_NULL(iaObj);
+
+    iaContainerClass = (*env)->GetObjectClass(env, iaContainerObj);
+    iaFieldID = (*env)->GetFieldID(env, iaContainerClass, "addr", "Ljava/net/InetAddress;");
+    CHECK_NULL(iaFieldID);
+    (*env)->SetObjectField(env, iaContainerObj, iaFieldID, iaObj);
+}
+
+
+/*
+ * Class:     java_net_DualStackPlainSocketImpl
+ * Method:    listen0
+ * Signature: (II)V
+ */
+JNIEXPORT void JNICALL Java_java_net_DualStackPlainSocketImpl_listen0
+  (JNIEnv *env, jclass clazz, jint fd, jint backlog) {
+    if (listen(fd, backlog) == SOCKET_ERROR) {
+        NET_ThrowNew(env, WSAGetLastError(), "listen failed");
+    }
+}
+
+/*
+ * Class:     java_net_DualStackPlainSocketImpl
+ * Method:    accept0
+ * Signature: (I[Ljava/net/InetSocketAddress;)I
+ */
+JNIEXPORT jint JNICALL Java_java_net_DualStackPlainSocketImpl_accept0
+  (JNIEnv *env, jclass clazz, jint fd, jobjectArray isaa) {
+    int newfd, port=0;
+    jobject isa;
+    jobject ia;
+    SOCKETADDRESS sa;
+    int len = sizeof(sa);
+
+    memset((char *)&sa, 0, len);
+    newfd = accept(fd, (struct sockaddr *)&sa, &len);
+
+    if (newfd == INVALID_SOCKET) {
+        if (WSAGetLastError() == -2) {
+            JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException",
+                            "operation interrupted");
+        } else {
+            JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
+                            "socket closed");
+        }
+        return -1;
+    }
+
+    ia = NET_SockaddrToInetAddress(env, (struct sockaddr *)&sa, &port);
+    isa = (*env)->NewObject(env, isa_class, isa_ctorID, ia, port);
+    (*env)->SetObjectArrayElement(env, isaa, 0, isa);
+
+    return newfd;
+}
+
+/*
+ * Class:     java_net_DualStackPlainSocketImpl
+ * Method:    waitForNewConnection
+ * Signature: (II)V
+ */
+JNIEXPORT void JNICALL Java_java_net_DualStackPlainSocketImpl_waitForNewConnection
+  (JNIEnv *env, jclass clazz, jint fd, jint timeout) {
+    int rv;
+
+    rv = NET_Timeout(fd, timeout);
+    if (rv == 0) {
+        JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException",
+                        "Accept timed out");
+    } else if (rv == -1) {
+        JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "socket closed");
+    } else if (rv == -2) {
+        JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException",
+                        "operation interrupted");
+    }
+}
+
+/*
+ * Class:     java_net_DualStackPlainSocketImpl
+ * Method:    available0
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL Java_java_net_DualStackPlainSocketImpl_available0
+  (JNIEnv *env, jclass clazz, jint fd) {
+    jint available = -1;
+
+    if ((ioctlsocket(fd, FIONREAD, &available)) == SOCKET_ERROR) {
+        NET_ThrowNew(env, WSAGetLastError(), "socket available");
+    }
+
+    return available;
+}
+
+/*
+ * Class:     java_net_DualStackPlainSocketImpl
+ * Method:    close0
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL Java_java_net_DualStackPlainSocketImpl_close0
+  (JNIEnv *env, jclass clazz, jint fd) {
+     NET_SocketClose(fd);
+}
+
+/*
+ * Class:     java_net_DualStackPlainSocketImpl
+ * Method:    shutdown0
+ * Signature: (II)V
+ */
+JNIEXPORT void JNICALL Java_java_net_DualStackPlainSocketImpl_shutdown0
+  (JNIEnv *env, jclass clazz, jint fd, jint howto) {
+    shutdown(fd, howto);
+}
+
+
+/*
+ * Class:     java_net_DualStackPlainSocketImpl
+ * Method:    setIntOption
+ * Signature: (III)V
+ */
+JNIEXPORT void JNICALL Java_java_net_DualStackPlainSocketImpl_setIntOption
+  (JNIEnv *env, jclass clazz, jint fd, jint cmd, jint value) {
+
+    int level = 0, opt = 0;
+    struct linger linger = {0, 0};
+    char *parg;
+    int arglen;
+
+    if (NET_MapSocketOption(cmd, &level, &opt) < 0) {
+        JNU_ThrowByNameWithLastError(env,
+                                     JNU_JAVANETPKG "SocketException",
+                                     "Invalid option");
+        return;
+    }
+
+    if (opt == java_net_SocketOptions_SO_LINGER) {
+        parg = (char *)&linger;
+        arglen = sizeof(linger);
+        if (value >= 0) {
+            linger.l_onoff = 1;
+            linger.l_linger = (unsigned short)value;
+        } else {
+            linger.l_onoff = 0;
+            linger.l_linger = 0;
+        }
+    } else {
+        parg = (char *)&value;
+        arglen = sizeof(value);
+    }
+
+    if (NET_SetSockOpt(fd, level, opt, parg, arglen) < 0) {
+        NET_ThrowNew(env, WSAGetLastError(), "setsockopt");
+    }
+}
+
+/*
+ * Class:     java_net_DualStackPlainSocketImpl
+ * Method:    getIntOption
+ * Signature: (II)I
+ */
+JNIEXPORT jint JNICALL Java_java_net_DualStackPlainSocketImpl_getIntOption
+  (JNIEnv *env, jclass clazz, jint fd, jint cmd) {
+
+    int level = 0, opt = 0;
+    int result=0;
+    struct linger linger = {0, 0};
+    char *arg;
+    int arglen;
+
+    if (NET_MapSocketOption(cmd, &level, &opt) < 0) {
+        JNU_ThrowByNameWithLastError(env,
+                                     JNU_JAVANETPKG "SocketException",
+                                     "Unsupported socket option");
+        return -1;
+    }
+
+    if (opt == java_net_SocketOptions_SO_LINGER) {
+        arg = (char *)&linger;
+        arglen = sizeof(linger);
+    } else {
+        arg = (char *)&result;
+        arglen = sizeof(result);
+    }
+
+    if (NET_GetSockOpt(fd, level, opt, arg, &arglen) < 0) {
+        NET_ThrowNew(env, WSAGetLastError(), "getsockopt");
+        return -1;
+    }
+
+    if (opt == java_net_SocketOptions_SO_LINGER)
+        return linger.l_onoff ? linger.l_linger : -1;
+    else
+        return result;
+}
+
+
+/*
+ * Class:     java_net_DualStackPlainSocketImpl
+ * Method:    sendOOB
+ * Signature: (II)V
+ */
+JNIEXPORT void JNICALL Java_java_net_DualStackPlainSocketImpl_sendOOB
+  (JNIEnv *env, jclass clazz, jint fd, jint data) {
+    jint n;
+    unsigned char d = (unsigned char) data & 0xff;
+
+    n = send(fd, (char *)&data, 1, MSG_OOB);
+    if (n == SOCKET_ERROR) {
+        NET_ThrowNew(env, WSAGetLastError(), "send");
+    }
+}
+
+/*
+ * Class:     java_net_DualStackPlainSocketImpl
+ * Method:    configureBlocking
+ * Signature: (IZ)V
+ */
+JNIEXPORT void JNICALL Java_java_net_DualStackPlainSocketImpl_configureBlocking
+  (JNIEnv *env, jclass clazz, jint fd, jboolean blocking) {
+    u_long arg;
+    int result;
+
+    if (blocking == JNI_TRUE) {
+        arg = SET_BLOCKING;    // 0
+    } else {
+        arg = SET_NONBLOCKING;   // 1
+    }
+
+    result = ioctlsocket(fd, FIONBIO, &arg);
+    if (result == SOCKET_ERROR) {
+        NET_ThrowNew(env, WSAGetLastError(), "configureBlocking");
+    }
+}