0N/A/*
2362N/A * Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A#include <windows.h>
0N/A#include <winsock2.h>
0N/A#include "jni.h"
0N/A#include "net_util.h"
0N/A#include "java_net_DualStackPlainSocketImpl.h"
0N/A
0N/A#define SET_BLOCKING 0
0N/A#define SET_NONBLOCKING 1
0N/A
0N/Astatic jclass isa_class; /* java.net.InetSocketAddress */
0N/Astatic jmethodID isa_ctorID; /* InetSocketAddress(InetAddress, int) */
0N/A
0N/A/*
0N/A * Class: java_net_DualStackPlainSocketImpl
0N/A * Method: initIDs
0N/A * Signature: ()V
0N/A */
0N/AJNIEXPORT void JNICALL Java_java_net_DualStackPlainSocketImpl_initIDs
0N/A (JNIEnv *env, jclass clazz) {
0N/A
0N/A jclass cls = (*env)->FindClass(env, "java/net/InetSocketAddress");
0N/A isa_class = (*env)->NewGlobalRef(env, cls);
0N/A isa_ctorID = (*env)->GetMethodID(env, cls, "<init>",
0N/A "(Ljava/net/InetAddress;I)V");
0N/A
0N/A // implement read timeout with select.
0N/A isRcvTimeoutSupported = 0;
0N/A}
0N/A
0N/A/*
0N/A * Class: java_net_DualStackPlainSocketImpl
0N/A * Method: socket0
0N/A * Signature: (ZZ)I
0N/A */
0N/AJNIEXPORT jint JNICALL Java_java_net_DualStackPlainSocketImpl_socket0
0N/A (JNIEnv *env, jclass clazz, jboolean stream, jboolean v6Only /*unused*/) {
0N/A int fd, rv, opt=0;
0N/A
0N/A fd = NET_Socket(AF_INET6, (stream ? SOCK_STREAM : SOCK_DGRAM), 0);
0N/A if (fd == INVALID_SOCKET) {
0N/A NET_ThrowNew(env, WSAGetLastError(), "create");
0N/A return -1;
0N/A }
0N/A
0N/A rv = setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &opt, sizeof(opt));
0N/A if (rv == SOCKET_ERROR) {
0N/A NET_ThrowNew(env, WSAGetLastError(), "create");
0N/A }
0N/A
0N/A SetHandleInformation((HANDLE)(UINT_PTR)fd, HANDLE_FLAG_INHERIT, FALSE);
0N/A
0N/A return fd;
0N/A}
0N/A
0N/A/*
0N/A * Class: java_net_DualStackPlainSocketImpl
0N/A * Method: bind0
0N/A * Signature: (ILjava/net/InetAddress;I)V
0N/A */
0N/AJNIEXPORT void JNICALL Java_java_net_DualStackPlainSocketImpl_bind0
6272N/A (JNIEnv *env, jclass clazz, jint fd, jobject iaObj, jint port,
6272N/A jboolean exclBind)
6272N/A{
0N/A SOCKETADDRESS sa;
0N/A int rv;
0N/A int sa_len = sizeof(sa);
0N/A
0N/A if (NET_InetAddressToSockaddr(env, iaObj, port, (struct sockaddr *)&sa,
0N/A &sa_len, JNI_TRUE) != 0) {
0N/A return;
0N/A }
0N/A
6272N/A rv = NET_WinBind(fd, (struct sockaddr *)&sa, sa_len, exclBind);
0N/A
0N/A if (rv == SOCKET_ERROR)
0N/A NET_ThrowNew(env, WSAGetLastError(), "JVM_Bind");
0N/A}
0N/A
0N/A/*
0N/A * Class: java_net_DualStackPlainSocketImpl
0N/A * Method: connect0
0N/A * Signature: (ILjava/net/InetAddress;I)I
0N/A */
0N/AJNIEXPORT jint JNICALL Java_java_net_DualStackPlainSocketImpl_connect0
0N/A (JNIEnv *env, jclass clazz, jint fd, jobject iaObj, jint port) {
0N/A SOCKETADDRESS sa;
0N/A int rv;
0N/A int sa_len = sizeof(sa);
0N/A
0N/A if (NET_InetAddressToSockaddr(env, iaObj, port, (struct sockaddr *)&sa,
0N/A &sa_len, JNI_TRUE) != 0) {
0N/A return -1;
0N/A }
0N/A
0N/A rv = connect(fd, (struct sockaddr *)&sa, sa_len);
0N/A if (rv == SOCKET_ERROR) {
0N/A int err = WSAGetLastError();
0N/A if (err == WSAEWOULDBLOCK) {
0N/A return java_net_DualStackPlainSocketImpl_WOULDBLOCK;
0N/A } else if (err == WSAEADDRNOTAVAIL) {
0N/A JNU_ThrowByName(env, JNU_JAVANETPKG "ConnectException",
0N/A "connect: Address is invalid on local machine, or port is not valid on remote machine");
0N/A } else {
0N/A NET_ThrowNew(env, err, "connect");
0N/A }
0N/A return -1; // return value not important.
0N/A }
0N/A return rv;
0N/A}
0N/A
0N/A/*
0N/A * Class: java_net_DualStackPlainSocketImpl
0N/A * Method: waitForConnect
0N/A * Signature: (II)V
0N/A */
0N/AJNIEXPORT void JNICALL Java_java_net_DualStackPlainSocketImpl_waitForConnect
0N/A (JNIEnv *env, jclass clazz, jint fd, jint timeout) {
0N/A int rv, retry;
0N/A int optlen = sizeof(rv);
0N/A fd_set wr, ex;
0N/A struct timeval t;
0N/A
0N/A FD_ZERO(&wr);
0N/A FD_ZERO(&ex);
0N/A FD_SET(fd, &wr);
0N/A FD_SET(fd, &ex);
0N/A t.tv_sec = timeout / 1000;
0N/A t.tv_usec = (timeout % 1000) * 1000;
0N/A
0N/A /*
0N/A * Wait for timeout, connection established or
0N/A * connection failed.
0N/A */
0N/A rv = select(fd+1, 0, &wr, &ex, &t);
0N/A
0N/A /*
0N/A * Timeout before connection is established/failed so
0N/A * we throw exception and shutdown input/output to prevent
0N/A * socket from being used.
0N/A * The socket should be closed immediately by the caller.
0N/A */
0N/A if (rv == 0) {
0N/A JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException",
0N/A "connect timed out");
0N/A shutdown( fd, SD_BOTH );
0N/A return;
0N/A }
0N/A
0N/A /*
0N/A * Socket is writable or error occured. On some Windows editions
0N/A * the socket will appear writable when the connect fails so we
0N/A * check for error rather than writable.
0N/A */
0N/A if (!FD_ISSET(fd, &ex)) {
0N/A return; /* connection established */
0N/A }
0N/A
0N/A /*
0N/A * Connection failed. The logic here is designed to work around
0N/A * bug on Windows NT whereby using getsockopt to obtain the
0N/A * last error (SO_ERROR) indicates there is no error. The workaround
0N/A * on NT is to allow winsock to be scheduled and this is done by
0N/A * yielding and retrying. As yielding is problematic in heavy
0N/A * load conditions we attempt up to 3 times to get the error reason.
0N/A */
0N/A for (retry=0; retry<3; retry++) {
0N/A NET_GetSockOpt(fd, SOL_SOCKET, SO_ERROR,
0N/A (char*)&rv, &optlen);
0N/A if (rv) {
0N/A break;
0N/A }
0N/A Sleep(0);
0N/A }
0N/A
0N/A if (rv == 0) {
0N/A JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
0N/A "Unable to establish connection");
0N/A } else {
0N/A NET_ThrowNew(env, rv, "connect");
0N/A }
0N/A}
0N/A
0N/A/*
0N/A * Class: java_net_DualStackPlainSocketImpl
0N/A * Method: localPort0
0N/A * Signature: (I)I
0N/A */
0N/AJNIEXPORT jint JNICALL Java_java_net_DualStackPlainSocketImpl_localPort0
0N/A (JNIEnv *env, jclass clazz, jint fd) {
0N/A SOCKETADDRESS sa;
0N/A int len = sizeof(sa);
0N/A
0N/A if (getsockname(fd, (struct sockaddr *)&sa, &len) == SOCKET_ERROR) {
0N/A if (WSAGetLastError() == WSAENOTSOCK) {
0N/A JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
0N/A "Socket closed");
0N/A } else {
0N/A NET_ThrowNew(env, WSAGetLastError(), "getsockname failed");
0N/A }
0N/A return -1;
0N/A }
0N/A return (int) ntohs((u_short)GET_PORT(&sa));
0N/A}
0N/A
0N/A/*
0N/A * Class: java_net_DualStackPlainSocketImpl
0N/A * Method: localAddress
0N/A * Signature: (ILjava/net/InetAddressContainer;)V
0N/A */
0N/AJNIEXPORT void JNICALL Java_java_net_DualStackPlainSocketImpl_localAddress
0N/A (JNIEnv *env, jclass clazz, jint fd, jobject iaContainerObj) {
0N/A int port;
0N/A SOCKETADDRESS sa;
0N/A int len = sizeof(sa);
0N/A jobject iaObj;
0N/A jclass iaContainerClass;
0N/A jfieldID iaFieldID;
0N/A
0N/A if (getsockname(fd, (struct sockaddr *)&sa, &len) == SOCKET_ERROR) {
0N/A NET_ThrowNew(env, WSAGetLastError(), "Error getting socket name");
0N/A return;
0N/A }
0N/A iaObj = NET_SockaddrToInetAddress(env, (struct sockaddr *)&sa, &port);
0N/A CHECK_NULL(iaObj);
0N/A
0N/A iaContainerClass = (*env)->GetObjectClass(env, iaContainerObj);
0N/A iaFieldID = (*env)->GetFieldID(env, iaContainerClass, "addr", "Ljava/net/InetAddress;");
0N/A CHECK_NULL(iaFieldID);
0N/A (*env)->SetObjectField(env, iaContainerObj, iaFieldID, iaObj);
0N/A}
0N/A
0N/A
0N/A/*
0N/A * Class: java_net_DualStackPlainSocketImpl
0N/A * Method: listen0
0N/A * Signature: (II)V
0N/A */
0N/AJNIEXPORT void JNICALL Java_java_net_DualStackPlainSocketImpl_listen0
0N/A (JNIEnv *env, jclass clazz, jint fd, jint backlog) {
0N/A if (listen(fd, backlog) == SOCKET_ERROR) {
0N/A NET_ThrowNew(env, WSAGetLastError(), "listen failed");
0N/A }
0N/A}
0N/A
0N/A/*
0N/A * Class: java_net_DualStackPlainSocketImpl
0N/A * Method: accept0
0N/A * Signature: (I[Ljava/net/InetSocketAddress;)I
0N/A */
0N/AJNIEXPORT jint JNICALL Java_java_net_DualStackPlainSocketImpl_accept0
0N/A (JNIEnv *env, jclass clazz, jint fd, jobjectArray isaa) {
0N/A int newfd, port=0;
0N/A jobject isa;
0N/A jobject ia;
0N/A SOCKETADDRESS sa;
0N/A int len = sizeof(sa);
0N/A
0N/A memset((char *)&sa, 0, len);
0N/A newfd = accept(fd, (struct sockaddr *)&sa, &len);
0N/A
0N/A if (newfd == INVALID_SOCKET) {
0N/A if (WSAGetLastError() == -2) {
0N/A JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException",
0N/A "operation interrupted");
0N/A } else {
0N/A JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
0N/A "socket closed");
0N/A }
0N/A return -1;
0N/A }
0N/A
0N/A ia = NET_SockaddrToInetAddress(env, (struct sockaddr *)&sa, &port);
0N/A isa = (*env)->NewObject(env, isa_class, isa_ctorID, ia, port);
0N/A (*env)->SetObjectArrayElement(env, isaa, 0, isa);
0N/A
0N/A return newfd;
0N/A}
0N/A
0N/A/*
0N/A * Class: java_net_DualStackPlainSocketImpl
0N/A * Method: waitForNewConnection
0N/A * Signature: (II)V
0N/A */
0N/AJNIEXPORT void JNICALL Java_java_net_DualStackPlainSocketImpl_waitForNewConnection
0N/A (JNIEnv *env, jclass clazz, jint fd, jint timeout) {
0N/A int rv;
0N/A
0N/A rv = NET_Timeout(fd, timeout);
0N/A if (rv == 0) {
0N/A JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException",
0N/A "Accept timed out");
0N/A } else if (rv == -1) {
0N/A JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "socket closed");
0N/A } else if (rv == -2) {
0N/A JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException",
0N/A "operation interrupted");
0N/A }
0N/A}
0N/A
0N/A/*
0N/A * Class: java_net_DualStackPlainSocketImpl
0N/A * Method: available0
0N/A * Signature: (I)I
0N/A */
0N/AJNIEXPORT jint JNICALL Java_java_net_DualStackPlainSocketImpl_available0
0N/A (JNIEnv *env, jclass clazz, jint fd) {
0N/A jint available = -1;
0N/A
0N/A if ((ioctlsocket(fd, FIONREAD, &available)) == SOCKET_ERROR) {
0N/A NET_ThrowNew(env, WSAGetLastError(), "socket available");
0N/A }
0N/A
0N/A return available;
0N/A}
0N/A
0N/A/*
0N/A * Class: java_net_DualStackPlainSocketImpl
0N/A * Method: close0
0N/A * Signature: (I)V
0N/A */
0N/AJNIEXPORT void JNICALL Java_java_net_DualStackPlainSocketImpl_close0
0N/A (JNIEnv *env, jclass clazz, jint fd) {
0N/A NET_SocketClose(fd);
0N/A}
0N/A
0N/A/*
0N/A * Class: java_net_DualStackPlainSocketImpl
0N/A * Method: shutdown0
0N/A * Signature: (II)V
0N/A */
0N/AJNIEXPORT void JNICALL Java_java_net_DualStackPlainSocketImpl_shutdown0
0N/A (JNIEnv *env, jclass clazz, jint fd, jint howto) {
0N/A shutdown(fd, howto);
0N/A}
0N/A
0N/A
0N/A/*
0N/A * Class: java_net_DualStackPlainSocketImpl
0N/A * Method: setIntOption
0N/A * Signature: (III)V
0N/A */
0N/AJNIEXPORT void JNICALL Java_java_net_DualStackPlainSocketImpl_setIntOption
0N/A (JNIEnv *env, jclass clazz, jint fd, jint cmd, jint value) {
0N/A
0N/A int level, opt;
0N/A struct linger linger;
0N/A char *parg;
0N/A int arglen;
0N/A
0N/A if (NET_MapSocketOption(cmd, &level, &opt) < 0) {
0N/A JNU_ThrowByNameWithLastError(env,
0N/A JNU_JAVANETPKG "SocketException",
0N/A "Invalid option");
0N/A return;
0N/A }
0N/A
0N/A if (opt == java_net_SocketOptions_SO_LINGER) {
0N/A parg = (char *)&linger;
0N/A arglen = sizeof(linger);
0N/A if (value >= 0) {
0N/A linger.l_onoff = 1;
0N/A linger.l_linger = (unsigned short)value;
0N/A } else {
0N/A linger.l_onoff = 0;
0N/A linger.l_linger = 0;
0N/A }
0N/A } else {
0N/A parg = (char *)&value;
0N/A arglen = sizeof(value);
0N/A }
0N/A
0N/A if (NET_SetSockOpt(fd, level, opt, parg, arglen) < 0) {
0N/A NET_ThrowNew(env, WSAGetLastError(), "setsockopt");
0N/A }
0N/A}
0N/A
0N/A/*
0N/A * Class: java_net_DualStackPlainSocketImpl
0N/A * Method: getIntOption
0N/A * Signature: (II)I
0N/A */
0N/AJNIEXPORT jint JNICALL Java_java_net_DualStackPlainSocketImpl_getIntOption
0N/A (JNIEnv *env, jclass clazz, jint fd, jint cmd) {
0N/A
0N/A int level, opt;
0N/A int result=0;
0N/A struct linger linger;
0N/A char *arg;
0N/A int arglen;
0N/A
0N/A if (NET_MapSocketOption(cmd, &level, &opt) < 0) {
0N/A JNU_ThrowByNameWithLastError(env,
0N/A JNU_JAVANETPKG "SocketException",
0N/A "Unsupported socket option");
0N/A return -1;
0N/A }
0N/A
0N/A if (opt == java_net_SocketOptions_SO_LINGER) {
0N/A arg = (char *)&linger;
0N/A arglen = sizeof(linger);
0N/A } else {
0N/A arg = (char *)&result;
0N/A arglen = sizeof(result);
0N/A }
0N/A
0N/A if (NET_GetSockOpt(fd, level, opt, arg, &arglen) < 0) {
0N/A NET_ThrowNew(env, WSAGetLastError(), "getsockopt");
0N/A return -1;
0N/A }
0N/A
0N/A if (opt == java_net_SocketOptions_SO_LINGER)
0N/A return linger.l_onoff ? linger.l_linger : -1;
0N/A else
0N/A return result;
0N/A}
0N/A
0N/A
0N/A/*
0N/A * Class: java_net_DualStackPlainSocketImpl
0N/A * Method: sendOOB
0N/A * Signature: (II)V
0N/A */
0N/AJNIEXPORT void JNICALL Java_java_net_DualStackPlainSocketImpl_sendOOB
0N/A (JNIEnv *env, jclass clazz, jint fd, jint data) {
0N/A jint n;
0N/A unsigned char d = (unsigned char) data & 0xff;
0N/A
0N/A n = send(fd, (char *)&data, 1, MSG_OOB);
0N/A if (n == JVM_IO_ERR) {
0N/A NET_ThrowNew(env, WSAGetLastError(), "send");
0N/A } else if (n == JVM_IO_INTR) {
0N/A JNU_ThrowByName(env, "java/io/InterruptedIOException", 0);
0N/A }
0N/A}
0N/A
0N/A/*
0N/A * Class: java_net_DualStackPlainSocketImpl
0N/A * Method: configureBlocking
0N/A * Signature: (IZ)V
0N/A */
0N/AJNIEXPORT void JNICALL Java_java_net_DualStackPlainSocketImpl_configureBlocking
0N/A (JNIEnv *env, jclass clazz, jint fd, jboolean blocking) {
0N/A u_long arg;
0N/A int result;
0N/A
0N/A if (blocking == JNI_TRUE) {
0N/A arg = SET_BLOCKING; // 0
0N/A } else {
0N/A arg = SET_NONBLOCKING; // 1
0N/A }
0N/A
0N/A result = ioctlsocket(fd, FIONBIO, &arg);
0N/A if (result == SOCKET_ERROR) {
0N/A NET_ThrowNew(env, WSAGetLastError(), "configureBlocking");
0N/A }
0N/A}