/* * Copyright (c) 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.*; final class DualStackPlainSocketImpl_c { private static final int JVM_IO_ERR = -1; private static final int JVM_IO_INTR = -2; private static final int SET_BLOCKING = 0; private static final int SET_NONBLOCKING = 1; /* #include #include #include "jni.h" #include "net_util.h" #include "java_net_DualStackPlainSocketImpl.h" #define SET_BLOCKING 0 #define SET_NONBLOCKING 1 static jclass isa_class; /* java.net.InetSocketAddress *-/ static jmethodID isa_ctorID; /* InetSocketAddress(InetAddress, int) *-/ /* * Class: java_net_DualStackPlainSocketImpl * Method: initIDs * Signature: ()V *-/ JNIEXPORT void JNICALL Java_java_net_DualStackPlainSocketImpl_initIDs (JNIEnv *env, jclass clazz) { jclass cls = (*env)->FindClass(env, "java/net/InetSocketAddress"); isa_class = (*env)->NewGlobalRef(env, cls); isa_ctorID = (*env)->GetMethodID(env, cls, "", "(Ljava/net/InetAddress;I)V"); // implement read timeout with select. isRcvTimeoutSupported = 0; } /* * Class: java_net_DualStackPlainSocketImpl * Method: socket0 * Signature: (ZZ)I */ static cli.System.Net.Sockets.Socket socket0 (JNIEnv env, boolean stream, boolean v6Only /*unused*/) { cli.System.Net.Sockets.Socket fd; int rv, opt=0; fd = NET_Socket(AF_INET6, (stream ? SOCK_STREAM : SOCK_DGRAM), 0); if (fd == INVALID_SOCKET) { NET_ThrowNew(env, WSAGetLastError(), "create"); return null; } rv = setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, opt); if (rv == SOCKET_ERROR) { NET_ThrowNew(env, WSAGetLastError(), "create"); } return fd; } /* * Class: java_net_DualStackPlainSocketImpl * Method: bind0 * Signature: (ILjava/net/InetAddress;I)V */ static void bind0 (JNIEnv env, cli.System.Net.Sockets.Socket fd, InetAddress iaObj, int port, boolean exclBind) { SOCKETADDRESS sa; sa = new SOCKETADDRESS(); int rv; if (NET_InetAddressToSockaddr(env, iaObj, port, sa, JNI_TRUE) != 0) { return; } rv = NET_WinBind(fd, sa, exclBind); if (rv == SOCKET_ERROR) NET_ThrowNew(env, WSAGetLastError(), "JVM_Bind"); } /* * Class: java_net_DualStackPlainSocketImpl * Method: connect0 * Signature: (ILjava/net/InetAddress;I)I */ static int connect0 (JNIEnv env, cli.System.Net.Sockets.Socket fd, InetAddress iaObj, int port) { SOCKETADDRESS sa; sa = new SOCKETADDRESS(); int rv; if (NET_InetAddressToSockaddr(env, iaObj, port, sa, JNI_TRUE) != 0) { return -1; } rv = connect(fd, sa); if (rv == SOCKET_ERROR) { int err = WSAGetLastError(); if (err == WSAEWOULDBLOCK) { return java.net.DualStackPlainSocketImpl.WOULDBLOCK; } else if (err == 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, err, "connect"); } return -1; // return value not important. } return rv; } /* * Class: java_net_DualStackPlainSocketImpl * Method: waitForConnect * Signature: (II)V */ static void waitForConnect (JNIEnv env, cli.System.Net.Sockets.Socket fd, int timeout) { int rv, retry; 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 timeout, connection established or * connection failed. */ rv = 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 (rv == 0) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketTimeoutException", "connect timed out"); shutdown( fd, SD_BOTH ); return; } /* * Socket is writable or error occured. On some Windows editions * the socket will appear writable when the connect fails so we * check for error rather than writable. */ if (!FD_ISSET(fd, ex)) { return; /* connection established */ } /* * Connection 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. */ for (retry=0; retry<3; retry++) { int[] tmp = { 0 }; NET_GetSockOpt(fd, SOL_SOCKET, SO_ERROR, tmp); rv = tmp[0]; if (rv != 0) { break; } Sleep(0); } if (rv == 0) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "Unable to establish connection"); } else { NET_ThrowNew(env, rv, "connect"); } } /* * Class: java_net_DualStackPlainSocketImpl * Method: localPort0 * Signature: (I)I */ static int localPort0 (JNIEnv env, cli.System.Net.Sockets.Socket fd) { SOCKETADDRESS sa; sa = new SOCKETADDRESS(); if (getsockname(fd, sa) == SOCKET_ERROR) { if (WSAGetLastError() == WSAENOTSOCK) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "Socket closed"); } else { NET_ThrowNew(env, WSAGetLastError(), "getsockname failed"); } return -1; } return ntohs(GET_PORT(sa)); } /* * Class: java_net_DualStackPlainSocketImpl * Method: localAddress * Signature: (ILjava/net/InetAddressContainer;)V */ static void localAddress (JNIEnv env, cli.System.Net.Sockets.Socket fd, InetAddressContainer iaContainerObj) { int[] port = { 0 }; SOCKETADDRESS sa; sa = new SOCKETADDRESS(); InetAddress iaObj; if (getsockname(fd, sa) == SOCKET_ERROR) { NET_ThrowNew(env, WSAGetLastError(), "Error getting socket name"); return; } iaObj = NET_SockaddrToInetAddress(env, sa, port); iaContainerObj.addr = iaObj; } /* * Class: java_net_DualStackPlainSocketImpl * Method: listen0 * Signature: (II)V */ static void listen0 (JNIEnv env, cli.System.Net.Sockets.Socket fd, int backlog) { if (listen(fd, backlog) == SOCKET_ERROR) { NET_ThrowNew(env, WSAGetLastError(), "listen failed"); } } /* * Class: java_net_DualStackPlainSocketImpl * Method: accept0 * Signature: (I[Ljava/net/InetSocketAddress;)I */ static cli.System.Net.Sockets.Socket accept0 (JNIEnv env, cli.System.Net.Sockets.Socket fd, InetSocketAddress[] isaa) { cli.System.Net.Sockets.Socket newfd; int[] port = { 0 }; InetSocketAddress isa; InetAddress ia; SOCKETADDRESS sa; sa = new SOCKETADDRESS(); newfd = accept(fd, sa); if (newfd == INVALID_SOCKET) { if (WSAGetLastError() == -2) { JNU_ThrowByName(env, JNU_JAVAIOPKG+"InterruptedIOException", "operation interrupted"); } else { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "socket closed"); } return null; } ia = NET_SockaddrToInetAddress(env, sa, port); isa = new InetSocketAddress(ia, port[0]); isaa[0] = isa; return newfd; } /* * Class: java_net_DualStackPlainSocketImpl * Method: waitForNewConnection * Signature: (II)V */ static void waitForNewConnection (JNIEnv env, cli.System.Net.Sockets.Socket fd, int timeout) { int rv; rv = NET_Timeout(fd, timeout); if (rv == 0) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketTimeoutException", "Accept timed out"); } else if (rv == -1) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "socket closed"); } else if (rv == -2) { JNU_ThrowByName(env, JNU_JAVAIOPKG+"InterruptedIOException", "operation interrupted"); } } /* * Class: java_net_DualStackPlainSocketImpl * Method: available0 * Signature: (I)I */ static int available0 (JNIEnv env, cli.System.Net.Sockets.Socket fd) { int[] available = { -1 }; if ((ioctlsocket(fd, FIONREAD, available)) == SOCKET_ERROR) { NET_ThrowNew(env, WSAGetLastError(), "socket available"); } return available[0]; } /* * Class: java_net_DualStackPlainSocketImpl * Method: close0 * Signature: (I)V */ static void close0 (JNIEnv env, cli.System.Net.Sockets.Socket fd) { NET_SocketClose(fd); } /* * Class: java_net_DualStackPlainSocketImpl * Method: shutdown0 * Signature: (II)V */ static void shutdown0 (JNIEnv env, cli.System.Net.Sockets.Socket fd, int howto) { shutdown(fd, howto); } /* * Class: java_net_DualStackPlainSocketImpl * Method: setIntOption * Signature: (III)V */ static void setIntOption (JNIEnv env, cli.System.Net.Sockets.Socket fd, int cmd, int value) { int[] level = { 0 }; int[] opt = { 0 }; linger linger; Object optval; if (NET_MapSocketOption(cmd, level, opt) < 0) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "Invalid option"); return; } if (opt[0] == java.net.SocketOptions.SO_LINGER) { linger = new linger(); if (value >= 0) { linger.l_onoff = 1; linger.l_linger = value & 0xFFFF; } else { linger.l_onoff = 0; linger.l_linger = 0; } optval = linger; } else { optval = value; } if (NET_SetSockOpt(fd, level[0], opt[0], optval) < 0) { NET_ThrowNew(env, WSAGetLastError(), "setsockopt"); } } /* * Class: java_net_DualStackPlainSocketImpl * Method: getIntOption * Signature: (II)I */ static int getIntOption (JNIEnv env, cli.System.Net.Sockets.Socket fd, int cmd) { int[] level = { 0 }; int[] opt = { 0 }; int[] result = { 0 }; linger linger; Object optval; if (NET_MapSocketOption(cmd, level, opt) < 0) { JNU_ThrowByName(env, JNU_JAVANETPKG+"SocketException", "Unsupported socket option"); return -1; } if (opt[0] == java.net.SocketOptions.SO_LINGER) { linger = new linger(); optval = linger; } else { linger = null; optval = result; } if (NET_GetSockOpt(fd, level[0], opt[0], optval) < 0) { NET_ThrowNew(env, WSAGetLastError(), "getsockopt"); return -1; } if (opt[0] == java.net.SocketOptions.SO_LINGER) return linger.l_onoff != 0 ? linger.l_linger : -1; else return result[0]; } /* * Class: java_net_DualStackPlainSocketImpl * Method: sendOOB * Signature: (II)V */ static void sendOOB (JNIEnv env, cli.System.Net.Sockets.Socket fd, int data) { int n; n = send(fd, new byte[] { (byte)data }, 1, MSG_OOB); if (n == JVM_IO_ERR) { NET_ThrowNew(env, WSAGetLastError(), "send"); } else if (n == JVM_IO_INTR) { JNU_ThrowByName(env, "java.io.InterruptedIOException", null); } } /* * Class: java_net_DualStackPlainSocketImpl * Method: configureBlocking * Signature: (IZ)V */ static void configureBlocking (JNIEnv env, cli.System.Net.Sockets.Socket fd, boolean blocking) { int arg; int result; if (blocking == JNI_TRUE) { arg = SET_BLOCKING; // 0 } else { arg = SET_NONBLOCKING; // 1 } result = ioctlsocket(fd, FIONBIO, arg); if (result == SOCKET_ERROR) { NET_ThrowNew(env, WSAGetLastError(), "configureBlocking"); } } }