| /* |
| * Copyright (c) 1997, 2011, 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. |
| */ |
| |
| #include <errno.h> |
| #include <string.h> |
| #include <sys/types.h> |
| #include <sys/socket.h> |
| #if defined(__linux__) && !defined(USE_SELECT) |
| #include <sys/poll.h> |
| #endif |
| #include <netinet/tcp.h> /* Defines TCP_NODELAY, needed for 2.6 */ |
| #include <netinet/in.h> |
| #ifdef __linux__ |
| #include <netinet/ip.h> |
| #endif |
| #include <netdb.h> |
| #include <stdlib.h> |
| |
| #ifdef __solaris__ |
| #include <fcntl.h> |
| #endif |
| #ifdef __linux__ |
| #include <unistd.h> |
| //#include <sys/sysctl.h> |
| #endif |
| |
| #include "jvm.h" |
| #include "jni_util.h" |
| #include "net_util.h" |
| |
| #include "java_net_SocketOptions.h" |
| #include "java_net_PlainSocketImpl.h" |
| #include "JNIHelp.h" |
| |
| #define NATIVE_METHOD(className, functionName, signature) \ |
| { #functionName, signature, (void*)(className ## _ ## functionName) } |
| |
| /************************************************************************ |
| * PlainSocketImpl |
| */ |
| |
| static jfieldID IO_fd_fdID; |
| |
| jfieldID psi_fdID; |
| jfieldID psi_addressID; |
| jfieldID psi_ipaddressID; |
| jfieldID psi_portID; |
| jfieldID psi_localportID; |
| jfieldID psi_timeoutID; |
| jfieldID psi_trafficClassID; |
| jfieldID psi_serverSocketID; |
| jfieldID psi_fdLockID; |
| jfieldID psi_closePendingID; |
| |
| extern void setDefaultScopeID(JNIEnv *env, struct sockaddr *him); |
| |
| |
| #define SET_NONBLOCKING(fd) { \ |
| int flags = fcntl(fd, F_GETFL); \ |
| flags |= O_NONBLOCK; \ |
| fcntl(fd, F_SETFL, flags); \ |
| } |
| |
| #define SET_BLOCKING(fd) { \ |
| int flags = fcntl(fd, F_GETFL); \ |
| flags &= ~O_NONBLOCK; \ |
| fcntl(fd, F_SETFL, flags); \ |
| } |
| |
| /* |
| * Return the file descriptor given a PlainSocketImpl |
| */ |
| static int getFD(JNIEnv *env, jobject this) { |
| jobject fdObj = (*env)->GetObjectField(env, this, psi_fdID); |
| CHECK_NULL_RETURN(fdObj, -1); |
| return (*env)->GetIntField(env, fdObj, IO_fd_fdID); |
| } |
| |
| static void PlainSocketImpl_initProto(JNIEnv *env) { |
| jclass cls = (*env)->FindClass(env, "java/net/PlainSocketImpl"); |
| psi_fdID = (*env)->GetFieldID(env, cls , "fd", |
| "Ljava/io/FileDescriptor;"); |
| CHECK_NULL(psi_fdID); |
| 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_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); |
| psi_fdLockID = (*env)->GetFieldID(env, cls, "fdLock", |
| "Ljava/lang/Object;"); |
| CHECK_NULL(psi_fdLockID); |
| psi_closePendingID = (*env)->GetFieldID(env, cls, "closePending", "Z"); |
| CHECK_NULL(psi_closePendingID); |
| IO_fd_fdID = NET_GetFileDescriptorID(env); |
| CHECK_NULL(IO_fd_fdID); |
| |
| } |
| |
| /* a global reference to the java.net.SocketException class. In |
| * socketCreate, we ensure that this is initialized. This is to |
| * prevent the problem where socketCreate runs out of file |
| * descriptors, and is then unable to load the exception class. |
| */ |
| static jclass socketExceptionCls; |
| |
| /* |
| * Class: java_net_PlainSocketImpl |
| * Method: socketCreate |
| * Signature: (Z)V */ |
| JNIEXPORT void JNICALL |
| PlainSocketImpl_socketCreate(JNIEnv *env, jobject this, |
| jboolean stream) { |
| jobject fdObj, ssObj; |
| int fd; |
| int type = (stream ? SOCK_STREAM : SOCK_DGRAM); |
| #ifdef AF_INET6 |
| int domain = ipv6_available() ? AF_INET6 : AF_INET; |
| #else |
| int domain = AF_INET; |
| #endif |
| |
| if (socketExceptionCls == NULL) { |
| jclass c = (*env)->FindClass(env, "java/net/SocketException"); |
| CHECK_NULL(c); |
| socketExceptionCls = (jclass)(*env)->NewGlobalRef(env, c); |
| CHECK_NULL(socketExceptionCls); |
| } |
| fdObj = (*env)->GetObjectField(env, this, psi_fdID); |
| |
| if (fdObj == NULL) { |
| (*env)->ThrowNew(env, socketExceptionCls, "null fd object"); |
| return; |
| } |
| |
| if ((fd = JVM_Socket(domain, type, 0)) == JVM_IO_ERR) { |
| /* note: if you run out of fds, you may not be able to load |
| * the exception class, and get a NoClassDefFoundError |
| * instead. |
| */ |
| NET_ThrowNew(env, errno, "can't create socket"); |
| return; |
| } |
| tagSocket(env, fd); |
| |
| #ifdef AF_INET6 |
| /* Disable IPV6_V6ONLY to ensure dual-socket support */ |
| if (domain == AF_INET6) { |
| int arg = 0; |
| if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&arg, |
| sizeof(int)) < 0) { |
| NET_ThrowNew(env, errno, "cannot set IPPROTO_IPV6"); |
| untagSocket(env, fd); |
| close(fd); |
| return; |
| } |
| } |
| #endif /* AF_INET6 */ |
| |
| /* |
| * If this is a server socket then enable SO_REUSEADDR |
| * automatically and set to non blocking. |
| */ |
| ssObj = (*env)->GetObjectField(env, this, psi_serverSocketID); |
| if (ssObj != NULL) { |
| int arg = 1; |
| SET_NONBLOCKING(fd); |
| if (JVM_SetSockOpt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&arg, |
| sizeof(arg)) < 0) { |
| NET_ThrowNew(env, errno, "cannot set SO_REUSEADDR"); |
| untagSocket(env, fd); |
| close(fd); |
| return; |
| } |
| } |
| |
| (*env)->SetIntField(env, fdObj, IO_fd_fdID, fd); |
| } |
| |
| /* |
| * inetAddress is the address object passed to the socket connect |
| * call. |
| * |
| * Class: java_net_PlainSocketImpl |
| * Method: socketConnect |
| * Signature: (Ljava/net/InetAddress;I)V |
| */ |
| JNIEXPORT void JNICALL |
| PlainSocketImpl_socketConnect(JNIEnv *env, jobject this, |
| jobject iaObj, jint port, |
| jint timeout) |
| { |
| jint localport = (*env)->GetIntField(env, this, psi_localportID); |
| int len = 0; |
| |
| /* fdObj is the FileDescriptor field on this */ |
| jobject fdObj = (*env)->GetObjectField(env, this, psi_fdID); |
| |
| jclass clazz = (*env)->GetObjectClass(env, this); |
| |
| jobject fdLock; |
| |
| jint trafficClass = (*env)->GetIntField(env, this, psi_trafficClassID); |
| |
| /* fd is an int field on iaObj */ |
| jint fd; |
| |
| SOCKADDR him; |
| /* The result of the connection */ |
| int connect_rv = -1; |
| |
| if (IS_NULL(fdObj)) { |
| JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed"); |
| return; |
| } else { |
| fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID); |
| } |
| if (IS_NULL(iaObj)) { |
| JNU_ThrowNullPointerException(env, "inet address argument null."); |
| return; |
| } |
| |
| /* connect */ |
| if (NET_InetAddressToSockaddr(env, iaObj, port, (struct sockaddr *)&him, &len, JNI_TRUE) != 0) { |
| return; |
| } |
| setDefaultScopeID(env, (struct sockaddr *)&him); |
| |
| #ifdef AF_INET6 |
| if (trafficClass != 0 && ipv6_available()) { |
| NET_SetTrafficClass((struct sockaddr *)&him, trafficClass); |
| } |
| #endif /* AF_INET6 */ |
| if (timeout <= 0) { |
| connect_rv = NET_Connect(fd, (struct sockaddr *)&him, len); |
| #ifdef __solaris__ |
| if (connect_rv == JVM_IO_ERR && errno == EINPROGRESS ) { |
| |
| /* This can happen if a blocking connect is interrupted by a signal. |
| * See 6343810. |
| */ |
| while (1) { |
| #ifndef USE_SELECT |
| { |
| struct pollfd pfd; |
| pfd.fd = fd; |
| pfd.events = POLLOUT; |
| |
| connect_rv = NET_Poll(&pfd, 1, -1); |
| } |
| #else |
| { |
| fd_set wr, ex; |
| |
| FD_ZERO(&wr); |
| FD_SET(fd, &wr); |
| FD_ZERO(&ex); |
| FD_SET(fd, &ex); |
| |
| connect_rv = NET_Select(fd+1, 0, &wr, &ex, 0); |
| } |
| #endif |
| |
| if (connect_rv == JVM_IO_ERR) { |
| if (errno == EINTR) { |
| continue; |
| } else { |
| break; |
| } |
| } |
| if (connect_rv > 0) { |
| int optlen; |
| /* has connection been established */ |
| optlen = sizeof(connect_rv); |
| if (JVM_GetSockOpt(fd, SOL_SOCKET, SO_ERROR, |
| (void*)&connect_rv, &optlen) <0) { |
| connect_rv = errno; |
| } |
| |
| if (connect_rv != 0) { |
| /* restore errno */ |
| errno = connect_rv; |
| connect_rv = JVM_IO_ERR; |
| } |
| break; |
| } |
| } |
| } |
| #endif |
| } else { |
| /* |
| * A timeout was specified. We put the socket into non-blocking |
| * mode, connect, and then wait for the connection to be |
| * established, fail, or timeout. |
| */ |
| SET_NONBLOCKING(fd); |
| |
| /* no need to use NET_Connect as non-blocking */ |
| connect_rv = connect(fd, (struct sockaddr *)&him, len); |
| |
| /* connection not established immediately */ |
| if (connect_rv != 0) { |
| int optlen; |
| jlong prevTime = JVM_CurrentTimeMillis(env, 0); |
| |
| if (errno != EINPROGRESS) { |
| NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "ConnectException", |
| "connect failed"); |
| SET_BLOCKING(fd); |
| return; |
| } |
| |
| /* |
| * Wait for the connection to be established or a |
| * timeout occurs. poll/select needs to handle EINTR in |
| * case lwp sig handler redirects any process signals to |
| * this thread. |
| */ |
| while (1) { |
| jlong newTime; |
| #ifndef USE_SELECT |
| { |
| struct pollfd pfd; |
| pfd.fd = fd; |
| pfd.events = POLLOUT; |
| |
| errno = 0; |
| connect_rv = NET_Poll(&pfd, 1, timeout); |
| } |
| #else |
| { |
| fd_set wr, ex; |
| struct timeval t; |
| |
| t.tv_sec = timeout / 1000; |
| t.tv_usec = (timeout % 1000) * 1000; |
| |
| FD_ZERO(&wr); |
| FD_SET(fd, &wr); |
| FD_ZERO(&ex); |
| FD_SET(fd, &ex); |
| |
| errno = 0; |
| connect_rv = NET_Select(fd+1, 0, &wr, &ex, &t); |
| } |
| #endif |
| |
| if (connect_rv >= 0) { |
| break; |
| } |
| if (errno != EINTR) { |
| break; |
| } |
| |
| /* |
| * The poll was interrupted so adjust timeout and |
| * restart |
| */ |
| newTime = JVM_CurrentTimeMillis(env, 0); |
| timeout -= (newTime - prevTime); |
| if (timeout <= 0) { |
| connect_rv = 0; |
| break; |
| } |
| prevTime = newTime; |
| |
| } /* while */ |
| |
| if (connect_rv == 0) { |
| JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException", |
| "connect timed out"); |
| |
| /* |
| * Timeout out but connection may still be established. |
| * At the high level it should be closed immediately but |
| * just in case we make the socket blocking again and |
| * shutdown input & output. |
| */ |
| SET_BLOCKING(fd); |
| JVM_SocketShutdown(fd, 2); |
| return; |
| } |
| |
| /* has connection been established */ |
| optlen = sizeof(connect_rv); |
| if (JVM_GetSockOpt(fd, SOL_SOCKET, SO_ERROR, (void*)&connect_rv, |
| &optlen) <0) { |
| connect_rv = errno; |
| } |
| } |
| |
| /* make socket blocking again */ |
| SET_BLOCKING(fd); |
| |
| /* restore errno */ |
| if (connect_rv != 0) { |
| errno = connect_rv; |
| connect_rv = JVM_IO_ERR; |
| } |
| } |
| |
| /* report the appropriate exception */ |
| if (connect_rv < 0) { |
| |
| #ifdef __linux__ |
| /* |
| * Linux/GNU distribution setup /etc/hosts so that |
| * InetAddress.getLocalHost gets back the loopback address |
| * rather than the host address. Thus a socket can be |
| * bound to the loopback address and the connect will |
| * fail with EADDRNOTAVAIL. In addition the Linux kernel |
| * returns the wrong error in this case - it returns EINVAL |
| * instead of EADDRNOTAVAIL. We handle this here so that |
| * a more descriptive exception text is used. |
| */ |
| if (connect_rv == JVM_IO_ERR && errno == EINVAL) { |
| JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", |
| "Invalid argument or cannot assign requested address"); |
| return; |
| } |
| #endif |
| if (connect_rv == JVM_IO_INTR) { |
| JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException", |
| "operation interrupted"); |
| #if defined(EPROTO) |
| } else if (errno == EPROTO) { |
| NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "ProtocolException", |
| "Protocol error"); |
| #endif |
| } else if (errno == ECONNREFUSED) { |
| NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "ConnectException", |
| "Connection refused"); |
| } else if (errno == ETIMEDOUT) { |
| NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "ConnectException", |
| "Connection timed out"); |
| } else if (errno == EHOSTUNREACH) { |
| NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "NoRouteToHostException", |
| "Host unreachable"); |
| } else if (errno == EADDRNOTAVAIL) { |
| NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "NoRouteToHostException", |
| "Address not available"); |
| } else if ((errno == EISCONN) || (errno == EBADF)) { |
| JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", |
| "Socket closed"); |
| } else { |
| NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "connect failed"); |
| } |
| return; |
| } |
| |
| (*env)->SetIntField(env, fdObj, IO_fd_fdID, fd); |
| |
| /* set the remote peer address and port */ |
| (*env)->SetObjectField(env, this, psi_addressID, iaObj); |
| (*env)->SetIntField(env, this, psi_portID, 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. |
| */ |
| len = SOCKADDR_LEN; |
| if (JVM_GetSockName(fd, (struct sockaddr *)&him, &len) == -1) { |
| NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", |
| "Error getting socket name"); |
| } else { |
| localport = NET_GetPortFromSockaddr((struct sockaddr *)&him); |
| (*env)->SetIntField(env, this, psi_localportID, localport); |
| } |
| } |
| } |
| |
| /* |
| * Class: java_net_PlainSocketImpl |
| * Method: socketBind |
| * Signature: (Ljava/net/InetAddress;I)V |
| */ |
| JNIEXPORT void JNICALL |
| PlainSocketImpl_socketBind(JNIEnv *env, jobject this, |
| jobject iaObj, jint localport) { |
| |
| /* fdObj is the FileDescriptor field on this */ |
| jobject fdObj = (*env)->GetObjectField(env, this, psi_fdID); |
| /* fd is an int field on fdObj */ |
| int fd; |
| int len; |
| SOCKADDR him; |
| |
| if (IS_NULL(fdObj)) { |
| JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", |
| "Socket closed"); |
| return; |
| } else { |
| fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID); |
| } |
| if (IS_NULL(iaObj)) { |
| JNU_ThrowNullPointerException(env, "iaObj is null."); |
| return; |
| } |
| |
| /* bind */ |
| if (NET_InetAddressToSockaddr(env, iaObj, localport, (struct sockaddr *)&him, &len, JNI_TRUE) != 0) { |
| return; |
| } |
| setDefaultScopeID(env, (struct sockaddr *)&him); |
| |
| if (NET_Bind(fd, (struct sockaddr *)&him, len) < 0) { |
| if (errno == EADDRINUSE || errno == EADDRNOTAVAIL || |
| errno == EPERM || errno == EACCES) { |
| NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "BindException", |
| "Bind failed"); |
| } else { |
| NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", |
| "Bind failed"); |
| } |
| return; |
| } |
| |
| /* set the address */ |
| (*env)->SetObjectField(env, this, psi_addressID, iaObj); |
| |
| /* intialize the local port */ |
| 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 (JVM_GetSockName(fd, (struct sockaddr *)&him, &len) == -1) { |
| NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", |
| "Error getting socket name"); |
| return; |
| } |
| localport = NET_GetPortFromSockaddr((struct sockaddr *)&him); |
| (*env)->SetIntField(env, this, psi_localportID, localport); |
| } else { |
| (*env)->SetIntField(env, this, psi_localportID, localport); |
| } |
| } |
| |
| /* |
| * Class: java_net_PlainSocketImpl |
| * Method: socketListen |
| * Signature: (I)V |
| */ |
| JNIEXPORT void JNICALL |
| PlainSocketImpl_socketListen (JNIEnv *env, jobject this, |
| jint count) |
| { |
| /* this FileDescriptor fd field */ |
| jobject fdObj = (*env)->GetObjectField(env, this, psi_fdID); |
| /* fdObj's int fd field */ |
| int fd; |
| |
| if (IS_NULL(fdObj)) { |
| JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", |
| "Socket closed"); |
| return; |
| } else { |
| fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID); |
| } |
| |
| /* |
| * Workaround for bugid 4101691 in Solaris 2.6. See 4106600. |
| * If listen backlog is Integer.MAX_VALUE then subtract 1. |
| */ |
| if (count == 0x7fffffff) |
| count -= 1; |
| |
| if (JVM_Listen(fd, count) == JVM_IO_ERR) { |
| NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", |
| "Listen failed"); |
| } |
| } |
| |
| /* |
| * Class: java_net_PlainSocketImpl |
| * Method: socketAccept |
| * Signature: (Ljava/net/SocketImpl;)V |
| */ |
| JNIEXPORT void JNICALL |
| PlainSocketImpl_socketAccept(JNIEnv *env, jobject this, |
| jobject socket) |
| { |
| /* fields on this */ |
| int port; |
| jint timeout = (*env)->GetIntField(env, this, psi_timeoutID); |
| jlong prevTime = 0; |
| jobject fdObj = (*env)->GetObjectField(env, this, psi_fdID); |
| |
| /* the FileDescriptor field on socket */ |
| jobject socketFdObj; |
| /* the InetAddress field on socket */ |
| jobject socketAddressObj; |
| |
| /* the ServerSocket fd int field on fdObj */ |
| jint fd; |
| |
| /* accepted fd */ |
| jint newfd; |
| |
| SOCKADDR him; |
| int len; |
| |
| len = SOCKADDR_LEN; |
| |
| if (IS_NULL(fdObj)) { |
| JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", |
| "Socket closed"); |
| return; |
| } else { |
| fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID); |
| } |
| if (IS_NULL(socket)) { |
| JNU_ThrowNullPointerException(env, "socket is null"); |
| return; |
| } |
| |
| /* |
| * accept connection but ignore ECONNABORTED indicating that |
| * connection was eagerly accepted by the OS but was reset |
| * before accept() was called. |
| * |
| * If accept timeout in place and timeout is adjusted with |
| * each ECONNABORTED or EWOULDBLOCK to ensure that semantics |
| * of timeout are preserved. |
| */ |
| for (;;) { |
| int ret; |
| |
| /* first usage pick up current time */ |
| if (prevTime == 0 && timeout > 0) { |
| prevTime = JVM_CurrentTimeMillis(env, 0); |
| } |
| |
| /* passing a timeout of 0 to poll will return immediately, |
| but in the case of ServerSocket 0 means infinite. */ |
| if (timeout <= 0) { |
| ret = NET_Timeout(fd, -1); |
| } else { |
| ret = NET_Timeout(fd, timeout); |
| } |
| |
| if (ret == 0) { |
| JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException", |
| "Accept timed out"); |
| return; |
| } else if (ret == JVM_IO_ERR) { |
| if (errno == EBADF) { |
| JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed"); |
| } else { |
| NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "Accept failed"); |
| } |
| return; |
| } else if (ret == JVM_IO_INTR) { |
| JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException", |
| "operation interrupted"); |
| return; |
| } |
| |
| newfd = NET_Accept(fd, (struct sockaddr *)&him, (jint*)&len); |
| |
| /* connection accepted */ |
| if (newfd >= 0) { |
| SET_BLOCKING(newfd); |
| break; |
| } |
| |
| /* non (ECONNABORTED or EWOULDBLOCK) error */ |
| if (!(errno == ECONNABORTED || errno == EWOULDBLOCK)) { |
| break; |
| } |
| |
| /* ECONNABORTED or EWOULDBLOCK error so adjust timeout if there is one. */ |
| if (timeout) { |
| jlong currTime = JVM_CurrentTimeMillis(env, 0); |
| timeout -= (currTime - prevTime); |
| |
| if (timeout <= 0) { |
| JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException", |
| "Accept timed out"); |
| return; |
| } |
| prevTime = currTime; |
| } |
| } |
| |
| if (newfd < 0) { |
| if (newfd == -2) { |
| JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException", |
| "operation interrupted"); |
| } else { |
| if (errno == EINVAL) { |
| errno = EBADF; |
| } |
| if (errno == EBADF) { |
| JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed"); |
| } else { |
| NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "Accept failed"); |
| } |
| } |
| return; |
| } |
| |
| /* |
| * fill up the remote peer port and address in the new socket structure. |
| */ |
| socketAddressObj = NET_SockaddrToInetAddress(env, (struct sockaddr *)&him, &port); |
| if (socketAddressObj == NULL) { |
| /* should be pending exception */ |
| untagSocket(env, fd); |
| close(newfd); |
| return; |
| } |
| |
| /* |
| * Populate SocketImpl.fd.fd |
| */ |
| socketFdObj = (*env)->GetObjectField(env, socket, psi_fdID); |
| (*env)->SetIntField(env, socketFdObj, IO_fd_fdID, newfd); |
| |
| (*env)->SetObjectField(env, socket, psi_addressID, socketAddressObj); |
| (*env)->SetIntField(env, socket, psi_portID, port); |
| /* also fill up the local port information */ |
| port = (*env)->GetIntField(env, this, psi_localportID); |
| (*env)->SetIntField(env, socket, psi_localportID, port); |
| } |
| |
| |
| /* |
| * Class: java_net_PlainSocketImpl |
| * Method: socketAvailable |
| * Signature: ()I |
| */ |
| JNIEXPORT jint JNICALL |
| PlainSocketImpl_socketAvailable(JNIEnv *env, jobject this) { |
| |
| jint ret = -1; |
| jobject fdObj = (*env)->GetObjectField(env, this, psi_fdID); |
| jint fd; |
| |
| if (IS_NULL(fdObj)) { |
| JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", |
| "Socket closed"); |
| return -1; |
| } else { |
| fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID); |
| } |
| /* JVM_SocketAvailable returns 0 for failure, 1 for success */ |
| if (!JVM_SocketAvailable(fd, &ret)){ |
| if (errno == ECONNRESET) { |
| JNU_ThrowByName(env, "sun/net/ConnectionResetException", ""); |
| } else { |
| NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", |
| "ioctl FIONREAD failed"); |
| } |
| } |
| return ret; |
| } |
| |
| /* |
| * Class: java_net_PlainSocketImpl |
| * Method: socketClose0 |
| * Signature: ()V |
| */ |
| JNIEXPORT void JNICALL |
| PlainSocketImpl_socketClose0(JNIEnv *env, jobject this) { |
| |
| jobject fdObj = (*env)->GetObjectField(env, this, psi_fdID); |
| jint fd; |
| |
| if (IS_NULL(fdObj)) { |
| JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", |
| "socket already closed"); |
| return; |
| } else { |
| fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID); |
| } |
| if (fd != -1) { |
| (*env)->SetIntField(env, fdObj, IO_fd_fdID, -1); |
| untagSocket(env, fd); |
| NET_SocketClose(fd); |
| } |
| } |
| |
| /* |
| * Class: java_net_PlainSocketImpl |
| * Method: socketShutdown |
| * Signature: (I)V |
| */ |
| JNIEXPORT void JNICALL |
| PlainSocketImpl_socketShutdown(JNIEnv *env, jobject this, |
| jint howto) |
| { |
| |
| jobject fdObj = (*env)->GetObjectField(env, this, psi_fdID); |
| jint 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 = (*env)->GetIntField(env, fdObj, IO_fd_fdID); |
| } |
| JVM_SocketShutdown(fd, howto); |
| } |
| |
| |
| /* |
| * Class: java_net_PlainSocketImpl |
| * Method: socketSetOption |
| * Signature: (IZLjava/lang/Object;)V |
| */ |
| JNIEXPORT void JNICALL |
| PlainSocketImpl_socketSetOption(JNIEnv *env, jobject this, |
| jint cmd, jboolean on, |
| jobject value) { |
| int fd; |
| int level, optname, optlen; |
| union { |
| int i; |
| struct linger ling; |
| } optval; |
| |
| /* |
| * Check that socket hasn't been closed |
| */ |
| fd = getFD(env, this); |
| if (fd < 0) { |
| JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", |
| "Socket closed"); |
| return; |
| } |
| |
| /* |
| * SO_TIMEOUT is a no-op on Solaris/Linux |
| */ |
| if (cmd == java_net_SocketOptions_SO_TIMEOUT) { |
| return; |
| } |
| |
| /* |
| * Map the Java level socket option to the platform specific |
| * level and option name. |
| */ |
| if (NET_MapSocketOption(cmd, &level, &optname)) { |
| JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Invalid option"); |
| return; |
| } |
| |
| switch (cmd) { |
| case java_net_SocketOptions_SO_SNDBUF : |
| case java_net_SocketOptions_SO_RCVBUF : |
| case java_net_SocketOptions_SO_LINGER : |
| case java_net_SocketOptions_IP_TOS : |
| { |
| jclass cls; |
| jfieldID fid; |
| |
| cls = (*env)->FindClass(env, "java/lang/Integer"); |
| CHECK_NULL(cls); |
| fid = (*env)->GetFieldID(env, cls, "value", "I"); |
| CHECK_NULL(fid); |
| |
| if (cmd == java_net_SocketOptions_SO_LINGER) { |
| if (on) { |
| optval.ling.l_onoff = 1; |
| optval.ling.l_linger = (*env)->GetIntField(env, value, fid); |
| } else { |
| optval.ling.l_onoff = 0; |
| optval.ling.l_linger = 0; |
| } |
| optlen = sizeof(optval.ling); |
| } else { |
| optval.i = (*env)->GetIntField(env, value, fid); |
| optlen = sizeof(optval.i); |
| } |
| |
| break; |
| } |
| |
| /* Boolean -> int */ |
| default : |
| optval.i = (on ? 1 : 0); |
| optlen = sizeof(optval.i); |
| |
| } |
| |
| if (NET_SetSockOpt(fd, level, optname, (const void *)&optval, optlen) < 0) { |
| #ifdef __solaris__ |
| if (errno == EINVAL) { |
| // On Solaris setsockopt will set errno to EINVAL if the socket |
| // is closed. The default error message is then confusing |
| char fullMsg[128]; |
| jio_snprintf(fullMsg, sizeof(fullMsg), "Invalid option or socket reset by remote peer"); |
| JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", fullMsg); |
| return; |
| } |
| #endif /* __solaris__ */ |
| NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", |
| "Error setting socket option"); |
| } |
| } |
| |
| /* |
| * Class: java_net_PlainSocketImpl |
| * Method: socketGetOption |
| * Signature: (I)I |
| */ |
| JNIEXPORT jint JNICALL |
| PlainSocketImpl_socketGetOption(JNIEnv *env, jobject this, |
| jint cmd, jobject iaContainerObj) { |
| |
| int fd; |
| int level, optname, optlen; |
| union { |
| int i; |
| struct linger ling; |
| } optval; |
| |
| /* |
| * Check that socket hasn't been closed |
| */ |
| fd = getFD(env, this); |
| if (fd < 0) { |
| JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", |
| "Socket closed"); |
| return -1; |
| } |
| |
| /* |
| * SO_BINDADDR isn't a socket option |
| */ |
| if (cmd == java_net_SocketOptions_SO_BINDADDR) { |
| SOCKADDR him; |
| socklen_t len = 0; |
| int port; |
| jobject iaObj; |
| jclass iaCntrClass; |
| jfieldID iaFieldID; |
| |
| len = SOCKADDR_LEN; |
| |
| if (getsockname(fd, (struct sockaddr *)&him, &len) < 0) { |
| NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", |
| "Error getting socket name"); |
| return -1; |
| } |
| iaObj = NET_SockaddrToInetAddress(env, (struct sockaddr *)&him, &port); |
| CHECK_NULL_RETURN(iaObj, -1); |
| |
| iaCntrClass = (*env)->GetObjectClass(env, iaContainerObj); |
| iaFieldID = (*env)->GetFieldID(env, iaCntrClass, "addr", "Ljava/net/InetAddress;"); |
| CHECK_NULL_RETURN(iaFieldID, -1); |
| (*env)->SetObjectField(env, iaContainerObj, iaFieldID, iaObj); |
| return 0; /* notice change from before */ |
| } |
| |
| /* |
| * Map the Java level socket option to the platform specific |
| * level and option name. |
| */ |
| if (NET_MapSocketOption(cmd, &level, &optname)) { |
| JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Invalid option"); |
| return -1; |
| } |
| |
| /* |
| * Args are int except for SO_LINGER |
| */ |
| if (cmd == java_net_SocketOptions_SO_LINGER) { |
| optlen = sizeof(optval.ling); |
| } else { |
| optlen = sizeof(optval.i); |
| } |
| |
| if (NET_GetSockOpt(fd, level, optname, (void *)&optval, &optlen) < 0) { |
| NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", |
| "Error getting socket option"); |
| return -1; |
| } |
| |
| switch (cmd) { |
| case java_net_SocketOptions_SO_LINGER: |
| return (optval.ling.l_onoff ? optval.ling.l_linger: -1); |
| |
| case java_net_SocketOptions_SO_SNDBUF: |
| case java_net_SocketOptions_SO_RCVBUF: |
| case java_net_SocketOptions_IP_TOS: |
| return optval.i; |
| |
| default : |
| return (optval.i == 0) ? -1 : 1; |
| } |
| } |
| |
| |
| /* |
| * Class: java_net_PlainSocketImpl |
| * Method: socketSendUrgentData |
| * Signature: (B)V |
| */ |
| JNIEXPORT void JNICALL |
| PlainSocketImpl_socketSendUrgentData(JNIEnv *env, jobject this, |
| jint data) { |
| /* The fd field */ |
| jobject fdObj = (*env)->GetObjectField(env, this, psi_fdID); |
| int n, fd; |
| unsigned char d = data & 0xFF; |
| |
| if (IS_NULL(fdObj)) { |
| JNU_ThrowByName(env, "java/net/SocketException", "Socket closed"); |
| return; |
| } else { |
| fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID); |
| /* Bug 4086704 - If the Socket associated with this file descriptor |
| * was closed (sysCloseFD), the the file descriptor is set to -1. |
| */ |
| if (fd == -1) { |
| JNU_ThrowByName(env, "java/net/SocketException", "Socket closed"); |
| return; |
| } |
| |
| } |
| n = JVM_Send(fd, (char *)&d, 1, MSG_OOB); |
| if (n == JVM_IO_ERR) { |
| NET_ThrowByNameWithLastError(env, "java/io/IOException", "Write failed"); |
| return; |
| } |
| if (n == JVM_IO_INTR) { |
| JNU_ThrowByName(env, "java/io/InterruptedIOException", 0); |
| return; |
| } |
| } |
| |
| static JNINativeMethod gMethods[] = { |
| NATIVE_METHOD(PlainSocketImpl, socketSendUrgentData, "(I)V"), |
| NATIVE_METHOD(PlainSocketImpl, socketGetOption, "(ILjava/lang/Object;)I"), |
| NATIVE_METHOD(PlainSocketImpl, socketSetOption, "(IZLjava/lang/Object;)V"), |
| NATIVE_METHOD(PlainSocketImpl, socketShutdown, "(I)V"), |
| NATIVE_METHOD(PlainSocketImpl, socketClose0, "()V"), |
| NATIVE_METHOD(PlainSocketImpl, socketAccept, "(Ljava/net/SocketImpl;)V"), |
| NATIVE_METHOD(PlainSocketImpl, socketAvailable, "()I"), |
| NATIVE_METHOD(PlainSocketImpl, socketListen, "(I)V"), |
| NATIVE_METHOD(PlainSocketImpl, socketBind, "(Ljava/net/InetAddress;I)V"), |
| NATIVE_METHOD(PlainSocketImpl, socketConnect, "(Ljava/net/InetAddress;II)V"), |
| NATIVE_METHOD(PlainSocketImpl, socketCreate, "(Z)V"), |
| }; |
| |
| void register_java_net_PlainSocketImpl(JNIEnv* env) { |
| jniRegisterNativeMethods(env, "java/net/PlainSocketImpl", gMethods, NELEM(gMethods)); |
| PlainSocketImpl_initProto(env); |
| } |