/* * Copyright (c) 1997, 2007, 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. */ package java.net; import java.io.FileDescriptor; import static ikvm.internal.JNI.*; import static ikvm.internal.Winsock.*; import static java.net.net_util_md.*; import static java.net.InetAddress.IPv4; import static java.net.InetAddress.IPv6; final class TwoStacksPlainSocketImpl_c { static final int JVM_IO_ERR = -1; static final int JVM_IO_INTR = -2; static final int java_net_SocketOptions_SO_TIMEOUT = SocketOptions.SO_TIMEOUT; static final int java_net_SocketOptions_SO_BINDADDR = SocketOptions.SO_BINDADDR; static final int java_net_SocketOptions_SO_SNDBUF = SocketOptions.SO_SNDBUF; static final int java_net_SocketOptions_SO_RCVBUF = SocketOptions.SO_RCVBUF; static final int java_net_SocketOptions_IP_TOS = SocketOptions.IP_TOS; static final int java_net_SocketOptions_SO_REUSEADDR = SocketOptions.SO_REUSEADDR; static final int java_net_SocketOptions_TCP_NODELAY = SocketOptions.TCP_NODELAY; static final int java_net_SocketOptions_SO_OOBINLINE = SocketOptions.SO_OOBINLINE; static final int java_net_SocketOptions_SO_KEEPALIVE = SocketOptions.SO_KEEPALIVE; static final int java_net_SocketOptions_SO_LINGER = SocketOptions.SO_LINGER; /* #include <windows.h> #include <winsock2.h> #include <ctype.h> #include <stdio.h> #include <stdlib.h> #include <malloc.h> #include <sys/types.h> #include "java_net_SocketOptions.h" #include "java_net_TwoStacksPlainSocketImpl.h" #include "java_net_SocketImpl.h" #include "java_net_InetAddress.h" #include "java_io_FileDescriptor.h" #include "java_lang_Integer.h" #include "jvm.h" #include "net_util.h" #include "jni_util.h" */ /************************************************************************ * TwoStacksPlainSocketImpl */ /* static jfieldID IO_fd_fdID; jfieldID psi_fdID; jfieldID psi_fd1ID; jfieldID psi_addressID; jfieldID psi_portID; jfieldID psi_localportID; jfieldID psi_timeoutID; jfieldID psi_trafficClassID; jfieldID psi_serverSocketID; jfieldID psi_lastfdID; */ /* * the level of the TCP protocol for setsockopt and getsockopt * we only want to look this up once, from the static initializer * of TwoStacksPlainSocketImpl */ static int tcp_level = -1; static cli.System.Net.Sockets.Socket getFD(JNIEnv env, TwoStacksPlainSocketImpl _this) { FileDescriptor fdObj = _this.fd; if (fdObj == NULL) { return null; } return fdObj.getSocket(); } static cli.System.Net.Sockets.Socket getFD1(JNIEnv env, TwoStacksPlainSocketImpl _this) { FileDescriptor fdObj = _this.fd1; if (fdObj == NULL) { return null; } return fdObj.getSocket(); } /* * The initProto function is called whenever TwoStacksPlainSocketImpl is * loaded, to cache fieldIds for efficiency. This is called everytime * the Java class is loaded. * * Class: java_net_TwoStacksPlainSocketImpl * Method: initProto * Signature: ()V */ /* JNIEXPORT void JNICALL Java_java_net_TwoStacksPlainSocketImpl_initProto(JNIEnv *env, jclass cls) { struct protoent *proto = getprotobyname("TCP"); tcp_level = (proto == 0 ? IPPROTO_TCP: proto->p_proto); psi_fdID = (*env)->GetFieldID(env, cls , "fd", "Ljava/io/FileDescriptor;"); CHECK_NULL(psi_fdID); psi_fd1ID =(*env)->GetFieldID(env, cls , "fd1", "Ljava/io/FileDescriptor;"); CHECK_NULL(psi_fd1ID); psi_addressID = (*env)->GetFieldID(env, cls, "address", "Ljava/net/InetAddress;"); CHECK_NULL(psi_addressID); psi_portID = (*env)->GetFieldID(env, cls, "port", "I"); CHECK_NULL(psi_portID); psi_lastfdID = (*env)->GetFieldID(env, cls, "lastfd", "I"); CHECK_NULL(psi_portID); psi_localportID = (*env)->GetFieldID(env, cls, "localport", "I"); CHECK_NULL(psi_localportID); psi_timeoutID = (*env)->GetFieldID(env, cls, "timeout", "I"); CHECK_NULL(psi_timeoutID); psi_trafficClassID = (*env)->GetFieldID(env, cls, "trafficClass", "I"); CHECK_NULL(psi_trafficClassID); psi_serverSocketID = (*env)->GetFieldID(env, cls, "serverSocket", "Ljava/net/ServerSocket;"); CHECK_NULL(psi_serverSocketID); IO_fd_fdID = NET_GetFileDescriptorID(env); CHECK_NULL(IO_fd_fdID); } */ /* * Class: java_net_TwoStacksPlainSocketImpl * Method: socketCreate * Signature: (Z)V */ static void socketCreate(JNIEnv env, TwoStacksPlainSocketImpl _this, boolean stream) { FileDescriptor fdObj = _this.fd; FileDescriptor fd1Obj = _this.fd1; cli.System.Net.Sockets.Socket fd = null; cli.System.Net.Sockets.Socket fd1 = null; if (IS_NULL(fdObj)) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "null fd object"); return; } fd = socket(AF_INET, (stream ? SOCK_STREAM: SOCK_DGRAM), 0); if (fd == INVALID_SOCKET) { NET_ThrowCurrent(env, "create"); return; } else { /* Set socket attribute so it is not passed to any child process */ //SetHandleInformation((HANDLE)(UINT_PTR)fd, HANDLE_FLAG_INHERIT, FALSE); fdObj.setSocket(fd); } if (ipv6_available()) { if (IS_NULL(fd1Obj)) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "null fd1 object"); fdObj.setSocket(null); NET_SocketClose(fd); return; } fd1 = socket(AF_INET6, (stream ? SOCK_STREAM: SOCK_DGRAM), 0); if (fd1 == INVALID_SOCKET) { NET_ThrowCurrent(env, "create"); fdObj.setSocket(null); NET_SocketClose(fd); return; } else { fd1Obj.setSocket(fd1); } } else { _this.fd1 = null; } } /* * inetAddress is the address object passed to the socket connect * call. * * Class: java_net_TwoStacksPlainSocketImpl * Method: socketConnect * Signature: (Ljava/net/InetAddress;I)V */ static void socketConnect(JNIEnv env, TwoStacksPlainSocketImpl _this, InetAddress iaObj, int port, int timeout) { int localport = _this.localport; /* family and localport are int fields of iaObj */ int family; cli.System.Net.Sockets.Socket fd = null; cli.System.Net.Sockets.Socket fd1 = null; boolean ipv6_supported = ipv6_available(); /* fd initially points to the IPv4 socket and fd1 to the IPv6 socket * If we want to connect to IPv6 then we swap the two sockets/objects * This way, fd is always the connected socket, and fd1 always gets closed. */ FileDescriptor fdObj = _this.fd; FileDescriptor fd1Obj = _this.fd1; SOCKETADDRESS him; him = new SOCKETADDRESS(); /* The result of the connection */ int connect_res; if (!IS_NULL(fdObj)) { fd = fdObj.getSocket(); } if (ipv6_supported && !IS_NULL(fd1Obj)) { fd1 = fd1Obj.getSocket(); } if (IS_NULL(iaObj)) { JNU_ThrowNullPointerException(env, "inet address argument is null."); return; } if (NET_InetAddressToSockaddr(env, iaObj, port, him, JNI_FALSE) != 0) { return; } family = him.him.sa_family; if (family == AF_INET6) { if (!ipv6_supported) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "Protocol family not supported"); return; } else { if (fd1 == null) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "Destination unreachable"); return; } /* close the v4 socket, and set fd to be the v6 socket */ _this.fd = fd1Obj; _this.fd1 = null; NET_SocketClose(fd); fd = fd1; fdObj = fd1Obj; } } else { if (fd1 != null) { fd1Obj.setSocket(null); NET_SocketClose(fd1); } if (fd == null) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "Destination unreachable"); return; } } _this.fd1 = null; if (timeout <= 0) { connect_res = connect(fd, him); if (connect_res == SOCKET_ERROR) { connect_res = WSAGetLastError(); } } else { int optval; /* make socket non-blocking */ optval = 1; ioctlsocket( fd, FIONBIO, optval ); /* initiate the connect */ connect_res = connect(fd, him); if (connect_res == SOCKET_ERROR) { if (WSAGetLastError() != WSAEWOULDBLOCK) { connect_res = WSAGetLastError(); } else { fd_set wr, ex; wr = new fd_set(); ex = new fd_set(); timeval t = new timeval(); 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 timout, connection established or * connection failed. */ connect_res = select(null, 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 (connect_res == 0) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketTimeoutException", "connect timed out"); shutdown( fd, SD_BOTH ); /* make socket blocking again - just in case */ optval = 0; ioctlsocket( fd, FIONBIO, optval ); return; } /* * We must now determine if the connection has been established * or if it has 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. */ if (!FD_ISSET(fd, ex)) { connect_res = 0; } else { int retry; for (retry=0; retry<3; retry++) { int[] tmp = { 0 }; NET_GetSockOpt(fd, SOL_SOCKET, SO_ERROR, tmp); connect_res = tmp[0]; if (connect_res != 0) { break; } Sleep(0); } if (connect_res == 0) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "Unable to establish connection"); return; } } } } /* make socket blocking again */ optval = 0; ioctlsocket(fd, FIONBIO, optval); } if (connect_res != 0) { if (connect_res == 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, connect_res, "connect"); } return; } fdObj.setSocket(fd); /* set the remote peer address and port */ _this.address = iaObj; _this.port = port; /* * we need to initialize the local port field if bind was called * previously to the connect (by the client) then localport field * will already be initialized */ if (localport == 0) { /* Now that we're a connected socket, let's extract the port number * that the system chose for us and store it in the Socket object. */ if (getsockname(fd, him) == -1) { if (WSAGetLastError() == WSAENOTSOCK) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "Socket closed"); } else { NET_ThrowCurrent(env, "getsockname failed"); } return; } port = ntohs (GET_PORT(him)); _this.localport = port; } } /* * Class: java_net_TwoStacksPlainSocketImpl * Method: socketBind * Signature: (Ljava/net/InetAddress;I)V */ static void socketBind(JNIEnv env, TwoStacksPlainSocketImpl _this, InetAddress iaObj, int localport) { FileDescriptor fdObj = _this.fd; FileDescriptor fd1Obj = _this.fd1; cli.System.Net.Sockets.Socket fd = null; cli.System.Net.Sockets.Socket fd1 = null; boolean ipv6_supported = ipv6_available(); /* family is an int field of iaObj */ int family; int rv; SOCKETADDRESS him; him = new SOCKETADDRESS(); family = iaObj.family; if (family == IPv6 && !ipv6_supported) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "Protocol family not supported"); return; } if (IS_NULL(fdObj) || (ipv6_supported && IS_NULL(fd1Obj))) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "Socket closed"); return; } else { fd = fdObj.getSocket(); if (ipv6_supported) { fd1 = fd1Obj.getSocket(); } } if (IS_NULL(iaObj)) { JNU_ThrowNullPointerException(env, "inet address argument"); return; } if (NET_InetAddressToSockaddr(env, iaObj, localport, him, JNI_FALSE) != 0) { return; } if (ipv6_supported) { ipv6bind v6bind = new ipv6bind(); v6bind.addr = him; v6bind.ipv4_fd = fd; v6bind.ipv6_fd = fd1; rv = NET_BindV6(v6bind); if (rv != -1) { /* check if the fds have changed */ if (v6bind.ipv4_fd != fd) { fd = v6bind.ipv4_fd; if (fd == null) { /* socket is closed. */ _this.fd = null; } else { /* socket was re-created */ fdObj.setSocket(fd); } } if (v6bind.ipv6_fd != fd1) { fd1 = v6bind.ipv6_fd; if (fd1 == null) { /* socket is closed. */ _this.fd1 = null; } else { /* socket was re-created */ fd1Obj.setSocket(fd1); } } } } else { rv = NET_Bind(fd, him); } if (rv == -1) { NET_ThrowCurrent(env, "JVM_Bind"); return; } /* set the address */ _this.address = iaObj; /* intialize the local port */ if (localport == 0) { /* Now that we're a bound socket, let's extract the port number * that the system chose for us and store it in the Socket object. */ int port; fd = him.him.sa_family == AF_INET? fd: fd1; if (getsockname(fd, him) == -1) { NET_ThrowCurrent(env, "getsockname in plain socketBind"); return; } port = ntohs (GET_PORT (him)); _this.localport = port; } else { _this.localport = localport; } } /* * Class: java_net_TwoStacksPlainSocketImpl * Method: socketListen * Signature: (I)V */ static void socketListen (JNIEnv env, TwoStacksPlainSocketImpl _this, int count) { /* this FileDescriptor fd field */ FileDescriptor fdObj = _this.fd; FileDescriptor fd1Obj = _this.fd1; InetAddress address; /* fdObj's int fd field */ cli.System.Net.Sockets.Socket fd = null; cli.System.Net.Sockets.Socket fd1 = null; SOCKETADDRESS addr = new SOCKETADDRESS(); if (IS_NULL(fdObj) && IS_NULL(fd1Obj)) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "socket closed"); return; } if (!IS_NULL(fdObj)) { fd = fdObj.getSocket(); } /* Listen on V4 if address type is v4 or if v6 and address is ::0. * Listen on V6 if address type is v6 or if v4 and address is 0.0.0.0. * In cases, where we listen on one space only, we close the other socket. */ address = _this.address; if (IS_NULL(address)) { JNU_ThrowNullPointerException(env, "socket address"); return; } if (NET_InetAddressToSockaddr(env, address, 0, addr, JNI_FALSE) != 0) { return; } if (addr.him.sa_family == AF_INET || IN6ADDR_ISANY(addr.him6)) { /* listen on v4 */ if (listen(fd, count) == -1) { NET_ThrowCurrent(env, "listen failed"); } } else { NET_SocketClose (fd); _this.fd = null; } if (ipv6_available() && !IS_NULL(fd1Obj)) { fd1 = fd1Obj.getSocket(); if (addr.him.sa_family == AF_INET6 || addr.him4.sin_addr.s_addr == INADDR_ANY) { /* listen on v6 */ if (listen(fd1, count) == -1) { NET_ThrowCurrent(env, "listen failed"); } } else { NET_SocketClose (fd1); _this.fd1 = null; } } } /* * Class: java_net_TwoStacksPlainSocketImpl * Method: socketAccept * Signature: (Ljava/net/SocketImpl;)V */ static void socketAccept(JNIEnv env, TwoStacksPlainSocketImpl _this, SocketImpl socket) { /* fields on this */ int port; int timeout = _this.timeout; FileDescriptor fdObj = _this.fd; FileDescriptor fd1Obj = _this.fd1; /* the FileDescriptor field on socket */ FileDescriptor socketFdObj; /* the InetAddress field on socket */ InetAddress socketAddressObj; /* the fd int field on fdObj */ cli.System.Net.Sockets.Socket fd=null, fd1=null; SOCKETADDRESS him; him = new SOCKETADDRESS(); if (IS_NULL(fdObj) && IS_NULL(fd1Obj)) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "Socket closed"); return; } if (!IS_NULL(fdObj)) { fd = fdObj.getSocket(); } if (!IS_NULL(fd1Obj)) { fd1 = fd1Obj.getSocket(); } if (IS_NULL(socket)) { JNU_ThrowNullPointerException(env, "socket is null"); return; } else { socketFdObj = socket.fd; socketAddressObj = socket.address; } if ((IS_NULL(socketAddressObj)) || (IS_NULL(socketFdObj))) { JNU_ThrowNullPointerException(env, "socket address or fd obj"); return; } if (fd != null && fd1 != null) { fd_set rfds = new fd_set(); timeval t = new timeval(); cli.System.Net.Sockets.Socket lastfd, fd2; FD_ZERO(rfds); FD_SET(fd,rfds); FD_SET(fd1,rfds); if (timeout != 0) { t.tv_sec = timeout/1000; t.tv_usec = (timeout%1000)*1000; } else { t = null; } int res = select (rfds, null, null, t); if (res == 0) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketTimeoutException", "Accept timed out"); return; } else if (res == 1) { fd2 = FD_ISSET(fd, rfds)? fd: fd1; } else if (res == 2) { /* avoid starvation */ lastfd = _this.lastfd; if (lastfd != null) { fd2 = lastfd==fd? fd1: fd; } else { fd2 = fd; } _this.lastfd = fd2; } else { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "select failed"); return; } fd = fd2; } else { int ret; if (fd1 != null) { fd = fd1; } if (timeout != 0) { ret = NET_Timeout(fd, timeout); if (ret == 0) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketTimeoutException", "Accept timed out"); return; } else if (ret == -1) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "socket closed"); /* REMIND: SOCKET CLOSED PROBLEM */ /* NET_ThrowCurrent(env, "Accept failed"); */ return; } else if (ret == -2) { JNU_ThrowByName(env, JNU_JAVAIOPKG+"InterruptedIOException", "operation interrupted"); return; } } } fd = accept(fd, him); if (fd == null) { /* REMIND: SOCKET CLOSED PROBLEM */ if (false) { JNU_ThrowByName(env, JNU_JAVAIOPKG+"InterruptedIOException", "operation interrupted"); } else { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "socket closed"); } return; } socketFdObj.setSocket(fd); if (him.him.sa_family == AF_INET) { /* * fill up the remote peer port and address in the new socket structure */ socketAddressObj = new Inet4Address(null, ntohl(him.him4.sin_addr.s_addr)); socket.address = socketAddressObj; } else { /* AF_INET6 -> Inet6Address */ // [IKVM] We need to convert scope_id 0 to -1 here, because for sin6_scope_id 0 means unspecified, whereas Java uses -1 int scopeId = him.him6.sin6_scope_id; socketAddressObj = new Inet6Address(null, him.him6.sin6_addr, scopeId == 0 ? -1 : scopeId); } /* fields common to AF_INET and AF_INET6 */ port = ntohs (GET_PORT (him)); socket.port = port; port = _this.localport; socket.localport = port; socket.address = socketAddressObj; } /* * Class: java_net_TwoStacksPlainSocketImpl * Method: socketAvailable * Signature: ()I */ static int socketAvailable(JNIEnv env, TwoStacksPlainSocketImpl _this) { int[] available = { -1 }; int res; FileDescriptor fdObj = _this.fd; cli.System.Net.Sockets.Socket fd; if (IS_NULL(fdObj)) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "Socket closed"); return -1; } else { fd = fdObj.getSocket(); } res = ioctlsocket(fd, FIONREAD, available); /* if result isn't 0, it means an error */ if (res != 0) { NET_ThrowNew(env, res, "socket available"); } return available[0]; } /* * Class: java_net_TwoStacksPlainSocketImpl * Method: socketClose * Signature: ()V */ static void socketClose0(JNIEnv env, TwoStacksPlainSocketImpl _this, boolean useDeferredClose) { FileDescriptor fdObj = _this.fd; FileDescriptor fd1Obj = _this.fd1; cli.System.Net.Sockets.Socket fd = null; cli.System.Net.Sockets.Socket fd1 = null; if (IS_NULL(fdObj) && IS_NULL(fd1Obj)) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "socket already closed"); return; } if (!IS_NULL(fdObj)) { fd = fdObj.getSocket(); } if (!IS_NULL(fd1Obj)) { fd1 = fd1Obj.getSocket(); } if (fd != null) { fdObj.setSocket(null); NET_SocketClose(fd); } if (fd1 != null) { fd1Obj.setSocket(null); NET_SocketClose(fd1); } } /* * Socket options for plainsocketImpl * * * Class: java_net_TwoStacksPlainSocketImpl * Method: socketSetOption * Signature: (IZLjava/lang/Object;)V */ static void socketSetOption(JNIEnv env, TwoStacksPlainSocketImpl _this, int cmd, boolean on, Object value) { cli.System.Net.Sockets.Socket fd, fd1; int[] level = new int[1]; int[] optname = new int[1]; Object optval; /* * Get SOCKET and check that it hasn't been closed */ fd = getFD(env, _this); fd1 = getFD1(env, _this); if (fd == null && fd1 == null) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "Socket closed"); return; } /* * SO_TIMEOUT is the socket option used to specify the timeout * for ServerSocket.accept and Socket.getInputStream().read. * It does not typically map to a native level socket option. * For Windows we special-case this and use the SOL_SOCKET/SO_RCVTIMEO * socket option to specify a receive timeout on the socket. This * receive timeout is applicable to Socket only and the socket * option should not be set on ServerSocket. */ if (cmd == java_net_SocketOptions_SO_TIMEOUT) { /* * Don't enable the socket option on ServerSocket as it's * meaningless (we don't receive on a ServerSocket). */ Object ssObj = _this.serverSocket; if (ssObj != NULL) { return; } /* * SO_RCVTIMEO is only supported on Microsoft's implementation * of Windows Sockets so if WSAENOPROTOOPT returned then * reset flag and timeout will be implemented using * select() -- see SocketInputStream.socketRead. */ if (isRcvTimeoutSupported) { int timeout = ((Integer)value).intValue(); /* * Disable SO_RCVTIMEO if timeout is <= 5 second. */ if (timeout <= 5000) { timeout = 0; } if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, timeout) < 0) { if (WSAGetLastError() == WSAENOPROTOOPT) { isRcvTimeoutSupported = JNI_FALSE; } else { NET_ThrowCurrent(env, "setsockopt SO_RCVTIMEO"); } } if (fd1 != null) { if (setsockopt(fd1, SOL_SOCKET, SO_RCVTIMEO, timeout) < 0) { NET_ThrowCurrent(env, "setsockopt SO_RCVTIMEO"); } } } return; } /* * Map the Java level socket option to the platform specific * level */ if (NET_MapSocketOption(cmd, level, optname) != 0) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "Invalid option"); return; } switch (cmd) { case java_net_SocketOptions_TCP_NODELAY : case java_net_SocketOptions_SO_OOBINLINE : case java_net_SocketOptions_SO_KEEPALIVE : case java_net_SocketOptions_SO_REUSEADDR : optval = on; break; case java_net_SocketOptions_SO_SNDBUF : case java_net_SocketOptions_SO_RCVBUF : case java_net_SocketOptions_IP_TOS : optval = ((Integer)value).intValue(); break; case java_net_SocketOptions_SO_LINGER : { linger ling = new linger(); if (on) { ling.l_onoff = 1; ling.l_linger = ((Integer)value).intValue(); } else { ling.l_onoff = 0; ling.l_linger = 0; } optval = ling; } break; default: /* shouldn't get here */ JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "Option not supported by TwoStacksPlainSocketImpl"); return; } if (fd != null) { if (NET_SetSockOpt(fd, level[0], optname[0], optval) < 0) { NET_ThrowCurrent(env, "setsockopt"); } } if (fd1 != null) { if (NET_SetSockOpt(fd1, level[0], optname[0], optval) < 0) { NET_ThrowCurrent(env, "setsockopt"); } } } /* * Class: java_net_TwoStacksPlainSocketImpl * Method: socketGetOption * Signature: (I)I */ static int socketGetOption(JNIEnv env, TwoStacksPlainSocketImpl _this, int opt, Object iaContainerObj) { cli.System.Net.Sockets.Socket fd, fd1; int[] level = new int[1]; int[] optname = new int[1]; Object optval; /* * Get SOCKET and check it hasn't been closed */ fd = getFD(env, _this); fd1 = getFD1(env, _this); if (fd == null && fd1 == null) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "Socket closed"); return -1; } if (fd == null) { fd = fd1; } /* For IPv6, we assume both sockets have the same setting always */ /* * SO_BINDADDR isn't a socket option */ if (opt == java_net_SocketOptions_SO_BINDADDR) { SOCKETADDRESS him; him = new SOCKETADDRESS(); int[] port = { 0 }; InetAddress iaObj; if (fd == null) { /* must be an IPV6 only socket. Case where both sockets are != -1 * is handled in java */ fd = getFD1 (env, _this); } if (getsockname(fd, him) < 0) { NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG+"SocketException", "Error getting socket name"); return -1; } iaObj = NET_SockaddrToInetAddress(him, port); ((InetAddressContainer)iaContainerObj).addr = iaObj; return 0; /* notice change from before */ } /* * Map the Java level socket option to the platform specific * level and option name. */ if (NET_MapSocketOption(opt, level, optname) != 0) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "Invalid option"); return -1; } /* * Args are int except for SO_LINGER */ if (opt == java_net_SocketOptions_SO_LINGER) { optval = new linger(); } else { optval = new int[1]; } if (NET_GetSockOpt(fd, level[0], optname[0], optval) < 0) { NET_ThrowCurrent(env, "getsockopt"); return -1; } switch (opt) { case java_net_SocketOptions_SO_LINGER: return (((linger)optval).l_onoff != 0 ? ((linger)optval).l_linger: -1); case java_net_SocketOptions_SO_SNDBUF: case java_net_SocketOptions_SO_RCVBUF: case java_net_SocketOptions_IP_TOS: return ((int[])optval)[0]; case java_net_SocketOptions_TCP_NODELAY : case java_net_SocketOptions_SO_OOBINLINE : case java_net_SocketOptions_SO_KEEPALIVE : case java_net_SocketOptions_SO_REUSEADDR : return (((int[])optval)[0] == 0) ? -1 : 1; default: /* shouldn't get here */ JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "Option not supported by TwoStacksPlainSocketImpl"); return -1; } } /* * Class: java_net_TwoStacksPlainSocketImpl * Method: socketShutdown * Signature: (I)V */ static void socketShutdown(JNIEnv env, TwoStacksPlainSocketImpl _this, int howto) { FileDescriptor fdObj = _this.fd; cli.System.Net.Sockets.Socket fd; /* * WARNING: THIS NEEDS LOCKING. ALSO: SHOULD WE CHECK for fd being * -1 already? */ if (IS_NULL(fdObj)) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "socket already closed"); return; } else { fd = fdObj.getSocket(); } shutdown(fd, howto); } /* * Class: java_net_TwoStacksPlainSocketImpl * Method: socketSendUrgentData * Signature: (B)V */ static void socketSendUrgentData(JNIEnv env, TwoStacksPlainSocketImpl _this, int data) { /* The fd field */ FileDescriptor fdObj = _this.fd; int n; cli.System.Net.Sockets.Socket fd; if (IS_NULL(fdObj)) { JNU_ThrowByName(env, "java/net/SocketException", "Socket closed"); return; } else { fd = fdObj.getSocket(); /* Bug 4086704 - If the Socket associated with this file descriptor * was closed (sysCloseFD), the the file descriptor is set to -1. */ if (fd == null) { JNU_ThrowByName(env, "java/net/SocketException", "Socket closed"); return; } } n = send(fd, new byte[] { (byte)data }, 1, MSG_OOB); if (n == JVM_IO_ERR) { NET_ThrowCurrent(env, "send"); return; } if (n == JVM_IO_INTR) { JNU_ThrowByName(env, "java/io/InterruptedIOException", null); return; } } }