491 lines
13 KiB
Java
491 lines
13 KiB
Java
/*
|
|
* 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 <windows.h>
|
|
#include <winsock2.h>
|
|
#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, "<init>",
|
|
"(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");
|
|
}
|
|
}
|
|
}
|