diff --git a/openjdk/sun/nio/ch/DatagramChannelImpl.java b/openjdk/sun/nio/ch/DatagramChannelImpl.java index fb6f6f8f..4fa9796e 100644 --- a/openjdk/sun/nio/ch/DatagramChannelImpl.java +++ b/openjdk/sun/nio/ch/DatagramChannelImpl.java @@ -43,12 +43,6 @@ class DatagramChannelImpl extends DatagramChannel implements SelChImpl { - // Windows 2000 introduced a "feature" that causes it to return WSAECONNRESET from receive, - // if a previous send resulted in an ICMP port unreachable. We disable this feature by using - // this ioctl. - private static final int IOC_IN = (int)0x80000000; - private static final int IOC_VENDOR = 0x18000000; - private static final int SIO_UDP_CONNRESET = IOC_IN | IOC_VENDOR | 12; // Used to make native read and write calls private static NativeDispatcher nd = new SocketDispatcher(); @@ -115,15 +109,6 @@ class DatagramChannelImpl this.fd = Net.socket(family, false); this.fdVal = IOUtil.fdVal(fd); this.state = ST_UNCONNECTED; - try - { - if (false) throw new cli.System.Net.Sockets.SocketException(); - fd.getSocket().IOControl(SIO_UDP_CONNRESET, new byte[] { 0 }, null); - } - catch (cli.System.Net.Sockets.SocketException x) - { - throw SocketUtil.convertSocketExceptionToIOException(x); - } } catch (IOException ioe) { ResourceManager.afterUdpClose(); throw ioe; @@ -151,15 +136,6 @@ class DatagramChannelImpl this.fd = Net.socket(family, false); this.fdVal = IOUtil.fdVal(fd); this.state = ST_UNCONNECTED; - try - { - if (false) throw new cli.System.Net.Sockets.SocketException(); - fd.getSocket().IOControl(SIO_UDP_CONNRESET, new byte[] { 0 }, null); - } - catch (cli.System.Net.Sockets.SocketException x) - { - throw SocketUtil.convertSocketExceptionToIOException(x); - } } public DatagramChannelImpl(SelectorProvider sp, FileDescriptor fd) @@ -337,7 +313,7 @@ class DatagramChannelImpl throw new ClosedChannelException(); } - private SocketAddress sender; // Set by receive0 (## ugh) + SocketAddress sender; // Set by receive0 (## ugh) public SocketAddress receive(ByteBuffer dst) throws IOException { if (dst.isReadOnly()) @@ -359,7 +335,7 @@ class DatagramChannelImpl readerThread = NativeThread.current(); if (isConnected() || (security == null)) { do { - n = receive0(dst); + n = receive(fd, dst); } while ((n == IOStatus.INTERRUPTED) && isOpen()); if (n == IOStatus.UNAVAILABLE) return null; @@ -367,7 +343,7 @@ class DatagramChannelImpl bb = ByteBuffer.allocate(dst.remaining()); for (;;) { do { - n = receive0(bb); + n = receive(fd, bb); } while ((n == IOStatus.INTERRUPTED) && isOpen()); if (n == IOStatus.UNAVAILABLE) return null; @@ -396,6 +372,42 @@ class DatagramChannelImpl } } + private int receive(FileDescriptor fd, ByteBuffer dst) + throws IOException + { + int pos = dst.position(); + int lim = dst.limit(); + assert (pos <= lim); + int rem = (pos <= lim ? lim - pos : 0); + if (dst.hasArray() && rem > 0) + return receiveIntoManagedBuffer(fd, dst, rem, pos); + + // Substitute a managed buffer. If the supplied buffer is empty + // we must instead use a nonempty buffer, otherwise the call + // will not block waiting for a datagram on some platforms. + int newSize = Math.max(rem, 1); + ByteBuffer bb = ByteBuffer.allocate(newSize); + try { + int n = receiveIntoManagedBuffer(fd, bb, newSize, 0); + bb.flip(); + if (n > 0 && rem > 0) + dst.put(bb); + return n; + } finally { + } + } + + private int receiveIntoManagedBuffer(FileDescriptor fd, ByteBuffer bb, + int rem, int pos) + throws IOException + { + int n = receive0(fd, bb.array(), bb.arrayOffset() + pos, rem, + isConnected()); + if (n > 0) + bb.position(pos + n); + return n; + } + public int send(ByteBuffer src, SocketAddress target) throws IOException { @@ -437,7 +449,7 @@ class DatagramChannelImpl return 0; writerThread = NativeThread.current(); do { - n = sendImpl(src, isa); + n = send(fd, src, target); } while ((n == IOStatus.INTERRUPTED) && isOpen()); synchronized (stateLock) { @@ -454,6 +466,52 @@ class DatagramChannelImpl } } + private int send(FileDescriptor fd, ByteBuffer src, SocketAddress target) + throws IOException + { + if (src.hasArray()) + return sendFromManagedBuffer(fd, src, target); + + // Substitute a managed buffer + int pos = src.position(); + int lim = src.limit(); + assert (pos <= lim); + int rem = (pos <= lim ? lim - pos : 0); + + ByteBuffer bb = ByteBuffer.allocate(rem); + try { + bb.put(src); + bb.flip(); + // Do not update src until we see how many bytes were written + src.position(pos); + + int n = sendFromManagedBuffer(fd, bb, target); + if (n > 0) { + // now update src + src.position(pos + n); + } + return n; + } finally { + } + } + + private int sendFromManagedBuffer(FileDescriptor fd, ByteBuffer bb, + SocketAddress target) + throws IOException + { + int pos = bb.position(); + int lim = bb.limit(); + assert (pos <= lim); + int rem = (pos <= lim ? lim - pos : 0); + + boolean preferIPv6 = (family != StandardProtocolFamily.INET); + int written = send0(preferIPv6, fd, bb.array(), bb.arrayOffset() + pos, + rem, target); + if (written > 0) + bb.position(pos + written); + return written; + } + public int read(ByteBuffer buf) throws IOException { if (buf == null) throw new NullPointerException(); @@ -642,21 +700,12 @@ class DatagramChannelImpl if (sm != null) sm.checkConnect(isa.getAddress().getHostAddress(), isa.getPort()); - try - { - if (false) throw new cli.System.Net.Sockets.SocketException(); - if (false) throw new cli.System.ObjectDisposedException(""); - fd.getSocket().Connect(SocketUtil.getAddressFromInetAddress(isa.getAddress()), isa.getPort()); - fd.getSocket().IOControl(SIO_UDP_CONNRESET, new byte[] { 1 }, null); - } - catch (cli.System.Net.Sockets.SocketException x) - { - throw new SocketException(x.getMessage()); - } - catch (cli.System.ObjectDisposedException x1) - { - throw new SocketException("Socket is closed"); - } + int n = Net.connect(family, + fd, + isa.getAddress(), + isa.getPort()); + if (n <= 0) + throw new Error(); // Can't happen // Connection succeeded; disallow further invocation state = ST_CONNECTED; @@ -1010,131 +1059,22 @@ class DatagramChannelImpl // -- Native methods -- - private static void disconnect0(FileDescriptor fd) throws IOException - { - try - { - if (false) throw new cli.System.Net.Sockets.SocketException(); - if (false) throw new cli.System.ObjectDisposedException(""); - fd.getSocket().Connect(new cli.System.Net.IPEndPoint(cli.System.Net.IPAddress.Any, 0)); - fd.getSocket().IOControl(SIO_UDP_CONNRESET, new byte[] { 0 }, null); - } - catch (cli.System.Net.Sockets.SocketException x) - { - throw SocketUtil.convertSocketExceptionToIOException(x); - } - catch (cli.System.ObjectDisposedException x1) - { - throw new SocketException("Socket is closed"); - } + private static native void initIDs(); + + private static native void disconnect0(FileDescriptor fd) + throws IOException; + + private native int receive0(FileDescriptor fd, byte[] buf, int pos, int len, + boolean connected) + throws IOException; + + private native int send0(boolean preferIPv6, FileDescriptor fd, byte[] buf, int pos, int len, + SocketAddress sa) + throws IOException; + + static { + Util.load(); + initIDs(); } - private int receive0(ByteBuffer bb) throws IOException - { - byte[] buf = new byte[bb.remaining()]; - cli.System.Net.EndPoint[] remoteEP = new cli.System.Net.EndPoint[] - { - new cli.System.Net.IPEndPoint(0, 0) - }; - InetSocketAddress addr; - int length; - do - { - for (; ; ) - { - try - { - if (false) throw new cli.System.Net.Sockets.SocketException(); - if (false) throw new cli.System.ObjectDisposedException(""); - length = fd.getSocket().ReceiveFrom(buf, 0, buf.length, cli.System.Net.Sockets.SocketFlags.wrap(cli.System.Net.Sockets.SocketFlags.None), remoteEP); - break; - } - catch (cli.System.Net.Sockets.SocketException x) - { - if (x.get_ErrorCode() == SocketUtil.WSAECONNRESET) - { - // A previous send failed (i.e. the remote host responded with a ICMP that the port is closed) and - // the winsock stack helpfully lets us know this, but we only care about this when we're connected, - // otherwise we'll simply retry the receive (note that we use SIO_UDP_CONNRESET to prevent these - // WSAECONNRESET exceptions, but when switching from connected to disconnected, some can slip through). - if (isConnected()) - { - throw new PortUnreachableException(); - } - continue; - } - if (x.get_ErrorCode() == SocketUtil.WSAEMSGSIZE) - { - // The buffer size was too small for the packet, ReceiveFrom receives the part of the packet - // that fits in the buffer and then throws an exception, so we have to ignore the exception in this case. - length = buf.length; - break; - } - if (x.get_ErrorCode() == SocketUtil.WSAEWOULDBLOCK) - { - return IOStatus.UNAVAILABLE; - } - throw SocketUtil.convertSocketExceptionToIOException(x); - } - catch (cli.System.ObjectDisposedException x1) - { - throw new SocketException("Socket is closed"); - } - } - cli.System.Net.IPEndPoint ep = (cli.System.Net.IPEndPoint)remoteEP[0]; - addr = new InetSocketAddress(SocketUtil.getInetAddressFromIPEndPoint(ep), ep.get_Port()); - } while (remoteAddress != null && !addr.equals(remoteAddress)); - sender = addr; - bb.put(buf, 0, length); - return length; - } - - private int sendImpl(ByteBuffer bb, InetSocketAddress addr) throws IOException - { - try - { - if (false) throw new cli.System.Net.Sockets.SocketException(); - if (false) throw new cli.System.ObjectDisposedException(""); - int position = bb.position(); - byte[] buf; - int offset; - int length; - if (bb.hasArray()) - { - buf = bb.array(); - offset = bb.arrayOffset() + bb.position(); - length = bb.remaining(); - } - else - { - buf = new byte[bb.remaining()]; - offset = 0; - length = buf.length; - bb.get(buf); - bb.position(position); - } - int sent = fd.getSocket().SendTo(buf, offset, length, cli.System.Net.Sockets.SocketFlags.wrap(cli.System.Net.Sockets.SocketFlags.None), new cli.System.Net.IPEndPoint(SocketUtil.getAddressFromInetAddress(addr.getAddress()), addr.getPort())); - if (bb.hasArray()) - { - bb.position(position + sent); - } - else - { - bb.put(buf, 0, sent); - } - return sent; - } - catch (cli.System.Net.Sockets.SocketException x) - { - if (x.get_ErrorCode() == SocketUtil.WSAEWOULDBLOCK) - { - return IOStatus.UNAVAILABLE; - } - throw SocketUtil.convertSocketExceptionToIOException(x); - } - catch (cli.System.ObjectDisposedException x1) - { - throw new SocketException("Socket is closed"); - } - } } diff --git a/runtime/openjdk/sun.nio.ch.cs b/runtime/openjdk/sun.nio.ch.cs index ab44a410..aabc2725 100644 --- a/runtime/openjdk/sun.nio.ch.cs +++ b/runtime/openjdk/sun.nio.ch.cs @@ -28,6 +28,116 @@ using FileDescriptor = java.io.FileDescriptor; using InetAddress = java.net.InetAddress; using ByteBuffer = java.nio.ByteBuffer; +static class Java_sun_nio_ch_DatagramChannelImpl +{ + public static void initIDs() + { + } + + public static void disconnect0(FileDescriptor fd) + { +#if !FIRST_PASS + try + { + fd.getSocket().Connect(new System.Net.IPEndPoint(System.Net.IPAddress.Any, 0)); + IKVM.NativeCode.sun.nio.ch.Net.setConnectionReset(fd.getSocket(), false); + } + catch (System.Net.Sockets.SocketException x) + { + throw java.net.SocketUtil.convertSocketExceptionToIOException(x); + } + catch (ObjectDisposedException) + { + throw new java.net.SocketException("Socket is closed"); + } +#endif + } + + public static int receive0(object obj, FileDescriptor fd, byte[] buf, int pos, int len, bool connected) + { +#if FIRST_PASS + return 0; +#else + sun.nio.ch.DatagramChannelImpl impl = (sun.nio.ch.DatagramChannelImpl)obj; + java.net.SocketAddress remoteAddress = impl.remoteAddress(); + System.Net.EndPoint remoteEP = new System.Net.IPEndPoint(0, 0); + java.net.InetSocketAddress addr; + int length; + do + { + for (; ; ) + { + try + { + length = fd.getSocket().ReceiveFrom(buf, pos, len, System.Net.Sockets.SocketFlags.None, ref remoteEP); + break; + } + catch (System.Net.Sockets.SocketException x) + { + if (x.ErrorCode == java.net.SocketUtil.WSAECONNRESET) + { + // A previous send failed (i.e. the remote host responded with a ICMP that the port is closed) and + // the winsock stack helpfully lets us know this, but we only care about this when we're connected, + // otherwise we'll simply retry the receive (note that we use SIO_UDP_CONNRESET to prevent these + // WSAECONNRESET exceptions, but when switching from connected to disconnected, some can slip through). + if (connected) + { + throw new java.net.PortUnreachableException(); + } + continue; + } + if (x.ErrorCode == java.net.SocketUtil.WSAEMSGSIZE) + { + // The buffer size was too small for the packet, ReceiveFrom receives the part of the packet + // that fits in the buffer and then throws an exception, so we have to ignore the exception in this case. + length = len; + break; + } + if (x.ErrorCode == java.net.SocketUtil.WSAEWOULDBLOCK) + { + return sun.nio.ch.IOStatus.UNAVAILABLE; + } + throw java.net.SocketUtil.convertSocketExceptionToIOException(x); + } + catch (ObjectDisposedException) + { + throw new java.net.SocketException("Socket is closed"); + } + } + System.Net.IPEndPoint ep = (System.Net.IPEndPoint)remoteEP; + addr = new java.net.InetSocketAddress(java.net.SocketUtil.getInetAddressFromIPEndPoint(ep), ep.Port); + } while (remoteAddress != null && !addr.equals(remoteAddress)); + impl.sender = addr; + return length; +#endif + } + + public static int send0(object obj, bool preferIPv6, FileDescriptor fd, byte[] buf, int pos, int len, object sa) + { +#if FIRST_PASS + return 0; +#else + java.net.InetSocketAddress addr = (java.net.InetSocketAddress)sa; + try + { + return fd.getSocket().SendTo(buf, pos, len, System.Net.Sockets.SocketFlags.None, new System.Net.IPEndPoint(java.net.SocketUtil.getAddressFromInetAddress(addr.getAddress()), addr.getPort())); + } + catch (System.Net.Sockets.SocketException x) + { + if (x.ErrorCode == java.net.SocketUtil.WSAEWOULDBLOCK) + { + return sun.nio.ch.IOStatus.UNAVAILABLE; + } + throw java.net.SocketUtil.convertSocketExceptionToIOException(x); + } + catch (ObjectDisposedException) + { + throw new java.net.SocketException("Socket is closed"); + } +#endif + } +} + #if !FIRST_PASS namespace IKVM.Internal.AsyncSocket { @@ -620,7 +730,6 @@ namespace IKVM.NativeCode.sun.nio.ch #else try { - FileDescriptor fd = new FileDescriptor(); System.Net.Sockets.AddressFamily addressFamily = preferIPv6 ? System.Net.Sockets.AddressFamily.InterNetworkV6 : System.Net.Sockets.AddressFamily.InterNetwork; @@ -630,7 +739,19 @@ namespace IKVM.NativeCode.sun.nio.ch System.Net.Sockets.ProtocolType protocolType = stream ? System.Net.Sockets.ProtocolType.Tcp : System.Net.Sockets.ProtocolType.Udp; - fd.setSocket(new System.Net.Sockets.Socket(addressFamily, socketType, protocolType)); + System.Net.Sockets.Socket socket = new System.Net.Sockets.Socket(addressFamily, socketType, protocolType); + if (preferIPv6) + { + // enable IPv4 over IPv6 sockets (note that we don't have to check for >= Vista here, because nio sockets only support IPv6 on >= Vista) + const System.Net.Sockets.SocketOptionName IPV6_V6ONLY = (System.Net.Sockets.SocketOptionName)27; + socket.SetSocketOption(System.Net.Sockets.SocketOptionLevel.IPv6, IPV6_V6ONLY, 0); + } + if (!stream) + { + setConnectionReset(socket, false); + } + FileDescriptor fd = new FileDescriptor(); + fd.setSocket(socket); return fd; } catch (System.Net.Sockets.SocketException x) @@ -676,6 +797,18 @@ namespace IKVM.NativeCode.sun.nio.ch #endif } + internal static void setConnectionReset(System.Net.Sockets.Socket socket, bool enable) + { + // Windows 2000 introduced a "feature" that causes it to return WSAECONNRESET from receive, + // if a previous send resulted in an ICMP port unreachable. For unconnected datagram sockets, + // we disable this feature by using this ioctl. + const int IOC_IN = unchecked((int)0x80000000); + const int IOC_VENDOR = 0x18000000; + const int SIO_UDP_CONNRESET = IOC_IN | IOC_VENDOR | 12; + + socket.IOControl(SIO_UDP_CONNRESET, new byte[] { enable ? (byte)1 : (byte)0 }, null); + } + public static int connect0(bool preferIPv6, FileDescriptor fd, InetAddress remote, int remotePort) { #if FIRST_PASS @@ -684,9 +817,14 @@ namespace IKVM.NativeCode.sun.nio.ch try { System.Net.IPEndPoint ep = new System.Net.IPEndPoint(global::java.net.SocketUtil.getAddressFromInetAddress(remote), remotePort); - if (fd.isSocketBlocking()) + bool datagram = fd.getSocket().SocketType == System.Net.Sockets.SocketType.Dgram; + if (datagram || fd.isSocketBlocking()) { fd.getSocket().Connect(ep); + if (datagram) + { + setConnectionReset(fd.getSocket(), true); + } return 1; } else