Path: blob/master/src/java.base/windows/native/libnet/PlainSocketImpl.c
41119 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;120jboolean v4MappedAddress = ipv6_available() ? JNI_TRUE : JNI_FALSE;121122if (NET_InetAddressToSockaddr(env, iaObj, port, &sa,123&sa_len, v4MappedAddress) != 0) {124return -1;125}126127rv = connect(fd, &sa.sa, sa_len);128if (rv == SOCKET_ERROR) {129int err = WSAGetLastError();130if (err == WSAEWOULDBLOCK) {131return java_net_PlainSocketImpl_WOULDBLOCK;132} else if (err == WSAEADDRNOTAVAIL) {133JNU_ThrowByName(env, JNU_JAVANETPKG "ConnectException",134"connect: Address is invalid on local machine,"135" or port is not valid on remote machine");136} else {137NET_ThrowNew(env, err, "connect");138}139// return value not important.140}141return rv;142}143144/*145* Class: java_net_PlainSocketImpl146* Method: waitForConnect147* Signature: (II)V148*/149JNIEXPORT void JNICALL Java_java_net_PlainSocketImpl_waitForConnect150(JNIEnv *env, jclass clazz, jint fd, jint timeout) {151int rv, retry;152int optlen = sizeof(rv);153fd_set wr, ex;154struct timeval t;155156FD_ZERO(&wr);157FD_ZERO(&ex);158FD_SET(fd, &wr);159FD_SET(fd, &ex);160t.tv_sec = timeout / 1000;161t.tv_usec = (timeout % 1000) * 1000;162163/*164* Wait for timeout, connection established or165* connection failed.166*/167rv = select(fd+1, 0, &wr, &ex, &t);168169/*170* Timeout before connection is established/failed so171* we throw exception and shutdown input/output to prevent172* socket from being used.173* The socket should be closed immediately by the caller.174*/175if (rv == 0) {176JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException",177"connect timed out");178shutdown(fd, SD_BOTH);179return;180}181182/*183* Socket is writable or error occurred. On some Windows editions184* the socket will appear writable when the connect fails so we185* check for error rather than writable.186*/187if (!FD_ISSET(fd, &ex)) {188return; /* connection established */189}190191/*192* Connection failed. The logic here is designed to work around193* bug on Windows NT whereby using getsockopt to obtain the194* last error (SO_ERROR) indicates there is no error. The workaround195* on NT is to allow winsock to be scheduled and this is done by196* yielding and retrying. As yielding is problematic in heavy197* load conditions we attempt up to 3 times to get the error reason.198*/199for (retry = 0; retry < 3; retry++) {200NET_GetSockOpt(fd, SOL_SOCKET, SO_ERROR,201(char*)&rv, &optlen);202if (rv) {203break;204}205Sleep(0);206}207208if (rv == 0) {209JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",210"Unable to establish connection");211} else if (!ipv6_available() && rv == WSAEADDRNOTAVAIL) {212JNU_ThrowByName(env, JNU_JAVANETPKG "ConnectException",213"connect: Address is invalid on local machine,"214" or port is not valid on remote machine");215} else {216NET_ThrowNew(env, rv, "connect");217}218}219220/*221* Class: java_net_PlainSocketImpl222* Method: localPort0223* Signature: (I)I224*/225JNIEXPORT jint JNICALL Java_java_net_PlainSocketImpl_localPort0226(JNIEnv *env, jclass clazz, jint fd) {227SOCKETADDRESS sa;228int len = sizeof(sa);229230if (getsockname(fd, &sa.sa, &len) == SOCKET_ERROR) {231if (WSAGetLastError() == WSAENOTSOCK) {232JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",233"Socket closed");234} else {235NET_ThrowNew(env, WSAGetLastError(), "getsockname failed");236}237return -1;238}239return (int) ntohs((u_short)GET_PORT(&sa));240}241242/*243* Class: java_net_PlainSocketImpl244* Method: localAddress245* Signature: (ILjava/net/InetAddressContainer;)V246*/247JNIEXPORT void JNICALL Java_java_net_PlainSocketImpl_localAddress248(JNIEnv *env, jclass clazz, jint fd, jobject iaContainerObj) {249int port;250SOCKETADDRESS sa;251int len = sizeof(sa);252jobject iaObj;253jclass iaContainerClass;254jfieldID iaFieldID;255256if (getsockname(fd, &sa.sa, &len) == SOCKET_ERROR) {257NET_ThrowNew(env, WSAGetLastError(), "Error getting socket name");258return;259}260iaObj = NET_SockaddrToInetAddress(env, &sa, &port);261CHECK_NULL(iaObj);262263iaContainerClass = (*env)->GetObjectClass(env, iaContainerObj);264iaFieldID = (*env)->GetFieldID(env, iaContainerClass, "addr", "Ljava/net/InetAddress;");265CHECK_NULL(iaFieldID);266(*env)->SetObjectField(env, iaContainerObj, iaFieldID, iaObj);267}268269/*270* Class: java_net_PlainSocketImpl271* Method: listen0272* Signature: (II)V273*/274JNIEXPORT void JNICALL Java_java_net_PlainSocketImpl_listen0275(JNIEnv *env, jclass clazz, jint fd, jint backlog) {276if (listen(fd, backlog) == SOCKET_ERROR) {277NET_ThrowNew(env, WSAGetLastError(), "listen failed");278}279}280281/*282* Class: java_net_PlainSocketImpl283* Method: accept0284* Signature: (I[Ljava/net/InetSocketAddress;)I285*/286JNIEXPORT jint JNICALL Java_java_net_PlainSocketImpl_accept0287(JNIEnv *env, jclass clazz, jint fd, jobjectArray isaa) {288int newfd, port = 0;289jobject isa;290jobject ia;291SOCKETADDRESS sa;292int len = sizeof(sa);293294memset((char *)&sa, 0, len);295newfd = accept(fd, &sa.sa, &len);296297if (newfd == INVALID_SOCKET) {298NET_ThrowNew(env, WSAGetLastError(), "accept failed");299return -1;300}301302SetHandleInformation((HANDLE)(UINT_PTR)newfd, HANDLE_FLAG_INHERIT, 0);303304ia = NET_SockaddrToInetAddress(env, &sa, &port);305if (ia == NULL){306closesocket(newfd);307return -1;308}309isa = (*env)->NewObject(env, isa_class, isa_ctorID, ia, port);310if (isa == NULL) {311closesocket(newfd);312return -1;313}314(*env)->SetObjectArrayElement(env, isaa, 0, isa);315316return newfd;317}318319/*320* Class: java_net_PlainSocketImpl321* Method: waitForNewConnection322* Signature: (II)V323*/324JNIEXPORT void JNICALL Java_java_net_PlainSocketImpl_waitForNewConnection325(JNIEnv *env, jclass clazz, jint fd, jint timeout) {326int rv;327328rv = NET_Timeout(fd, timeout);329if (rv == 0) {330JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException",331"Accept timed out");332} else if (rv == -1) {333JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "socket closed");334} else if (rv == -2) {335JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException",336"operation interrupted");337}338}339340/*341* Class: java_net_PlainSocketImpl342* Method: available0343* Signature: (I)I344*/345JNIEXPORT jint JNICALL Java_java_net_PlainSocketImpl_available0346(JNIEnv *env, jclass clazz, jint fd) {347jint available = -1;348349if ((ioctlsocket(fd, FIONREAD, &available)) == SOCKET_ERROR) {350NET_ThrowNew(env, WSAGetLastError(), "socket available");351}352353return available;354}355356/*357* Class: java_net_PlainSocketImpl358* Method: close0359* Signature: (I)V360*/361JNIEXPORT void JNICALL Java_java_net_PlainSocketImpl_close0362(JNIEnv *env, jclass clazz, jint fd) {363NET_SocketClose(fd);364}365366/*367* Class: java_net_PlainSocketImpl368* Method: shutdown0369* Signature: (II)V370*/371JNIEXPORT void JNICALL Java_java_net_PlainSocketImpl_shutdown0372(JNIEnv *env, jclass clazz, jint fd, jint howto) {373shutdown(fd, howto);374}375376/*377* Class: java_net_PlainSocketImpl378* Method: setIntOption379* Signature: (III)V380*/381JNIEXPORT void JNICALL382Java_java_net_PlainSocketImpl_setIntOption383(JNIEnv *env, jclass clazz, jint fd, jint cmd, jint value)384{385int level = 0, opt = 0;386struct linger linger = {0, 0};387char *parg;388int arglen;389390if (NET_MapSocketOption(cmd, &level, &opt) < 0) {391JNU_ThrowByName(env, "java/net/SocketException", "Invalid option");392return;393}394395if (opt == java_net_SocketOptions_SO_LINGER) {396parg = (char *)&linger;397arglen = sizeof(linger);398if (value >= 0) {399linger.l_onoff = 1;400linger.l_linger = (unsigned short)value;401} else {402linger.l_onoff = 0;403linger.l_linger = 0;404}405} else {406parg = (char *)&value;407arglen = sizeof(value);408}409410if (NET_SetSockOpt(fd, level, opt, parg, arglen) < 0) {411NET_ThrowNew(env, WSAGetLastError(), "setsockopt");412}413}414415/*416* Class: java_net_PlainSocketImpl417* Method: setSoTimeout0418* Signature: (II)V419*/420JNIEXPORT void JNICALL421Java_java_net_PlainSocketImpl_setSoTimeout0422(JNIEnv *env, jclass clazz, jint fd, jint timeout)423{424/*425* SO_TIMEOUT is the socket option used to specify the timeout426* for ServerSocket.accept and Socket.getInputStream().read.427* It does not typically map to a native level socket option.428* For Windows we special-case this and use the SOL_SOCKET/SO_RCVTIMEO429* socket option to specify a receive timeout on the socket. This430* receive timeout is applicable to Socket only and the socket431* option should not be set on ServerSocket.432*/433434/*435* SO_RCVTIMEO is only supported on Microsoft's implementation436* of Windows Sockets so if WSAENOPROTOOPT returned then437* reset flag and timeout will be implemented using438* select() -- see SocketInputStream.socketRead.439*/440if (isRcvTimeoutSupported) {441/*442* Disable SO_RCVTIMEO if timeout is <= 5 second.443*/444if (timeout <= 5000) {445timeout = 0;446}447448if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout,449sizeof(timeout)) < 0) {450int err = WSAGetLastError();451if (err == WSAENOPROTOOPT) {452isRcvTimeoutSupported = JNI_FALSE;453} else {454NET_ThrowNew(env, err, "setsockopt SO_RCVTIMEO");455}456}457}458}459460/*461* Class: java_net_PlainSocketImpl462* Method: getIntOption463* Signature: (II)I464*/465JNIEXPORT jint JNICALL Java_java_net_PlainSocketImpl_getIntOption466(JNIEnv *env, jclass clazz, jint fd, jint cmd)467{468int level = 0, opt = 0;469int result = 0;470struct linger linger = {0, 0};471char *arg;472int arglen;473474if (NET_MapSocketOption(cmd, &level, &opt) < 0) {475JNU_ThrowByName(env, "java/net/SocketException", "Invalid option");476return -1;477}478479if (opt == java_net_SocketOptions_SO_LINGER) {480arg = (char *)&linger;481arglen = sizeof(linger);482} else {483arg = (char *)&result;484arglen = sizeof(result);485}486487if (NET_GetSockOpt(fd, level, opt, arg, &arglen) < 0) {488NET_ThrowNew(env, WSAGetLastError(), "getsockopt");489return -1;490}491492if (opt == java_net_SocketOptions_SO_LINGER)493return linger.l_onoff ? linger.l_linger : -1;494else495return result;496}497498/*499* Class: java_net_PlainSocketImpl500* Method: sendOOB501* Signature: (II)V502*/503JNIEXPORT void JNICALL Java_java_net_PlainSocketImpl_sendOOB504(JNIEnv *env, jclass clazz, jint fd, jint data) {505jint n;506unsigned char d = (unsigned char) data & 0xff;507508n = send(fd, (char *)&data, 1, MSG_OOB);509if (n == SOCKET_ERROR) {510NET_ThrowNew(env, WSAGetLastError(), "send");511}512}513514/*515* Class: java_net_PlainSocketImpl516* Method: configureBlocking517* Signature: (IZ)V518*/519JNIEXPORT void JNICALL Java_java_net_PlainSocketImpl_configureBlocking520(JNIEnv *env, jclass clazz, jint fd, jboolean blocking) {521u_long arg;522int result;523524if (blocking == JNI_TRUE) {525arg = SET_BLOCKING; // 0526} else {527arg = SET_NONBLOCKING; // 1528}529530result = ioctlsocket(fd, FIONBIO, &arg);531if (result == SOCKET_ERROR) {532NET_ThrowNew(env, WSAGetLastError(), "configureBlocking");533}534}535536537