Path: blob/master/src/java.base/windows/native/libnet/PlainSocketImpl.c
67723 views
/*1* Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/24#include "net_util.h"2526#include "java_net_PlainSocketImpl.h"27#include "java_net_SocketOptions.h"2829#define SET_BLOCKING 030#define SET_NONBLOCKING 13132static jclass isa_class; /* java.net.InetSocketAddress */33static jmethodID isa_ctorID; /* InetSocketAddress(InetAddress, int) */3435/*36* Class: java_net_PlainSocketImpl37* Method: initIDs38* Signature: ()V39*/40JNIEXPORT void JNICALL Java_java_net_PlainSocketImpl_initIDs41(JNIEnv *env, jclass clazz) {4243jclass cls = (*env)->FindClass(env, "java/net/InetSocketAddress");44CHECK_NULL(cls);45isa_class = (*env)->NewGlobalRef(env, cls);46CHECK_NULL(isa_class);47isa_ctorID = (*env)->GetMethodID(env, cls, "<init>",48"(Ljava/net/InetAddress;I)V");49CHECK_NULL(isa_ctorID);50initInetAddressIDs(env);5152// implement read timeout with select.53isRcvTimeoutSupported = JNI_FALSE;54}5556/*57* Class: java_net_PlainSocketImpl58* Method: socket059* Signature: (ZZ)I60*/61JNIEXPORT jint JNICALL Java_java_net_PlainSocketImpl_socket062(JNIEnv *env, jclass clazz, jboolean stream) {63int fd, rv, opt = 0;64int type = (stream ? SOCK_STREAM : SOCK_DGRAM);65int domain = ipv6_available() ? AF_INET6 : AF_INET;6667fd = NET_Socket(domain, type, 0);6869if (fd == INVALID_SOCKET) {70NET_ThrowNew(env, WSAGetLastError(), "create");71return -1;72}7374if (domain == AF_INET6) {75rv = setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &opt,76sizeof(opt));77if (rv == SOCKET_ERROR) {78NET_ThrowNew(env, WSAGetLastError(), "create");79closesocket(fd);80return -1;81}82}8384return fd;85}8687/*88* Class: java_net_PlainSocketImpl89* Method: bind090* Signature: (ILjava/net/InetAddress;I)V91*/92JNIEXPORT void JNICALL Java_java_net_PlainSocketImpl_bind093(JNIEnv *env, jclass clazz, jint fd, jobject iaObj, jint port,94jboolean exclBind)95{96SOCKETADDRESS sa;97int rv, sa_len = 0;98jboolean v4MappedAddress = ipv6_available() ? JNI_TRUE : JNI_FALSE;99100if (NET_InetAddressToSockaddr(env, iaObj, port, &sa,101&sa_len, v4MappedAddress) != 0) {102return;103}104105rv = NET_WinBind(fd, &sa, sa_len, exclBind);106107if (rv == SOCKET_ERROR)108NET_ThrowNew(env, WSAGetLastError(), "NET_Bind");109}110111/*112* Class: java_net_PlainSocketImpl113* Method: connect0114* Signature: (ILjava/net/InetAddress;I)I115*/116JNIEXPORT jint JNICALL Java_java_net_PlainSocketImpl_connect0117(JNIEnv *env, jclass clazz, jint fd, jobject iaObj, jint port) {118SOCKETADDRESS sa;119int rv, sa_len = 0;120int so_rv;121SOCKET s = (SOCKET)fd;122int type = 0, optlen = sizeof(type);123jboolean v4MappedAddress = ipv6_available() ? JNI_TRUE : JNI_FALSE;124125if (NET_InetAddressToSockaddr(env, iaObj, port, &sa,126&sa_len, v4MappedAddress) != 0) {127return -1;128}129130so_rv = getsockopt(s, SOL_SOCKET, SO_TYPE, (char*)&type, &optlen);131132/**133* Windows has a very long socket connect timeout of 2 seconds.134* If it's the loopback adapter we can shorten the wait interval.135*/136if (so_rv == 0 && type == SOCK_STREAM && IS_LOOPBACK_ADDRESS(&sa)) {137NET_EnableFastTcpLoopbackConnect(fd);138}139140rv = connect(fd, &sa.sa, sa_len);141if (rv == SOCKET_ERROR) {142int err = WSAGetLastError();143if (err == WSAEWOULDBLOCK) {144return java_net_PlainSocketImpl_WOULDBLOCK;145} else if (err == WSAEADDRNOTAVAIL) {146JNU_ThrowByName(env, JNU_JAVANETPKG "ConnectException",147"connect: Address is invalid on local machine,"148" or port is not valid on remote machine");149} else {150NET_ThrowNew(env, err, "connect");151}152// return value not important.153}154return rv;155}156157/*158* Class: java_net_PlainSocketImpl159* Method: waitForConnect160* Signature: (II)V161*/162JNIEXPORT void JNICALL Java_java_net_PlainSocketImpl_waitForConnect163(JNIEnv *env, jclass clazz, jint fd, jint timeout) {164int rv, retry;165int optlen = sizeof(rv);166fd_set wr, ex;167struct timeval t;168169FD_ZERO(&wr);170FD_ZERO(&ex);171FD_SET(fd, &wr);172FD_SET(fd, &ex);173t.tv_sec = timeout / 1000;174t.tv_usec = (timeout % 1000) * 1000;175176/*177* Wait for timeout, connection established or178* connection failed.179*/180rv = select(fd+1, 0, &wr, &ex, &t);181182/*183* Timeout before connection is established/failed so184* we throw exception and shutdown input/output to prevent185* socket from being used.186* The socket should be closed immediately by the caller.187*/188if (rv == 0) {189JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException",190"connect timed out");191shutdown(fd, SD_BOTH);192return;193}194195/*196* Socket is writable or error occurred. On some Windows editions197* the socket will appear writable when the connect fails so we198* check for error rather than writable.199*/200if (!FD_ISSET(fd, &ex)) {201return; /* connection established */202}203204/*205* Connection failed. The logic here is designed to work around206* bug on Windows NT whereby using getsockopt to obtain the207* last error (SO_ERROR) indicates there is no error. The workaround208* on NT is to allow winsock to be scheduled and this is done by209* yielding and retrying. As yielding is problematic in heavy210* load conditions we attempt up to 3 times to get the error reason.211*/212for (retry = 0; retry < 3; retry++) {213NET_GetSockOpt(fd, SOL_SOCKET, SO_ERROR,214(char*)&rv, &optlen);215if (rv) {216break;217}218Sleep(0);219}220221if (rv == 0) {222JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",223"Unable to establish connection");224} else if (!ipv6_available() && rv == WSAEADDRNOTAVAIL) {225JNU_ThrowByName(env, JNU_JAVANETPKG "ConnectException",226"connect: Address is invalid on local machine,"227" or port is not valid on remote machine");228} else {229NET_ThrowNew(env, rv, "connect");230}231}232233/*234* Class: java_net_PlainSocketImpl235* Method: localPort0236* Signature: (I)I237*/238JNIEXPORT jint JNICALL Java_java_net_PlainSocketImpl_localPort0239(JNIEnv *env, jclass clazz, jint fd) {240SOCKETADDRESS sa;241int len = sizeof(sa);242243if (getsockname(fd, &sa.sa, &len) == SOCKET_ERROR) {244if (WSAGetLastError() == WSAENOTSOCK) {245JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",246"Socket closed");247} else {248NET_ThrowNew(env, WSAGetLastError(), "getsockname failed");249}250return -1;251}252return (int) ntohs((u_short)GET_PORT(&sa));253}254255/*256* Class: java_net_PlainSocketImpl257* Method: localAddress258* Signature: (ILjava/net/InetAddressContainer;)V259*/260JNIEXPORT void JNICALL Java_java_net_PlainSocketImpl_localAddress261(JNIEnv *env, jclass clazz, jint fd, jobject iaContainerObj) {262int port;263SOCKETADDRESS sa;264int len = sizeof(sa);265jobject iaObj;266jclass iaContainerClass;267jfieldID iaFieldID;268269if (getsockname(fd, &sa.sa, &len) == SOCKET_ERROR) {270NET_ThrowNew(env, WSAGetLastError(), "Error getting socket name");271return;272}273iaObj = NET_SockaddrToInetAddress(env, &sa, &port);274CHECK_NULL(iaObj);275276iaContainerClass = (*env)->GetObjectClass(env, iaContainerObj);277iaFieldID = (*env)->GetFieldID(env, iaContainerClass, "addr", "Ljava/net/InetAddress;");278CHECK_NULL(iaFieldID);279(*env)->SetObjectField(env, iaContainerObj, iaFieldID, iaObj);280}281282/*283* Class: java_net_PlainSocketImpl284* Method: listen0285* Signature: (II)V286*/287JNIEXPORT void JNICALL Java_java_net_PlainSocketImpl_listen0288(JNIEnv *env, jclass clazz, jint fd, jint backlog) {289if (listen(fd, backlog) == SOCKET_ERROR) {290NET_ThrowNew(env, WSAGetLastError(), "listen failed");291}292}293294/*295* Class: java_net_PlainSocketImpl296* Method: accept0297* Signature: (I[Ljava/net/InetSocketAddress;)I298*/299JNIEXPORT jint JNICALL Java_java_net_PlainSocketImpl_accept0300(JNIEnv *env, jclass clazz, jint fd, jobjectArray isaa) {301int newfd, port = 0;302jobject isa;303jobject ia;304SOCKETADDRESS sa;305int len = sizeof(sa);306307memset((char *)&sa, 0, len);308newfd = accept(fd, &sa.sa, &len);309310if (newfd == INVALID_SOCKET) {311NET_ThrowNew(env, WSAGetLastError(), "accept failed");312return -1;313}314315SetHandleInformation((HANDLE)(UINT_PTR)newfd, HANDLE_FLAG_INHERIT, 0);316317ia = NET_SockaddrToInetAddress(env, &sa, &port);318if (ia == NULL){319closesocket(newfd);320return -1;321}322isa = (*env)->NewObject(env, isa_class, isa_ctorID, ia, port);323if (isa == NULL) {324closesocket(newfd);325return -1;326}327(*env)->SetObjectArrayElement(env, isaa, 0, isa);328329return newfd;330}331332/*333* Class: java_net_PlainSocketImpl334* Method: waitForNewConnection335* Signature: (II)V336*/337JNIEXPORT void JNICALL Java_java_net_PlainSocketImpl_waitForNewConnection338(JNIEnv *env, jclass clazz, jint fd, jint timeout) {339int rv;340341rv = NET_Timeout(fd, timeout);342if (rv == 0) {343JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException",344"Accept timed out");345} else if (rv == -1) {346JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "socket closed");347} else if (rv == -2) {348JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException",349"operation interrupted");350}351}352353/*354* Class: java_net_PlainSocketImpl355* Method: available0356* Signature: (I)I357*/358JNIEXPORT jint JNICALL Java_java_net_PlainSocketImpl_available0359(JNIEnv *env, jclass clazz, jint fd) {360jint available = -1;361362if ((ioctlsocket(fd, FIONREAD, &available)) == SOCKET_ERROR) {363NET_ThrowNew(env, WSAGetLastError(), "socket available");364}365366return available;367}368369/*370* Class: java_net_PlainSocketImpl371* Method: close0372* Signature: (I)V373*/374JNIEXPORT void JNICALL Java_java_net_PlainSocketImpl_close0375(JNIEnv *env, jclass clazz, jint fd) {376NET_SocketClose(fd);377}378379/*380* Class: java_net_PlainSocketImpl381* Method: shutdown0382* Signature: (II)V383*/384JNIEXPORT void JNICALL Java_java_net_PlainSocketImpl_shutdown0385(JNIEnv *env, jclass clazz, jint fd, jint howto) {386shutdown(fd, howto);387}388389/*390* Class: java_net_PlainSocketImpl391* Method: setIntOption392* Signature: (III)V393*/394JNIEXPORT void JNICALL395Java_java_net_PlainSocketImpl_setIntOption396(JNIEnv *env, jclass clazz, jint fd, jint cmd, jint value)397{398int level = 0, opt = 0;399struct linger linger = {0, 0};400char *parg;401int arglen;402403if (NET_MapSocketOption(cmd, &level, &opt) < 0) {404JNU_ThrowByName(env, "java/net/SocketException", "Invalid option");405return;406}407408if (opt == java_net_SocketOptions_SO_LINGER) {409parg = (char *)&linger;410arglen = sizeof(linger);411if (value >= 0) {412linger.l_onoff = 1;413linger.l_linger = (unsigned short)value;414} else {415linger.l_onoff = 0;416linger.l_linger = 0;417}418} else {419parg = (char *)&value;420arglen = sizeof(value);421}422423if (NET_SetSockOpt(fd, level, opt, parg, arglen) < 0) {424NET_ThrowNew(env, WSAGetLastError(), "setsockopt");425}426}427428/*429* Class: java_net_PlainSocketImpl430* Method: setSoTimeout0431* Signature: (II)V432*/433JNIEXPORT void JNICALL434Java_java_net_PlainSocketImpl_setSoTimeout0435(JNIEnv *env, jclass clazz, jint fd, jint timeout)436{437/*438* SO_TIMEOUT is the socket option used to specify the timeout439* for ServerSocket.accept and Socket.getInputStream().read.440* It does not typically map to a native level socket option.441* For Windows we special-case this and use the SOL_SOCKET/SO_RCVTIMEO442* socket option to specify a receive timeout on the socket. This443* receive timeout is applicable to Socket only and the socket444* option should not be set on ServerSocket.445*/446447/*448* SO_RCVTIMEO is only supported on Microsoft's implementation449* of Windows Sockets so if WSAENOPROTOOPT returned then450* reset flag and timeout will be implemented using451* select() -- see SocketInputStream.socketRead.452*/453if (isRcvTimeoutSupported) {454/*455* Disable SO_RCVTIMEO if timeout is <= 5 second.456*/457if (timeout <= 5000) {458timeout = 0;459}460461if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout,462sizeof(timeout)) < 0) {463int err = WSAGetLastError();464if (err == WSAENOPROTOOPT) {465isRcvTimeoutSupported = JNI_FALSE;466} else {467NET_ThrowNew(env, err, "setsockopt SO_RCVTIMEO");468}469}470}471}472473/*474* Class: java_net_PlainSocketImpl475* Method: getIntOption476* Signature: (II)I477*/478JNIEXPORT jint JNICALL Java_java_net_PlainSocketImpl_getIntOption479(JNIEnv *env, jclass clazz, jint fd, jint cmd)480{481int level = 0, opt = 0;482int result = 0;483struct linger linger = {0, 0};484char *arg;485int arglen;486487if (NET_MapSocketOption(cmd, &level, &opt) < 0) {488JNU_ThrowByName(env, "java/net/SocketException", "Invalid option");489return -1;490}491492if (opt == java_net_SocketOptions_SO_LINGER) {493arg = (char *)&linger;494arglen = sizeof(linger);495} else {496arg = (char *)&result;497arglen = sizeof(result);498}499500if (NET_GetSockOpt(fd, level, opt, arg, &arglen) < 0) {501NET_ThrowNew(env, WSAGetLastError(), "getsockopt");502return -1;503}504505if (opt == java_net_SocketOptions_SO_LINGER)506return linger.l_onoff ? linger.l_linger : -1;507else508return result;509}510511/*512* Class: java_net_PlainSocketImpl513* Method: sendOOB514* Signature: (II)V515*/516JNIEXPORT void JNICALL Java_java_net_PlainSocketImpl_sendOOB517(JNIEnv *env, jclass clazz, jint fd, jint data) {518jint n;519unsigned char d = (unsigned char) data & 0xff;520521n = send(fd, (char *)&data, 1, MSG_OOB);522if (n == SOCKET_ERROR) {523NET_ThrowNew(env, WSAGetLastError(), "send");524}525}526527/*528* Class: java_net_PlainSocketImpl529* Method: configureBlocking530* Signature: (IZ)V531*/532JNIEXPORT void JNICALL Java_java_net_PlainSocketImpl_configureBlocking533(JNIEnv *env, jclass clazz, jint fd, jboolean blocking) {534u_long arg;535int result;536537if (blocking == JNI_TRUE) {538arg = SET_BLOCKING; // 0539} else {540arg = SET_NONBLOCKING; // 1541}542543result = ioctlsocket(fd, FIONBIO, &arg);544if (result == SOCKET_ERROR) {545NET_ThrowNew(env, WSAGetLastError(), "configureBlocking");546}547}548549550