885 lines
31 KiB
Java
885 lines
31 KiB
Java
|
/*
|
||
|
* Copyright (c) 1997, 2008, 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 cli.System.Net.IPAddress;
|
||
|
import cli.System.Net.IPEndPoint;
|
||
|
import static ikvm.internal.JNI.*;
|
||
|
import static ikvm.internal.Winsock.*;
|
||
|
|
||
|
final class net_util_md
|
||
|
{
|
||
|
private net_util_md() { }
|
||
|
|
||
|
static final int INADDR_ANY = 0;
|
||
|
|
||
|
static final int IPTOS_TOS_MASK = 0x1e;
|
||
|
static final int IPTOS_PREC_MASK = 0xe0;
|
||
|
|
||
|
static boolean isRcvTimeoutSupported = true;
|
||
|
|
||
|
/*
|
||
|
* Table of Windows Sockets errors, the specific exception we
|
||
|
* throw for the error, and the error text.
|
||
|
*
|
||
|
* Note that this table excludes OS dependent errors.
|
||
|
*
|
||
|
* Latest list of Windows Sockets errors can be found at :-
|
||
|
* http://msdn.microsoft.com/library/psdk/winsock/errors_3wc2.htm
|
||
|
*/
|
||
|
private static class WinsockError
|
||
|
{
|
||
|
final int errCode;
|
||
|
final int exc;
|
||
|
final String errString;
|
||
|
|
||
|
WinsockError(int errCode, int exc, String errString)
|
||
|
{
|
||
|
this.errCode = errCode;
|
||
|
this.exc = exc;
|
||
|
this.errString = errString;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private static final int Exception_BindException = 1;
|
||
|
private static final int Exception_ConnectException = 2;
|
||
|
private static final int Exception_NoRouteToHostException = 3;
|
||
|
|
||
|
private static final WinsockError[] winsock_errors = {
|
||
|
new WinsockError(WSAEACCES, 0, "Permission denied"),
|
||
|
new WinsockError(WSAEADDRINUSE, Exception_BindException, "Address already in use"),
|
||
|
new WinsockError(WSAEADDRNOTAVAIL, Exception_BindException, "Cannot assign requested address"),
|
||
|
new WinsockError(WSAEAFNOSUPPORT, 0, "Address family not supported by protocol family"),
|
||
|
new WinsockError(WSAEALREADY, 0, "Operation already in progress"),
|
||
|
new WinsockError(WSAECONNABORTED, 0, "Software caused connection abort"),
|
||
|
new WinsockError(WSAECONNREFUSED, Exception_ConnectException, "Connection refused"),
|
||
|
new WinsockError(WSAECONNRESET, 0, "Connection reset by peer"),
|
||
|
new WinsockError(WSAEDESTADDRREQ, 0, "Destination address required"),
|
||
|
new WinsockError(WSAEFAULT, 0, "Bad address"),
|
||
|
new WinsockError(WSAEHOSTDOWN, 0, "Host is down"),
|
||
|
new WinsockError(WSAEHOSTUNREACH, Exception_NoRouteToHostException, "No route to host"),
|
||
|
new WinsockError(WSAEINPROGRESS, 0, "Operation now in progress"),
|
||
|
new WinsockError(WSAEINTR, 0, "Interrupted function call"),
|
||
|
new WinsockError(WSAEINVAL, 0, "Invalid argument"),
|
||
|
new WinsockError(WSAEISCONN, 0, "Socket is already connected"),
|
||
|
new WinsockError(WSAEMFILE, 0, "Too many open files"),
|
||
|
new WinsockError(WSAEMSGSIZE, 0, "The message is larger than the maximum supported by the underlying transport"),
|
||
|
new WinsockError(WSAENETDOWN, 0, "Network is down"),
|
||
|
new WinsockError(WSAENETRESET, 0, "Network dropped connection on reset"),
|
||
|
new WinsockError(WSAENETUNREACH, 0, "Network is unreachable"),
|
||
|
new WinsockError(WSAENOBUFS, 0, "No buffer space available (maximum connections reached?)"),
|
||
|
new WinsockError(WSAENOPROTOOPT, 0, "Bad protocol option"),
|
||
|
new WinsockError(WSAENOTCONN, 0, "Socket is not connected"),
|
||
|
new WinsockError(WSAENOTSOCK, 0, "Socket operation on nonsocket"),
|
||
|
new WinsockError(WSAEOPNOTSUPP, 0, "Operation not supported"),
|
||
|
new WinsockError(WSAEPFNOSUPPORT, 0, "Protocol family not supported"),
|
||
|
new WinsockError(WSAEPROCLIM, 0, "Too many processes"),
|
||
|
new WinsockError(WSAEPROTONOSUPPORT, 0, "Protocol not supported"),
|
||
|
new WinsockError(WSAEPROTOTYPE, 0, "Protocol wrong type for socket"),
|
||
|
new WinsockError(WSAESHUTDOWN, 0, "Cannot send after socket shutdown"),
|
||
|
new WinsockError(WSAESOCKTNOSUPPORT, 0, "Socket type not supported"),
|
||
|
new WinsockError(WSAETIMEDOUT, Exception_ConnectException, "Connection timed out"),
|
||
|
new WinsockError(WSATYPE_NOT_FOUND, 0, "Class type not found"),
|
||
|
new WinsockError(WSAEWOULDBLOCK, 0, "Resource temporarily unavailable"),
|
||
|
new WinsockError(WSAHOST_NOT_FOUND, 0, "Host not found"),
|
||
|
new WinsockError(WSA_NOT_ENOUGH_MEMORY, 0, "Insufficient memory available"),
|
||
|
new WinsockError(WSANOTINITIALISED, 0, "Successful WSAStartup not yet performed"),
|
||
|
new WinsockError(WSANO_DATA, 0, "Valid name, no data record of requested type"),
|
||
|
new WinsockError(WSANO_RECOVERY, 0, "This is a nonrecoverable error"),
|
||
|
new WinsockError(WSASYSNOTREADY, 0, "Network subsystem is unavailable"),
|
||
|
new WinsockError(WSATRY_AGAIN, 0, "Nonauthoritative host not found"),
|
||
|
new WinsockError(WSAVERNOTSUPPORTED, 0, "Winsock.dll version out of range"),
|
||
|
new WinsockError(WSAEDISCON, 0, "Graceful shutdown in progress"),
|
||
|
new WinsockError(WSA_OPERATION_ABORTED, 0, "Overlapped operation aborted")
|
||
|
};
|
||
|
|
||
|
/*
|
||
|
* Since winsock doesn't have the equivalent of strerror(errno)
|
||
|
* use table to lookup error text for the error.
|
||
|
*/
|
||
|
static SocketException NET_ThrowNew(int errorNum, String msg)
|
||
|
{
|
||
|
int i;
|
||
|
int table_size = winsock_errors.length;
|
||
|
int excP = 0;
|
||
|
String fullMsg;
|
||
|
|
||
|
if (msg == null) {
|
||
|
msg = "no further information";
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Check table for known winsock errors
|
||
|
*/
|
||
|
i=0;
|
||
|
while (i < table_size) {
|
||
|
if (errorNum == winsock_errors[i].errCode) {
|
||
|
break;
|
||
|
}
|
||
|
i++;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* If found get pick the specific exception and error
|
||
|
* message corresponding to this error.
|
||
|
*/
|
||
|
if (i < table_size) {
|
||
|
excP = winsock_errors[i].exc;
|
||
|
fullMsg = winsock_errors[i].errString + ": " + msg;
|
||
|
} else {
|
||
|
fullMsg = "Unrecognized Windows Sockets error: " + errorNum + ": " + msg;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Throw SocketException if no specific exception for this
|
||
|
* error.
|
||
|
*/
|
||
|
switch (excP) {
|
||
|
case Exception_BindException:
|
||
|
return new BindException(fullMsg);
|
||
|
case Exception_ConnectException:
|
||
|
return new ConnectException(fullMsg);
|
||
|
case Exception_NoRouteToHostException:
|
||
|
return new NoRouteToHostException(fullMsg);
|
||
|
default:
|
||
|
return new SocketException(fullMsg);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void NET_ThrowNew(JNIEnv env, int errorNum, String msg)
|
||
|
{
|
||
|
env.Throw(NET_ThrowNew(errorNum, msg));
|
||
|
}
|
||
|
|
||
|
static SocketException NET_ThrowCurrent(String msg)
|
||
|
{
|
||
|
return NET_ThrowNew(WSAGetLastError(), msg);
|
||
|
}
|
||
|
|
||
|
static void NET_ThrowCurrent(JNIEnv env, String msg)
|
||
|
{
|
||
|
env.Throw(NET_ThrowCurrent(msg));
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Return the default TOS value
|
||
|
*/
|
||
|
static int NET_GetDefaultTOS() {
|
||
|
// we always use the "default" default...
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Map the Java level socket option to the platform specific
|
||
|
* level and option name.
|
||
|
*/
|
||
|
|
||
|
private static final class sockopts {
|
||
|
int cmd;
|
||
|
int level;
|
||
|
int optname;
|
||
|
|
||
|
sockopts(int cmd, int level, int optname) {
|
||
|
this.cmd = cmd;
|
||
|
this.level = level;
|
||
|
this.optname = optname;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private static final sockopts opts[] = {
|
||
|
new sockopts(SocketOptions.TCP_NODELAY, IPPROTO_TCP, TCP_NODELAY ),
|
||
|
new sockopts(SocketOptions.SO_OOBINLINE, SOL_SOCKET, SO_OOBINLINE ),
|
||
|
new sockopts(SocketOptions.SO_LINGER, SOL_SOCKET, SO_LINGER ),
|
||
|
new sockopts(SocketOptions.SO_SNDBUF, SOL_SOCKET, SO_SNDBUF ),
|
||
|
new sockopts(SocketOptions.SO_RCVBUF, SOL_SOCKET, SO_RCVBUF ),
|
||
|
new sockopts(SocketOptions.SO_KEEPALIVE, SOL_SOCKET, SO_KEEPALIVE ),
|
||
|
new sockopts(SocketOptions.SO_REUSEADDR, SOL_SOCKET, SO_REUSEADDR ),
|
||
|
new sockopts(SocketOptions.SO_BROADCAST, SOL_SOCKET, SO_BROADCAST ),
|
||
|
new sockopts(SocketOptions.IP_MULTICAST_IF, IPPROTO_IP, IP_MULTICAST_IF ),
|
||
|
new sockopts(SocketOptions.IP_MULTICAST_LOOP, IPPROTO_IP, IP_MULTICAST_LOOP ),
|
||
|
new sockopts(SocketOptions.IP_TOS, IPPROTO_IP, IP_TOS ),
|
||
|
|
||
|
};
|
||
|
|
||
|
/* call NET_MapSocketOptionV6 for the IPv6 fd only
|
||
|
* and NET_MapSocketOption for the IPv4 fd
|
||
|
*/
|
||
|
static int NET_MapSocketOptionV6(int cmd, int[] level, int[] optname) {
|
||
|
|
||
|
switch (cmd) {
|
||
|
case SocketOptions.IP_MULTICAST_IF:
|
||
|
case SocketOptions.IP_MULTICAST_IF2:
|
||
|
level[0] = IPPROTO_IPV6;
|
||
|
optname[0] = IPV6_MULTICAST_IF;
|
||
|
return 0;
|
||
|
|
||
|
case SocketOptions.IP_MULTICAST_LOOP:
|
||
|
level[0] = IPPROTO_IPV6;
|
||
|
optname[0] = IPV6_MULTICAST_LOOP;
|
||
|
return 0;
|
||
|
}
|
||
|
return NET_MapSocketOption (cmd, level, optname);
|
||
|
}
|
||
|
|
||
|
static int NET_MapSocketOption(int cmd, int[] level, int[] optname) {
|
||
|
/*
|
||
|
* Map the Java level option to the native level
|
||
|
*/
|
||
|
for (int i=0; i<opts.length; i++) {
|
||
|
if (cmd == opts[i].cmd) {
|
||
|
level[0] = opts[i].level;
|
||
|
optname[0] = opts[i].optname;
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* not found */
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
static int NET_SetSockOpt(cli.System.Net.Sockets.Socket s, int level, int optname, Object optval)
|
||
|
{
|
||
|
int rv;
|
||
|
|
||
|
if (level == IPPROTO_IP && optname == IP_TOS) {
|
||
|
int tos = (Integer)optval;
|
||
|
tos &= (IPTOS_TOS_MASK | IPTOS_PREC_MASK);
|
||
|
optval = tos;
|
||
|
}
|
||
|
|
||
|
if (optname == SO_REUSEADDR) {
|
||
|
/*
|
||
|
* Do not set SO_REUSEADDE if SO_EXCLUSIVEADDUSE is already set
|
||
|
*/
|
||
|
int[] parg = new int[1];
|
||
|
rv = NET_GetSockOpt(s, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, parg);
|
||
|
if (rv == 0 && parg[0] == 1) {
|
||
|
return rv;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
rv = setsockopt(s, level, optname, optval);
|
||
|
|
||
|
if (rv == SOCKET_ERROR) {
|
||
|
/*
|
||
|
* IP_TOS & IP_MULTICAST_LOOP can't be set on some versions
|
||
|
* of Windows.
|
||
|
*/
|
||
|
if ((WSAGetLastError() == WSAENOPROTOOPT) &&
|
||
|
(level == IPPROTO_IP) &&
|
||
|
(optname == IP_TOS || optname == IP_MULTICAST_LOOP)) {
|
||
|
rv = 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* IP_TOS can't be set on unbound UDP sockets.
|
||
|
*/
|
||
|
if ((WSAGetLastError() == WSAEINVAL) &&
|
||
|
(level == IPPROTO_IP) &&
|
||
|
(optname == IP_TOS)) {
|
||
|
rv = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return rv;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Wrapper for setsockopt dealing with Windows specific issues :-
|
||
|
*
|
||
|
* IP_TOS is not supported on some versions of Windows so
|
||
|
* instead return the default value for the OS.
|
||
|
*/
|
||
|
static int NET_GetSockOpt(cli.System.Net.Sockets.Socket s, int level, int optname, Object optval)
|
||
|
{
|
||
|
int rv;
|
||
|
|
||
|
rv = getsockopt(s, level, optname, optval);
|
||
|
|
||
|
|
||
|
/*
|
||
|
* IPPROTO_IP/IP_TOS is not supported on some Windows
|
||
|
* editions so return the default type-of-service
|
||
|
* value.
|
||
|
*/
|
||
|
if (rv == SOCKET_ERROR) {
|
||
|
|
||
|
if (WSAGetLastError() == WSAENOPROTOOPT &&
|
||
|
level == IPPROTO_IP && optname == IP_TOS) {
|
||
|
|
||
|
((int[])optval)[0] = NET_GetDefaultTOS();
|
||
|
|
||
|
rv = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return rv;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Sets SO_ECLUSIVEADDRUSE if SO_REUSEADDR is not already set.
|
||
|
*/
|
||
|
static void setExclusiveBind(cli.System.Net.Sockets.Socket fd) {
|
||
|
int[] parg = new int[1];
|
||
|
int rv = 0;
|
||
|
rv = NET_GetSockOpt(fd, SOL_SOCKET, SO_REUSEADDR, parg);
|
||
|
if (rv == 0 && parg[0] == 0) {
|
||
|
rv = NET_SetSockOpt(fd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, 1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Wrapper for bind winsock call - transparent converts an
|
||
|
* error related to binding to a port that has exclusive access
|
||
|
* into an error indicating the port is in use (facilitates
|
||
|
* better error reporting).
|
||
|
*/
|
||
|
static int NET_Bind(cli.System.Net.Sockets.Socket s, SOCKETADDRESS him)
|
||
|
{
|
||
|
int rv;
|
||
|
rv = bind(s, him);
|
||
|
|
||
|
if (rv == SOCKET_ERROR) {
|
||
|
/*
|
||
|
* If bind fails with WSAEACCES it means that a privileged
|
||
|
* process has done an exclusive bind (NT SP4/2000/XP only).
|
||
|
*/
|
||
|
if (WSAGetLastError() == WSAEACCES) {
|
||
|
WSASetLastError(WSAEADDRINUSE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return rv;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Wrapper for NET_Bind call. Sets SO_EXCLUSIVEADDRUSE
|
||
|
* if required, and then calls NET_BIND
|
||
|
*/
|
||
|
static int NET_WinBind(cli.System.Net.Sockets.Socket s, SOCKETADDRESS him, boolean exclBind)
|
||
|
{
|
||
|
if (exclBind == JNI_TRUE)
|
||
|
setExclusiveBind(s);
|
||
|
return NET_Bind(s, him);
|
||
|
}
|
||
|
|
||
|
static int NET_SocketClose(cli.System.Net.Sockets.Socket fd) {
|
||
|
linger l = new linger();
|
||
|
int ret;
|
||
|
if (getsockopt(fd, SOL_SOCKET, SO_LINGER, l) == 0) {
|
||
|
if (l.l_onoff == 0) {
|
||
|
WSASendDisconnect(fd);
|
||
|
}
|
||
|
}
|
||
|
ret = closesocket (fd);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static int NET_Timeout(cli.System.Net.Sockets.Socket fd, long timeout) {
|
||
|
int ret;
|
||
|
fd_set tbl = new fd_set();
|
||
|
timeval t = new timeval();
|
||
|
t.tv_sec = timeout / 1000;
|
||
|
t.tv_usec = (timeout % 1000) * 1000;
|
||
|
FD_ZERO(tbl);
|
||
|
FD_SET(fd, tbl);
|
||
|
ret = select (tbl, null, null, t);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* differs from NET_Timeout() as follows:
|
||
|
*
|
||
|
* If timeout = -1, it blocks forever.
|
||
|
*
|
||
|
* returns 1 or 2 depending if only one or both sockets
|
||
|
* fire at same time.
|
||
|
*
|
||
|
* *fdret is (one of) the active fds. If both sockets
|
||
|
* fire at same time, *fdret = fd always.
|
||
|
*/
|
||
|
static int NET_Timeout2(cli.System.Net.Sockets.Socket fd, cli.System.Net.Sockets.Socket fd1, long timeout, cli.System.Net.Sockets.Socket[] fdret) {
|
||
|
int ret;
|
||
|
fd_set tbl = new fd_set();
|
||
|
timeval t = new timeval();
|
||
|
if (timeout == -1) {
|
||
|
t = null;
|
||
|
} else {
|
||
|
t.tv_sec = timeout / 1000;
|
||
|
t.tv_usec = (timeout % 1000) * 1000;
|
||
|
}
|
||
|
FD_ZERO(tbl);
|
||
|
FD_SET(fd, tbl);
|
||
|
FD_SET(fd1, tbl);
|
||
|
ret = select (tbl, null, null, t);
|
||
|
switch (ret) {
|
||
|
case 0:
|
||
|
return 0; /* timeout */
|
||
|
case 1:
|
||
|
if (FD_ISSET (fd, tbl)) {
|
||
|
fdret[0]= fd;
|
||
|
} else {
|
||
|
fdret[0]= fd1;
|
||
|
}
|
||
|
return 1;
|
||
|
case 2:
|
||
|
fdret[0]= fd;
|
||
|
return 2;
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* if ipv6 is available, call NET_BindV6 to bind to the required address/port.
|
||
|
* Because the same port number may need to be reserved in both v4 and v6 space,
|
||
|
* this may require socket(s) to be re-opened. Therefore, all of this information
|
||
|
* is passed in and returned through the ipv6bind structure.
|
||
|
*
|
||
|
* If the request is to bind to a specific address, then this (by definition) means
|
||
|
* only bind in either v4 or v6, and this is just the same as normal. ie. a single
|
||
|
* call to bind() will suffice. The other socket is closed in this case.
|
||
|
*
|
||
|
* The more complicated case is when the requested address is ::0 or 0.0.0.0.
|
||
|
*
|
||
|
* Two further cases:
|
||
|
* 2. If the reqeusted port is 0 (ie. any port) then we try to bind in v4 space
|
||
|
* first with a wild-card port argument. We then try to bind in v6 space
|
||
|
* using the returned port number. If this fails, we repeat the process
|
||
|
* until a free port common to both spaces becomes available.
|
||
|
*
|
||
|
* 3. If the requested port is a specific port, then we just try to get that
|
||
|
* port in both spaces, and if it is not free in both, then the bind fails.
|
||
|
*
|
||
|
* On failure, sockets are closed and an error returned with CLOSE_SOCKETS_AND_RETURN
|
||
|
*/
|
||
|
|
||
|
static class ipv6bind {
|
||
|
SOCKETADDRESS addr;
|
||
|
cli.System.Net.Sockets.Socket ipv4_fd;
|
||
|
cli.System.Net.Sockets.Socket ipv6_fd;
|
||
|
}
|
||
|
|
||
|
private static int CLOSE_SOCKETS_AND_RETURN(
|
||
|
cli.System.Net.Sockets.Socket fd,
|
||
|
cli.System.Net.Sockets.Socket ofd,
|
||
|
cli.System.Net.Sockets.Socket close_fd,
|
||
|
cli.System.Net.Sockets.Socket close_ofd,
|
||
|
ipv6bind b) {
|
||
|
if (fd != null) {
|
||
|
closesocket (fd);
|
||
|
}
|
||
|
if (ofd != null) {
|
||
|
closesocket (ofd);
|
||
|
}
|
||
|
if (close_fd != null) {
|
||
|
closesocket (close_fd);
|
||
|
}
|
||
|
if (close_ofd != null) {
|
||
|
closesocket (close_ofd);
|
||
|
}
|
||
|
b.ipv4_fd = b.ipv6_fd = null;
|
||
|
return SOCKET_ERROR;
|
||
|
}
|
||
|
|
||
|
static int NET_BindV6(ipv6bind b, boolean exclBind) {
|
||
|
cli.System.Net.Sockets.Socket fd = null;
|
||
|
cli.System.Net.Sockets.Socket ofd = null;
|
||
|
int rv;
|
||
|
/* need to defer close until new sockets created */
|
||
|
cli.System.Net.Sockets.Socket close_fd = null;
|
||
|
cli.System.Net.Sockets.Socket close_ofd = null;
|
||
|
|
||
|
SOCKETADDRESS oaddr = new SOCKETADDRESS();
|
||
|
int family = b.addr.him.sa_family;
|
||
|
int ofamily;
|
||
|
int port;
|
||
|
int bound_port;
|
||
|
|
||
|
if (family == AF_INET && (b.addr.him4.sin_addr.s_addr != INADDR_ANY)) {
|
||
|
/* bind to v4 only */
|
||
|
int ret;
|
||
|
ret = NET_WinBind (b.ipv4_fd, b.addr, exclBind);
|
||
|
if (ret == SOCKET_ERROR) {
|
||
|
return CLOSE_SOCKETS_AND_RETURN(fd, ofd, close_fd, close_ofd, b);
|
||
|
}
|
||
|
closesocket (b.ipv6_fd);
|
||
|
b.ipv6_fd = null;
|
||
|
return 0;
|
||
|
}
|
||
|
if (family == AF_INET6 && (!IN6ADDR_ISANY(b.addr))) {
|
||
|
/* bind to v6 only */
|
||
|
int ret;
|
||
|
ret = NET_WinBind (b.ipv6_fd, b.addr, exclBind);
|
||
|
if (ret == SOCKET_ERROR) {
|
||
|
return CLOSE_SOCKETS_AND_RETURN(fd, ofd, close_fd, close_ofd, b);
|
||
|
}
|
||
|
closesocket (b.ipv4_fd);
|
||
|
b.ipv4_fd = null;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* We need to bind on both stacks, with the same port number */
|
||
|
|
||
|
if (family == AF_INET) {
|
||
|
ofamily = AF_INET6;
|
||
|
fd = b.ipv4_fd;
|
||
|
ofd = b.ipv6_fd;
|
||
|
port = GET_PORT (b.addr);
|
||
|
oaddr.set(new IPEndPoint(IPAddress.IPv6Any, htons(port)));
|
||
|
} else {
|
||
|
ofamily = AF_INET;
|
||
|
ofd = b.ipv4_fd;
|
||
|
fd = b.ipv6_fd;
|
||
|
port = GET_PORT (b.addr);
|
||
|
oaddr.set(new IPEndPoint(IPAddress.Any, htons(port)));
|
||
|
}
|
||
|
|
||
|
rv = NET_WinBind (fd, b.addr, exclBind);
|
||
|
if (rv == SOCKET_ERROR) {
|
||
|
return CLOSE_SOCKETS_AND_RETURN(fd, ofd, close_fd, close_ofd, b);
|
||
|
}
|
||
|
|
||
|
/* get the port and set it in the other address */
|
||
|
if (getsockname(fd, b.addr) == -1) {
|
||
|
return CLOSE_SOCKETS_AND_RETURN(fd, ofd, close_fd, close_ofd, b);
|
||
|
}
|
||
|
bound_port = b.addr.sin_port;
|
||
|
oaddr.sin_port = bound_port;
|
||
|
if ((rv=NET_Bind (ofd, oaddr)) == SOCKET_ERROR) {
|
||
|
|
||
|
/* no retries unless, the request was for any free port */
|
||
|
|
||
|
if (port != 0) {
|
||
|
return CLOSE_SOCKETS_AND_RETURN(fd, ofd, close_fd, close_ofd, b);
|
||
|
}
|
||
|
|
||
|
int sotype = fd.get_SocketType().Value;
|
||
|
|
||
|
/* 50 is an arbitrary limit, just to ensure that this
|
||
|
* cannot be an endless loop. Would expect socket creation to
|
||
|
* succeed sooner.
|
||
|
*/
|
||
|
for (int retries = 0; retries < 50 /*SOCK_RETRIES*/; retries ++) {
|
||
|
close_fd = fd;
|
||
|
fd = null;
|
||
|
close_ofd = ofd;
|
||
|
ofd = null;
|
||
|
b.ipv4_fd = null;
|
||
|
b.ipv6_fd = null;
|
||
|
|
||
|
/* create two new sockets */
|
||
|
fd = socket (family, sotype, 0);
|
||
|
if (fd == INVALID_SOCKET) {
|
||
|
return CLOSE_SOCKETS_AND_RETURN(fd, ofd, close_fd, close_ofd, b);
|
||
|
}
|
||
|
ofd = socket (ofamily, sotype, 0);
|
||
|
if (ofd == INVALID_SOCKET) {
|
||
|
return CLOSE_SOCKETS_AND_RETURN(fd, ofd, close_fd, close_ofd, b);
|
||
|
}
|
||
|
|
||
|
/* bind random port on first socket */
|
||
|
oaddr.sin_port = 0;
|
||
|
rv = NET_WinBind (ofd, oaddr,
|
||
|
exclBind);
|
||
|
if (rv == SOCKET_ERROR) {
|
||
|
return CLOSE_SOCKETS_AND_RETURN(fd, ofd, close_fd, close_ofd, b);
|
||
|
}
|
||
|
/* close the original pair of sockets before continuing */
|
||
|
closesocket (close_fd);
|
||
|
closesocket (close_ofd);
|
||
|
close_fd = close_ofd = null;
|
||
|
|
||
|
/* bind new port on second socket */
|
||
|
if (getsockname(ofd, oaddr) == -1) {
|
||
|
return CLOSE_SOCKETS_AND_RETURN(fd, ofd, close_fd, close_ofd, b);
|
||
|
}
|
||
|
bound_port = oaddr.sin_port;
|
||
|
b.addr.sin_port = bound_port;
|
||
|
rv = NET_WinBind (fd, b.addr,
|
||
|
exclBind);
|
||
|
|
||
|
if (rv != SOCKET_ERROR) {
|
||
|
if (family == AF_INET) {
|
||
|
b.ipv4_fd = fd;
|
||
|
b.ipv6_fd = ofd;
|
||
|
} else {
|
||
|
b.ipv4_fd = ofd;
|
||
|
b.ipv6_fd = fd;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
return CLOSE_SOCKETS_AND_RETURN(fd, ofd, close_fd, close_ofd, b);
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* If address types is IPv6, then IPv6 must be available. Otherwise
|
||
|
* no address can be generated. In the case of an IPv4 Inetaddress this
|
||
|
* method will return an IPv4 mapped address where IPv6 is available and
|
||
|
* v4MappedAddress is TRUE. Otherwise it will return a sockaddr_in
|
||
|
* structure for an IPv4 InetAddress.
|
||
|
*/
|
||
|
static int NET_InetAddressToSockaddr(JNIEnv env, InetAddress iaObj, int port, SOCKETADDRESS him, boolean v4MappedAddress) {
|
||
|
if (iaObj.family == InetAddress.IPv4) {
|
||
|
him.set(new IPEndPoint(new IPAddress(htonl(iaObj.address) & 0xFFFFFFFFL), port));
|
||
|
return 0;
|
||
|
} else {
|
||
|
Inet6Address v6addr = (Inet6Address)iaObj;
|
||
|
int scope = v6addr.getScopeId();
|
||
|
if (scope == 0) {
|
||
|
him.set(new IPEndPoint(new IPAddress(v6addr.ipaddress), port));
|
||
|
return 0;
|
||
|
} else {
|
||
|
him.set(new IPEndPoint(new IPAddress(v6addr.ipaddress, scope & 0xFFFFFFFFL), port));
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static int NET_GetPortFromSockaddr(SOCKETADDRESS him) {
|
||
|
return ntohs(GET_PORT(him));
|
||
|
}
|
||
|
|
||
|
static boolean NET_IsIPv4Mapped(byte[] caddr) {
|
||
|
int i;
|
||
|
for (i = 0; i < 10; i++) {
|
||
|
if (caddr[i] != 0x00) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (((caddr[10] & 0xff) == 0xff) && ((caddr[11] & 0xff) == 0xff)) {
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
static int NET_IPv4MappedToIPv4(byte[] caddr) {
|
||
|
return ((caddr[12] & 0xff) << 24) | ((caddr[13] & 0xff) << 16) | ((caddr[14] & 0xff) << 8)
|
||
|
| (caddr[15] & 0xff);
|
||
|
}
|
||
|
|
||
|
static boolean NET_IsEqual(byte[] caddr1, byte[] caddr2) {
|
||
|
int i;
|
||
|
for (i = 0; i < 16; i++) {
|
||
|
if (caddr1[i] != caddr2[i]) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
static int getScopeID (SOCKETADDRESS him) {
|
||
|
return him.him6.sin6_scope_id;
|
||
|
}
|
||
|
|
||
|
static boolean cmpScopeID (int scope, SOCKETADDRESS him) {
|
||
|
return him.him6.sin6_scope_id == scope;
|
||
|
}
|
||
|
|
||
|
/* these methods are not from net_util_md.c */
|
||
|
|
||
|
static boolean ipv6_available() {
|
||
|
return InetAddressImplFactory.isIPv6Supported();
|
||
|
}
|
||
|
|
||
|
static boolean IS_NULL(Object obj) {
|
||
|
return obj == null;
|
||
|
}
|
||
|
|
||
|
static boolean IN6ADDR_ISANY(SOCKETADDRESS him) {
|
||
|
if (him.sa_family != AF_INET6) {
|
||
|
return false;
|
||
|
}
|
||
|
byte[] addr = him.him6.sin6_addr;
|
||
|
byte b = 0;
|
||
|
for (int i = 0; i < addr.length; i++) {
|
||
|
b |= addr[i];
|
||
|
}
|
||
|
return b == 0;
|
||
|
}
|
||
|
|
||
|
static boolean NET_SockaddrEqualsInetAddress(SOCKETADDRESS him, InetAddress iaObj) {
|
||
|
int family = iaObj.family == InetAddress.IPv4 ? AF_INET : AF_INET6;
|
||
|
|
||
|
if (him.sa_family == AF_INET6) {
|
||
|
byte[] caddrNew = him.him6.sin6_addr;
|
||
|
if (NET_IsIPv4Mapped(caddrNew)) {
|
||
|
int addrNew;
|
||
|
int addrCur;
|
||
|
if (family == AF_INET6) {
|
||
|
return false;
|
||
|
}
|
||
|
addrNew = NET_IPv4MappedToIPv4(caddrNew);
|
||
|
addrCur = iaObj.address;
|
||
|
if (addrNew == addrCur) {
|
||
|
return true;
|
||
|
} else {
|
||
|
return false;
|
||
|
}
|
||
|
} else {
|
||
|
byte[] caddrCur;
|
||
|
int scope;
|
||
|
|
||
|
if (family == AF_INET) {
|
||
|
return false;
|
||
|
}
|
||
|
scope = ((Inet6Address)iaObj).getScopeId();
|
||
|
caddrCur = ((Inet6Address)iaObj).ipaddress;
|
||
|
if (NET_IsEqual(caddrNew, caddrCur) && cmpScopeID(scope, him)) {
|
||
|
return true;
|
||
|
} else {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
int addrNew, addrCur;
|
||
|
if (family != AF_INET) {
|
||
|
return false;
|
||
|
}
|
||
|
addrNew = ntohl(him.him4.sin_addr.s_addr);
|
||
|
addrCur = iaObj.address;
|
||
|
if (addrNew == addrCur) {
|
||
|
return true;
|
||
|
} else {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static InetAddress NET_SockaddrToInetAddress(JNIEnv env, SOCKETADDRESS him, int[] port) {
|
||
|
return NET_SockaddrToInetAddress(him, port);
|
||
|
}
|
||
|
|
||
|
static InetAddress NET_SockaddrToInetAddress(SOCKETADDRESS him, int[] port) {
|
||
|
InetAddress iaObj;
|
||
|
if (him.sa_family == AF_INET6) {
|
||
|
byte[] caddr = him.him6.sin6_addr;
|
||
|
if (NET_IsIPv4Mapped(caddr)) {
|
||
|
iaObj = new Inet4Address(null, NET_IPv4MappedToIPv4(caddr));
|
||
|
} else {
|
||
|
int scope = getScopeID(him);
|
||
|
iaObj = new Inet6Address(null, caddr, scope > 0 ? scope : -1);
|
||
|
}
|
||
|
port[0] = ntohs(him.him6.sin_port);
|
||
|
} else {
|
||
|
iaObj = new Inet4Address(null, ntohl(him.him4.sin_addr.s_addr));
|
||
|
port[0] = ntohs(him.him4.sin_port);
|
||
|
}
|
||
|
return iaObj;
|
||
|
}
|
||
|
|
||
|
static void NET_ThrowByNameWithLastError(JNIEnv env, String exceptionClass, String message) {
|
||
|
JNU_ThrowByName(env, exceptionClass, "errno: " + WSAGetLastError() + ", error: " + message + "\n");
|
||
|
}
|
||
|
|
||
|
static boolean IN_MULTICAST(int ipv4address) {
|
||
|
return ((ipv4address >> 24) & 0xf0) == 0xe0;
|
||
|
}
|
||
|
|
||
|
static boolean IN6_IS_ADDR_MULTICAST(in6_addr address) {
|
||
|
return (address.s6_bytes()[0] & 0xff) == 0xff;
|
||
|
}
|
||
|
|
||
|
static final class SOCKETADDRESS implements IIPEndPointWrapper {
|
||
|
final SOCKETADDRESS him = this;
|
||
|
final SOCKETADDRESS him4 = this;
|
||
|
final SOCKETADDRESS him6 = this;
|
||
|
final SOCKETADDRESS sin_addr = this;
|
||
|
int sa_family;
|
||
|
int sin_port;
|
||
|
int s_addr;
|
||
|
byte[] sin6_addr;
|
||
|
int sin6_scope_id;
|
||
|
|
||
|
public void set(IPEndPoint ep) {
|
||
|
if (ep == null) {
|
||
|
sa_family = 0;
|
||
|
sin_port = 0;
|
||
|
s_addr = 0;
|
||
|
sin6_addr = null;
|
||
|
sin6_scope_id = 0;
|
||
|
} else {
|
||
|
sa_family = ep.get_AddressFamily().Value;
|
||
|
sin_port = htons(ep.get_Port());
|
||
|
if (sa_family == AF_INET) {
|
||
|
s_addr = (int)ep.get_Address().get_Address();
|
||
|
sin6_addr = null;
|
||
|
sin6_scope_id = 0;
|
||
|
} else {
|
||
|
s_addr = 0;
|
||
|
IPAddress addr = ep.get_Address();
|
||
|
sin6_addr = addr.GetAddressBytes();
|
||
|
sin6_scope_id = (int)addr.get_ScopeId();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public IPEndPoint get() {
|
||
|
if (sa_family == AF_INET) {
|
||
|
return new IPEndPoint(new IPAddress(s_addr & 0xFFFFFFFFL), ntohs(sin_port));
|
||
|
} else if (sa_family == AF_INET6) {
|
||
|
IPAddress addr;
|
||
|
if (sin6_addr == null) {
|
||
|
addr = IPAddress.IPv6Any;
|
||
|
} else {
|
||
|
addr = new IPAddress(sin6_addr, sin6_scope_id & 0xFFFFFFFFL);
|
||
|
}
|
||
|
return new IPEndPoint(addr, ntohs(sin_port));
|
||
|
} else {
|
||
|
return null;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static int GET_PORT(SOCKETADDRESS sockaddr) {
|
||
|
return sockaddr.sin_port;
|
||
|
}
|
||
|
|
||
|
static void Sleep(int ms) {
|
||
|
cli.System.Threading.Thread.Sleep(ms);
|
||
|
}
|
||
|
|
||
|
static cli.System.Net.Sockets.Socket NET_Socket (int domain, int type, int protocol) {
|
||
|
cli.System.Net.Sockets.Socket sock;
|
||
|
sock = socket (domain, type, protocol);
|
||
|
if (sock != INVALID_SOCKET) {
|
||
|
//SetHandleInformation((HANDLE)(uintptr_t)sock, HANDLE_FLAG_INHERIT, FALSE);
|
||
|
}
|
||
|
return sock;
|
||
|
}
|
||
|
|
||
|
static int getInetAddress_addr(JNIEnv env, InetAddress iaObj) {
|
||
|
return iaObj.address;
|
||
|
}
|
||
|
|
||
|
static int getInetAddress_family(JNIEnv env, InetAddress iaObj) {
|
||
|
return iaObj.family;
|
||
|
}
|
||
|
}
|