Path: blob/master/src/java.base/unix/native/libnet/PlainSocketImpl.c
41119 views
/*1* Copyright (c) 1997, 2020, 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 <errno.h>2526#include "jvm.h"27#include "net_util.h"2829#include "java_net_SocketOptions.h"30#include "java_net_PlainSocketImpl.h"3132/************************************************************************33* PlainSocketImpl34*/3536static jfieldID IO_fd_fdID;3738jfieldID psi_fdID;39jfieldID psi_addressID;40jfieldID psi_ipaddressID;41jfieldID psi_portID;42jfieldID psi_localportID;43jfieldID psi_timeoutID;44jfieldID psi_trafficClassID;45jfieldID psi_fdLockID;46jfieldID psi_closePendingID;4748/*49* file descriptor used for dup250*/51static int marker_fd = -1;525354#define SET_NONBLOCKING(fd) { \55int flags = fcntl(fd, F_GETFL); \56flags |= O_NONBLOCK; \57fcntl(fd, F_SETFL, flags); \58}5960#define SET_BLOCKING(fd) { \61int flags = fcntl(fd, F_GETFL); \62flags &= ~O_NONBLOCK; \63fcntl(fd, F_SETFL, flags); \64}6566/*67* Create the marker file descriptor by establishing a loopback connection68* which we shutdown but do not close the fd. The result is an fd that69* can be used for read/write.70*/71static int getMarkerFD()72{73int sv[2];7475#ifdef AF_UNIX76if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == -1) {77return -1;78}79#else80return -1;81#endif8283/*84* Finally shutdown sv[0] (any reads to this fd will get85* EOF; any writes will get an error).86*/87shutdown(sv[0], 2);88close(sv[1]);8990return sv[0];91}9293/*94* Return the file descriptor given a PlainSocketImpl95*/96static int getFD(JNIEnv *env, jobject this) {97jobject fdObj = (*env)->GetObjectField(env, this, psi_fdID);98CHECK_NULL_RETURN(fdObj, -1);99return (*env)->GetIntField(env, fdObj, IO_fd_fdID);100}101102/*103* The initroto function is called whenever PlainSocketImpl is104* loaded, to cache field IDs for efficiency. This is called every time105* the Java class is loaded.106*107* Class: java_net_PlainSocketImpl108* Method: initProto109* Signature: ()V110*/111JNIEXPORT void JNICALL112Java_java_net_PlainSocketImpl_initProto(JNIEnv *env, jclass cls) {113psi_fdID = (*env)->GetFieldID(env, cls , "fd",114"Ljava/io/FileDescriptor;");115CHECK_NULL(psi_fdID);116psi_addressID = (*env)->GetFieldID(env, cls, "address",117"Ljava/net/InetAddress;");118CHECK_NULL(psi_addressID);119psi_portID = (*env)->GetFieldID(env, cls, "port", "I");120CHECK_NULL(psi_portID);121psi_localportID = (*env)->GetFieldID(env, cls, "localport", "I");122CHECK_NULL(psi_localportID);123psi_timeoutID = (*env)->GetFieldID(env, cls, "timeout", "I");124CHECK_NULL(psi_timeoutID);125psi_trafficClassID = (*env)->GetFieldID(env, cls, "trafficClass", "I");126CHECK_NULL(psi_trafficClassID);127psi_fdLockID = (*env)->GetFieldID(env, cls, "fdLock",128"Ljava/lang/Object;");129CHECK_NULL(psi_fdLockID);130psi_closePendingID = (*env)->GetFieldID(env, cls, "closePending", "Z");131CHECK_NULL(psi_closePendingID);132IO_fd_fdID = NET_GetFileDescriptorID(env);133CHECK_NULL(IO_fd_fdID);134135initInetAddressIDs(env);136JNU_CHECK_EXCEPTION(env);137138/* Create the marker fd used for dup2 */139marker_fd = getMarkerFD();140}141142/* a global reference to the java.net.SocketException class. In143* socketCreate, we ensure that this is initialized. This is to144* prevent the problem where socketCreate runs out of file145* descriptors, and is then unable to load the exception class.146*/147static jclass socketExceptionCls;148149/*150* Class: java_net_PlainSocketImpl151* Method: socketCreate152* Signature: (ZZ)V */153JNIEXPORT void JNICALL154Java_java_net_PlainSocketImpl_socketCreate(JNIEnv *env, jobject this,155jboolean stream, jboolean isServer) {156jobject fdObj, ssObj;157int fd;158int type = (stream ? SOCK_STREAM : SOCK_DGRAM);159int domain = ipv6_available() ? AF_INET6 : AF_INET;160161if (socketExceptionCls == NULL) {162jclass c = (*env)->FindClass(env, "java/net/SocketException");163CHECK_NULL(c);164socketExceptionCls = (jclass)(*env)->NewGlobalRef(env, c);165CHECK_NULL(socketExceptionCls);166}167fdObj = (*env)->GetObjectField(env, this, psi_fdID);168169if (fdObj == NULL) {170(*env)->ThrowNew(env, socketExceptionCls, "null fd object");171return;172}173174if ((fd = socket(domain, type, 0)) == -1) {175/* note: if you run out of fds, you may not be able to load176* the exception class, and get a NoClassDefFoundError177* instead.178*/179NET_ThrowNew(env, errno, "can't create socket");180return;181}182183/*184* If IPv4 is available, disable IPV6_V6ONLY to ensure dual-socket support.185*/186if (domain == AF_INET6 && ipv4_available()) {187int arg = 0;188if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&arg,189sizeof(int)) < 0) {190NET_ThrowNew(env, errno, "cannot set IPPROTO_IPV6");191close(fd);192return;193}194}195196/*197* If this is a server socket then enable SO_REUSEADDR198* automatically and set to non blocking.199*/200if (isServer) {201int arg = 1;202SET_NONBLOCKING(fd);203if (NET_SetSockOpt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&arg,204sizeof(arg)) < 0) {205NET_ThrowNew(env, errno, "cannot set SO_REUSEADDR");206close(fd);207return;208}209}210211(*env)->SetIntField(env, fdObj, IO_fd_fdID, fd);212}213214/*215* inetAddress is the address object passed to the socket connect216* call.217*218* Class: java_net_PlainSocketImpl219* Method: socketConnect220* Signature: (Ljava/net/InetAddress;I)V221*/222JNIEXPORT void JNICALL223Java_java_net_PlainSocketImpl_socketConnect(JNIEnv *env, jobject this,224jobject iaObj, jint port,225jint timeout)226{227jint localport = (*env)->GetIntField(env, this, psi_localportID);228int len = 0;229/* fdObj is the FileDescriptor field on this */230jobject fdObj = (*env)->GetObjectField(env, this, psi_fdID);231232jclass clazz = (*env)->GetObjectClass(env, this);233234jobject fdLock;235236jint trafficClass = (*env)->GetIntField(env, this, psi_trafficClassID);237238/* fd is an int field on iaObj */239jint fd;240241SOCKETADDRESS sa;242/* The result of the connection */243int connect_rv = -1;244245if (IS_NULL(fdObj)) {246JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");247return;248} else {249fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);250}251if (IS_NULL(iaObj)) {252JNU_ThrowNullPointerException(env, "inet address argument null.");253return;254}255256/* connect */257if (NET_InetAddressToSockaddr(env, iaObj, port, &sa, &len,258JNI_TRUE) != 0) {259return;260}261262if (trafficClass != 0 && ipv6_available()) {263NET_SetTrafficClass(&sa, trafficClass);264}265266if (timeout <= 0) {267connect_rv = NET_Connect(fd, &sa.sa, len);268} else {269/*270* A timeout was specified. We put the socket into non-blocking271* mode, connect, and then wait for the connection to be272* established, fail, or timeout.273*/274SET_NONBLOCKING(fd);275276/* no need to use NET_Connect as non-blocking */277connect_rv = connect(fd, &sa.sa, len);278279/* connection not established immediately */280if (connect_rv != 0) {281socklen_t optlen;282jlong nanoTimeout = (jlong) timeout * NET_NSEC_PER_MSEC;283jlong prevNanoTime = JVM_NanoTime(env, 0);284285if (errno != EINPROGRESS) {286NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "ConnectException",287"connect failed");288SET_BLOCKING(fd);289return;290}291292/*293* Wait for the connection to be established or a294* timeout occurs. poll needs to handle EINTR in295* case lwp sig handler redirects any process signals to296* this thread.297*/298while (1) {299jlong newNanoTime;300struct pollfd pfd;301pfd.fd = fd;302pfd.events = POLLOUT;303304errno = 0;305connect_rv = NET_Poll(&pfd, 1, nanoTimeout / NET_NSEC_PER_MSEC);306307if (connect_rv >= 0) {308break;309}310if (errno != EINTR) {311break;312}313314/*315* The poll was interrupted so adjust timeout and316* restart317*/318newNanoTime = JVM_NanoTime(env, 0);319nanoTimeout -= (newNanoTime - prevNanoTime);320if (nanoTimeout < NET_NSEC_PER_MSEC) {321connect_rv = 0;322break;323}324prevNanoTime = newNanoTime;325326} /* while */327328if (connect_rv == 0) {329JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException",330"connect timed out");331332/*333* Timeout out but connection may still be established.334* At the high level it should be closed immediately but335* just in case we make the socket blocking again and336* shutdown input & output.337*/338SET_BLOCKING(fd);339shutdown(fd, 2);340return;341}342343/* has connection been established */344optlen = sizeof(connect_rv);345if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (void*)&connect_rv,346&optlen) <0) {347connect_rv = errno;348}349}350351/* make socket blocking again */352SET_BLOCKING(fd);353354/* restore errno */355if (connect_rv != 0) {356errno = connect_rv;357connect_rv = -1;358}359}360361/* report the appropriate exception */362if (connect_rv < 0) {363364#ifdef __linux__365/*366* Linux/GNU distribution setup /etc/hosts so that367* InetAddress.getLocalHost gets back the loopback address368* rather than the host address. Thus a socket can be369* bound to the loopback address and the connect will370* fail with EADDRNOTAVAIL. In addition the Linux kernel371* returns the wrong error in this case - it returns EINVAL372* instead of EADDRNOTAVAIL. We handle this here so that373* a more descriptive exception text is used.374*/375if (connect_rv == -1 && errno == EINVAL) {376JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",377"Invalid argument or cannot assign requested address");378return;379}380#endif381#if defined(EPROTO)382if (errno == EPROTO) {383NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "ProtocolException",384"Protocol error");385return;386}387#endif388if (errno == ECONNREFUSED) {389NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "ConnectException",390"Connection refused");391} else if (errno == ETIMEDOUT) {392NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "ConnectException",393"Connection timed out");394} else if (errno == EHOSTUNREACH) {395NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "NoRouteToHostException",396"Host unreachable");397} else if (errno == EADDRNOTAVAIL) {398NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "NoRouteToHostException",399"Address not available");400} else if ((errno == EISCONN) || (errno == EBADF)) {401JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",402"Socket closed");403} else {404JNU_ThrowByNameWithMessageAndLastError405(env, JNU_JAVANETPKG "SocketException", "connect failed");406}407return;408}409410(*env)->SetIntField(env, fdObj, IO_fd_fdID, fd);411412/* set the remote peer address and port */413(*env)->SetObjectField(env, this, psi_addressID, iaObj);414(*env)->SetIntField(env, this, psi_portID, port);415416/*417* we need to initialize the local port field if bind was called418* previously to the connect (by the client) then localport field419* will already be initialized420*/421if (localport == 0) {422/* Now that we're a connected socket, let's extract the port number423* that the system chose for us and store it in the Socket object.424*/425socklen_t slen = sizeof(SOCKETADDRESS);426if (getsockname(fd, &sa.sa, &slen) == -1) {427JNU_ThrowByNameWithMessageAndLastError428(env, JNU_JAVANETPKG "SocketException", "Error getting socket name");429} else {430localport = NET_GetPortFromSockaddr(&sa);431(*env)->SetIntField(env, this, psi_localportID, localport);432}433}434}435436/*437* Class: java_net_PlainSocketImpl438* Method: socketBind439* Signature: (Ljava/net/InetAddress;I)V440*/441JNIEXPORT void JNICALL442Java_java_net_PlainSocketImpl_socketBind(JNIEnv *env, jobject this,443jobject iaObj, jint localport) {444445/* fdObj is the FileDescriptor field on this */446jobject fdObj = (*env)->GetObjectField(env, this, psi_fdID);447/* fd is an int field on fdObj */448int fd;449int len = 0;450SOCKETADDRESS sa;451452if (IS_NULL(fdObj)) {453JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",454"Socket closed");455return;456} else {457fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);458}459if (IS_NULL(iaObj)) {460JNU_ThrowNullPointerException(env, "iaObj is null.");461return;462}463464/* bind */465if (NET_InetAddressToSockaddr(env, iaObj, localport, &sa,466&len, JNI_TRUE) != 0) {467return;468}469470if (NET_Bind(fd, &sa, len) < 0) {471if (errno == EADDRINUSE || errno == EADDRNOTAVAIL ||472errno == EPERM || errno == EACCES) {473NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "BindException",474"Bind failed");475} else {476JNU_ThrowByNameWithMessageAndLastError477(env, JNU_JAVANETPKG "SocketException", "Bind failed");478}479return;480}481482/* set the address */483(*env)->SetObjectField(env, this, psi_addressID, iaObj);484485/* initialize the local port */486if (localport == 0) {487socklen_t slen = sizeof(SOCKETADDRESS);488/* Now that we're a connected socket, let's extract the port number489* that the system chose for us and store it in the Socket object.490*/491if (getsockname(fd, &sa.sa, &slen) == -1) {492JNU_ThrowByNameWithMessageAndLastError493(env, JNU_JAVANETPKG "SocketException", "Error getting socket name");494return;495}496localport = NET_GetPortFromSockaddr(&sa);497(*env)->SetIntField(env, this, psi_localportID, localport);498} else {499(*env)->SetIntField(env, this, psi_localportID, localport);500}501}502503/*504* Class: java_net_PlainSocketImpl505* Method: socketListen506* Signature: (I)V507*/508JNIEXPORT void JNICALL509Java_java_net_PlainSocketImpl_socketListen(JNIEnv *env, jobject this,510jint count)511{512/* this FileDescriptor fd field */513jobject fdObj = (*env)->GetObjectField(env, this, psi_fdID);514/* fdObj's int fd field */515int fd;516517if (IS_NULL(fdObj)) {518JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",519"Socket closed");520return;521} else {522fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);523}524525/*526* Workaround for bugid 4101691 in Solaris 2.6. See 4106600.527* If listen backlog is Integer.MAX_VALUE then subtract 1.528*/529if (count == 0x7fffffff)530count -= 1;531532if (listen(fd, count) == -1) {533JNU_ThrowByNameWithMessageAndLastError534(env, JNU_JAVANETPKG "SocketException", "Listen failed");535}536}537538/*539* Class: java_net_PlainSocketImpl540* Method: socketAccept541* Signature: (Ljava/net/SocketImpl;)V542*/543JNIEXPORT void JNICALL544Java_java_net_PlainSocketImpl_socketAccept(JNIEnv *env, jobject this,545jobject socket)546{547/* fields on this */548int port;549jint timeout = (*env)->GetIntField(env, this, psi_timeoutID);550jlong prevNanoTime = 0;551jlong nanoTimeout = (jlong) timeout * NET_NSEC_PER_MSEC;552jobject fdObj = (*env)->GetObjectField(env, this, psi_fdID);553554/* the FileDescriptor field on socket */555jobject socketFdObj;556/* the InetAddress field on socket */557jobject socketAddressObj;558559/* the ServerSocket fd int field on fdObj */560jint fd;561562/* accepted fd */563jint newfd;564565SOCKETADDRESS sa;566socklen_t slen = sizeof(SOCKETADDRESS);567568if (IS_NULL(fdObj)) {569JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",570"Socket closed");571return;572} else {573fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);574}575if (IS_NULL(socket)) {576JNU_ThrowNullPointerException(env, "socket is null");577return;578}579580/*581* accept connection but ignore ECONNABORTED indicating that582* connection was eagerly accepted by the OS but was reset583* before accept() was called.584*585* If accept timeout in place and timeout is adjusted with586* each ECONNABORTED or EWOULDBLOCK or EAGAIN to ensure that587* semantics of timeout are preserved.588*/589for (;;) {590int ret;591jlong currNanoTime;592593/* first usage pick up current time */594if (prevNanoTime == 0 && nanoTimeout > 0) {595prevNanoTime = JVM_NanoTime(env, 0);596}597598/* passing a timeout of 0 to poll will return immediately,599but in the case of ServerSocket 0 means infinite. */600if (timeout <= 0) {601ret = NET_Timeout(env, fd, -1, 0);602} else {603ret = NET_Timeout(env, fd, nanoTimeout / NET_NSEC_PER_MSEC, prevNanoTime);604}605if (ret == 0) {606JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException",607"Accept timed out");608return;609} else if (ret == -1) {610if (errno == EBADF) {611JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");612} else if (errno == ENOMEM) {613JNU_ThrowOutOfMemoryError(env, "NET_Timeout native heap allocation failed");614} else {615JNU_ThrowByNameWithMessageAndLastError616(env, JNU_JAVANETPKG "SocketException", "Accept failed");617}618return;619}620621newfd = NET_Accept(fd, &sa.sa, &slen);622623/* connection accepted */624if (newfd >= 0) {625SET_BLOCKING(newfd);626break;627}628629/* non (ECONNABORTED or EWOULDBLOCK or EAGAIN) error */630if (!(errno == ECONNABORTED || errno == EWOULDBLOCK || errno == EAGAIN)) {631break;632}633634/* ECONNABORTED or EWOULDBLOCK or EAGAIN error so adjust timeout if there is one. */635if (nanoTimeout >= NET_NSEC_PER_MSEC) {636currNanoTime = JVM_NanoTime(env, 0);637nanoTimeout -= (currNanoTime - prevNanoTime);638if (nanoTimeout < NET_NSEC_PER_MSEC) {639JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException",640"Accept timed out");641return;642}643prevNanoTime = currNanoTime;644}645}646647if (newfd < 0) {648if (newfd == -2) {649JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException",650"operation interrupted");651} else {652if (errno == EINVAL) {653errno = EBADF;654}655if (errno == EBADF) {656JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");657} else {658JNU_ThrowByNameWithMessageAndLastError659(env, JNU_JAVANETPKG "SocketException", "Accept failed");660}661}662return;663}664665/*666* fill up the remote peer port and address in the new socket structure.667*/668socketAddressObj = NET_SockaddrToInetAddress(env, &sa, &port);669if (socketAddressObj == NULL) {670/* should be pending exception */671close(newfd);672return;673}674675/*676* Populate SocketImpl.fd.fd677*/678socketFdObj = (*env)->GetObjectField(env, socket, psi_fdID);679(*env)->SetIntField(env, socketFdObj, IO_fd_fdID, newfd);680681(*env)->SetObjectField(env, socket, psi_addressID, socketAddressObj);682(*env)->SetIntField(env, socket, psi_portID, port);683/* also fill up the local port information */684port = (*env)->GetIntField(env, this, psi_localportID);685(*env)->SetIntField(env, socket, psi_localportID, port);686}687688689/*690* Class: java_net_PlainSocketImpl691* Method: socketAvailable692* Signature: ()I693*/694JNIEXPORT jint JNICALL695Java_java_net_PlainSocketImpl_socketAvailable(JNIEnv *env, jobject this) {696int count = 0;697jobject fdObj = (*env)->GetObjectField(env, this, psi_fdID);698jint fd;699700if (IS_NULL(fdObj)) {701JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",702"Socket closed");703return -1;704} else {705fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);706}707if (NET_SocketAvailable(fd, &count) != 0) {708if (errno == ECONNRESET) {709JNU_ThrowByName(env, "sun/net/ConnectionResetException", "");710} else {711JNU_ThrowByNameWithMessageAndLastError712(env, JNU_JAVANETPKG "SocketException", "ioctl FIONREAD failed");713}714}715return (jint) count;716}717718/*719* Class: java_net_PlainSocketImpl720* Method: socketClose0721* Signature: (Z)V722*/723JNIEXPORT void JNICALL724Java_java_net_PlainSocketImpl_socketClose0(JNIEnv *env, jobject this,725jboolean useDeferredClose) {726727jobject fdObj = (*env)->GetObjectField(env, this, psi_fdID);728jint fd;729730if (IS_NULL(fdObj)) {731JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",732"socket already closed");733return;734} else {735fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);736}737if (fd != -1) {738if (useDeferredClose && marker_fd >= 0) {739NET_Dup2(marker_fd, fd);740} else {741(*env)->SetIntField(env, fdObj, IO_fd_fdID, -1);742NET_SocketClose(fd);743}744}745}746747/*748* Class: java_net_PlainSocketImpl749* Method: socketShutdown750* Signature: (I)V751*/752JNIEXPORT void JNICALL753Java_java_net_PlainSocketImpl_socketShutdown(JNIEnv *env, jobject this,754jint howto)755{756757jobject fdObj = (*env)->GetObjectField(env, this, psi_fdID);758jint fd;759760/*761* WARNING: THIS NEEDS LOCKING. ALSO: SHOULD WE CHECK for fd being762* -1 already?763*/764if (IS_NULL(fdObj)) {765JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",766"socket already closed");767return;768} else {769fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);770}771shutdown(fd, howto);772}773774775/*776* Class: java_net_PlainSocketImpl777* Method: socketSetOption0778* Signature: (IZLjava/lang/Object;)V779*/780JNIEXPORT void JNICALL781Java_java_net_PlainSocketImpl_socketSetOption0782(JNIEnv *env, jobject this, jint cmd, jboolean on, jobject value)783{784int fd;785int level, optname, optlen;786union {787int i;788struct linger ling;789} optval;790791/*792* Check that socket hasn't been closed793*/794fd = getFD(env, this);795if (fd < 0) {796JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",797"Socket closed");798return;799}800801/*802* SO_TIMEOUT is a NOOP on Solaris/Linux803*/804if (cmd == java_net_SocketOptions_SO_TIMEOUT) {805return;806}807808/*809* Map the Java level socket option to the platform specific810* level and option name.811*/812if (NET_MapSocketOption(cmd, &level, &optname)) {813JNU_ThrowByName(env, "java/net/SocketException", "Invalid option");814return;815}816817switch (cmd) {818case java_net_SocketOptions_SO_SNDBUF :819case java_net_SocketOptions_SO_RCVBUF :820case java_net_SocketOptions_SO_LINGER :821case java_net_SocketOptions_IP_TOS :822{823jclass cls;824jfieldID fid;825826cls = (*env)->FindClass(env, "java/lang/Integer");827CHECK_NULL(cls);828fid = (*env)->GetFieldID(env, cls, "value", "I");829CHECK_NULL(fid);830831if (cmd == java_net_SocketOptions_SO_LINGER) {832if (on) {833optval.ling.l_onoff = 1;834optval.ling.l_linger = (*env)->GetIntField(env, value, fid);835} else {836optval.ling.l_onoff = 0;837optval.ling.l_linger = 0;838}839optlen = sizeof(optval.ling);840} else {841optval.i = (*env)->GetIntField(env, value, fid);842optlen = sizeof(optval.i);843}844845break;846}847848/* Boolean -> int */849default :850optval.i = (on ? 1 : 0);851optlen = sizeof(optval.i);852853}854855if (NET_SetSockOpt(fd, level, optname, (const void *)&optval, optlen) < 0) {856#if defined(_AIX)857if (errno == EINVAL) {858// On AIX setsockopt will set errno to EINVAL if the socket859// is closed. The default error message is then confusing860char fullMsg[128];861jio_snprintf(fullMsg, sizeof(fullMsg), "Invalid option or socket reset by remote peer");862JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", fullMsg);863return;864}865#endif /* _AIX */866JNU_ThrowByNameWithMessageAndLastError867(env, JNU_JAVANETPKG "SocketException", "Error setting socket option");868}869}870871/*872* Class: java_net_PlainSocketImpl873* Method: socketGetOption874* Signature: (ILjava/lang/Object;)I875*/876JNIEXPORT jint JNICALL877Java_java_net_PlainSocketImpl_socketGetOption878(JNIEnv *env, jobject this, jint cmd, jobject iaContainerObj)879{880int fd;881int level, optname, optlen;882union {883int i;884struct linger ling;885} optval;886887/*888* Check that socket hasn't been closed889*/890fd = getFD(env, this);891if (fd < 0) {892JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",893"Socket closed");894return -1;895}896897/*898* SO_BINDADDR isn't a socket option899*/900if (cmd == java_net_SocketOptions_SO_BINDADDR) {901SOCKETADDRESS sa;902socklen_t len = sizeof(SOCKETADDRESS);903int port;904jobject iaObj;905jclass iaCntrClass;906jfieldID iaFieldID;907908if (getsockname(fd, &sa.sa, &len) < 0) {909JNU_ThrowByNameWithMessageAndLastError910(env, JNU_JAVANETPKG "SocketException", "Error getting socket name");911return -1;912}913iaObj = NET_SockaddrToInetAddress(env, &sa, &port);914CHECK_NULL_RETURN(iaObj, -1);915916iaCntrClass = (*env)->GetObjectClass(env, iaContainerObj);917iaFieldID = (*env)->GetFieldID(env, iaCntrClass, "addr", "Ljava/net/InetAddress;");918CHECK_NULL_RETURN(iaFieldID, -1);919(*env)->SetObjectField(env, iaContainerObj, iaFieldID, iaObj);920return 0; /* notice change from before */921}922923/*924* Map the Java level socket option to the platform specific925* level and option name.926*/927if (NET_MapSocketOption(cmd, &level, &optname)) {928JNU_ThrowByName(env, "java/net/SocketException", "Invalid option");929return -1;930}931932/*933* Args are int except for SO_LINGER934*/935if (cmd == java_net_SocketOptions_SO_LINGER) {936optlen = sizeof(optval.ling);937} else {938optlen = sizeof(optval.i);939}940941if (NET_GetSockOpt(fd, level, optname, (void *)&optval, &optlen) < 0) {942JNU_ThrowByNameWithMessageAndLastError943(env, JNU_JAVANETPKG "SocketException", "Error getting socket option");944return -1;945}946947switch (cmd) {948case java_net_SocketOptions_SO_LINGER:949return (optval.ling.l_onoff ? optval.ling.l_linger: -1);950951case java_net_SocketOptions_SO_SNDBUF:952case java_net_SocketOptions_SO_RCVBUF:953case java_net_SocketOptions_IP_TOS:954return optval.i;955956default :957return (optval.i == 0) ? -1 : 1;958}959}960961962/*963* Class: java_net_PlainSocketImpl964* Method: socketSendUrgentData965* Signature: (B)V966*/967JNIEXPORT void JNICALL968Java_java_net_PlainSocketImpl_socketSendUrgentData(JNIEnv *env, jobject this,969jint data) {970/* The fd field */971jobject fdObj = (*env)->GetObjectField(env, this, psi_fdID);972int n, fd;973unsigned char d = data & 0xFF;974975if (IS_NULL(fdObj)) {976JNU_ThrowByName(env, "java/net/SocketException", "Socket closed");977return;978} else {979fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);980/* Bug 4086704 - If the Socket associated with this file descriptor981* was closed (sysCloseFD), the file descriptor is set to -1.982*/983if (fd == -1) {984JNU_ThrowByName(env, "java/net/SocketException", "Socket closed");985return;986}987988}989n = NET_Send(fd, (char *)&d, 1, MSG_OOB);990if (n == -1) {991JNU_ThrowIOExceptionWithLastError(env, "Write failed");992}993}994995996