Hello,

I am interested in the unix domain sockets (UDS, AF_UNIX) support in Java.

Java is able to inherit a channel from the parent process and access it through System.inheritedChannel(). The channel is passed as an open file descriptor (FD) to Java form the parent process which is usually some superserver (like xinetd or systemd) or some other loader*. The channel might be both a ServerSocketChannel (accept() is called in Java) and a SocketChannel (accept() is called once in the parent process) or a DatagramChannel. And it might be TCP, UDP or UDS. But the UDS support is a bit incomplete.

I want to make the UDS support better so I wrote a proof-of-concept patch – see attached .diff and webrev: http://frantovo.cz/disk/openjdk-uds-08/ It still requires a lot of work (clean-up, refactoring, portability, security manager, documentation, testing…) but it seems feasible to me.

What already works in Java:

 * listening on an inherited UDS channel (ServerSocketChannel) – e.g.
   Jetty or Tomcat are able to listen and serve HTTP requests on UDS
   instead of TCP
 * receiving datagrams from UDS

What my proof-of-concept patch adds:

 * class UnixSocketAddress extends SocketAddress
 * support also for a single inherited UDS connection (SocketChannel)
 * getting correct local and remote addresses (instead of an
   InetAddress with some random IP and port)
 * creating new server and client UDS channels from Java

I am looking for a sponsor/mentor who will help me finish this task through wise advice and peer review.

My further plans:

 * finish the datagram part (sending responses to UDS does not work yet)
 * allow inheritance of multiple channels/sockets
 * getting client credentials (user name and group – where available)
 * maybe passing FDs through UDS (where available)
 * maybe FileChannel for accessing ordinary files inherited from the
   parent as FDs

Franta

*) might be just few lines in C e.g. https://blog.frantovo.cz/c/372/#toc_12

diff -r cff8aad2593f src/java.base/share/classes/java/net/ProtocolFamily.java
--- a/src/java.base/share/classes/java/net/ProtocolFamily.java	Fri Jul 19 16:25:04 2019 +0300
+++ b/src/java.base/share/classes/java/net/ProtocolFamily.java	Fri Jul 19 17:11:56 2019 +0200
@@ -27,6 +27,8 @@
 
 /**
  * Represents a family of communication protocols.
+ * 
+ * @see StandardProtocolFamily
  *
  * @since 1.7
  */
diff -r cff8aad2593f src/java.base/share/classes/java/net/StandardProtocolFamily.java
--- a/src/java.base/share/classes/java/net/StandardProtocolFamily.java	Fri Jul 19 16:25:04 2019 +0300
+++ b/src/java.base/share/classes/java/net/StandardProtocolFamily.java	Fri Jul 19 17:11:56 2019 +0200
@@ -41,5 +41,12 @@
     /**
      * Internet Protocol Version 6 (IPv6)
      */
-    INET6
+    INET6,
+    
+    /**
+     * Unix domain sockets (AF_UNIX / AF_LOCAL)
+     *
+     * @see UnixSocketAddress
+     */
+    UNIX
 }
diff -r cff8aad2593f src/java.base/share/classes/java/net/UnixSocketAddress.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/java/net/UnixSocketAddress.java	Fri Jul 19 17:11:56 2019 +0200
@@ -0,0 +1,60 @@
+package java.net;
+
+import java.util.Arrays;
+
+/**
+ * Immutable representation of a AF_UNIX address.
+ * 
+ * TODO: methods for detection of abstract addresses
+ * 
+ * @see StandardProtocolFamily#UNIX
+ * @author Ing. Frantisek Kucera (frantovo.cz)
+ */
+public class UnixSocketAddress extends SocketAddress {
+
+    private static final long serialVersionUID = 1l;
+
+    private final byte[] path;
+
+    /**
+     * Empty address -- used in case of unix domain socket client.
+     */
+    public UnixSocketAddress() {
+        this.path = null;
+    }
+
+    /**
+     * @param path raw path bytes in the platform encoding
+     */
+    public UnixSocketAddress(byte[] path) {
+        this.path = Arrays.copyOf(path, path.length);
+    }
+    
+    /**
+     * @param path String representation of the socket's path
+     */
+    public UnixSocketAddress(String path) {
+        this.path = path.getBytes();
+    }
+
+    /**
+     * @return String representation of the socket's path
+     */
+    public String getPath() {
+        return path == null ? null : new String(path);
+    }
+
+    /**
+     *
+     * @return original representation of the socket's path
+     */
+    public byte[] getPathBytes() {
+        return path == null ? null : Arrays.copyOf(path, path.length);
+    }
+
+    @Override
+    public String toString() {
+        return getClass().getSimpleName() + ": " + getPath();
+    }
+
+}
diff -r cff8aad2593f src/java.base/share/classes/java/nio/channels/ServerSocketChannel.java
--- a/src/java.base/share/classes/java/nio/channels/ServerSocketChannel.java	Fri Jul 19 16:25:04 2019 +0300
+++ b/src/java.base/share/classes/java/nio/channels/ServerSocketChannel.java	Fri Jul 19 17:11:56 2019 +0200
@@ -26,6 +26,7 @@
 package java.nio.channels;
 
 import java.io.IOException;
+import java.net.ProtocolFamily;
 import java.net.ServerSocket;
 import java.net.SocketOption;
 import java.net.SocketAddress;
@@ -112,6 +113,18 @@
     public static ServerSocketChannel open() throws IOException {
         return SelectorProvider.provider().openServerSocketChannel();
     }
+    
+    /**
+     * Opens a server-socket for the specific address family.
+     *
+     * @see #open()
+     * @param family The protocol family e.g. AF_UNIX
+     * @return A new socket channel
+     * @throws IOException If an I/O error occurs
+     */
+    public static ServerSocketChannel open(ProtocolFamily family) throws IOException {
+        return SelectorProvider.provider().openServerSocketChannel(family);
+    }
 
     /**
      * Returns an operation set identifying this channel's supported
diff -r cff8aad2593f src/java.base/share/classes/java/nio/channels/SocketChannel.java
--- a/src/java.base/share/classes/java/nio/channels/SocketChannel.java	Fri Jul 19 16:25:04 2019 +0300
+++ b/src/java.base/share/classes/java/nio/channels/SocketChannel.java	Fri Jul 19 17:11:56 2019 +0200
@@ -26,9 +26,12 @@
 package java.nio.channels;
 
 import java.io.IOException;
+import java.net.ProtocolFamily;
 import java.net.Socket;
 import java.net.SocketOption;
 import java.net.SocketAddress;
+import java.net.StandardProtocolFamily;
+import java.net.UnixSocketAddress;
 import java.nio.ByteBuffer;
 import java.nio.channels.spi.AbstractSelectableChannel;
 import java.nio.channels.spi.SelectorProvider;
@@ -149,6 +152,18 @@
     public static SocketChannel open() throws IOException {
         return SelectorProvider.provider().openSocketChannel();
     }
+    
+    /**
+     * Opens a socket channel for the specific address family.
+     *
+     * @see #open()
+     * @param family The protocol family e.g. AF_UNIX
+     * @return A new socket channel
+     * @throws IOException If an I/O error occurs
+     */
+    public static SocketChannel open(ProtocolFamily family) throws IOException {
+        return SelectorProvider.provider().openSocketChannel(family);
+    }
 
     /**
      * Opens a socket channel and connects it to a remote address.
@@ -189,7 +204,7 @@
     public static SocketChannel open(SocketAddress remote)
         throws IOException
     {
-        SocketChannel sc = open();
+        SocketChannel sc = remote instanceof UnixSocketAddress ? open(StandardProtocolFamily.UNIX) : open();
         try {
             sc.connect(remote);
         } catch (Throwable x) {
diff -r cff8aad2593f src/java.base/share/classes/java/nio/channels/spi/SelectorProvider.java
--- a/src/java.base/share/classes/java/nio/channels/spi/SelectorProvider.java	Fri Jul 19 16:25:04 2019 +0300
+++ b/src/java.base/share/classes/java/nio/channels/spi/SelectorProvider.java	Fri Jul 19 17:11:56 2019 +0200
@@ -247,6 +247,18 @@
      */
     public abstract ServerSocketChannel openServerSocketChannel()
         throws IOException;
+    
+    /**
+     * 
+     * @param family The protocol family
+     * 
+     * @return A new stream channel
+     * 
+     * @throws IOException
+     *         If an I/O error occurs
+     */
+    public abstract ServerSocketChannel openServerSocketChannel(ProtocolFamily family)
+        throws IOException;
 
     /**
      * Opens a socket channel.
@@ -260,6 +272,19 @@
         throws IOException;
 
     /**
+     * Opens a socket channel.
+     *
+     * @param family The protocol family
+     * 
+     * @return The new channel
+     * 
+     * @throws IOException 
+     *         If an I/O error occurs
+     */
+    public abstract SocketChannel openSocketChannel(ProtocolFamily family)
+        throws IOException;
+    
+    /**
      * Returns the channel inherited from the entity that created this
      * Java virtual machine.
      *
diff -r cff8aad2593f src/java.base/share/classes/sun/net/util/SocketExceptions.java
--- a/src/java.base/share/classes/sun/net/util/SocketExceptions.java	Fri Jul 19 16:25:04 2019 +0300
+++ b/src/java.base/share/classes/sun/net/util/SocketExceptions.java	Fri Jul 19 17:11:56 2019 +0200
@@ -28,6 +28,7 @@
 import java.io.IOException;
 import java.lang.reflect.Constructor;
 import java.net.InetSocketAddress;
+import java.net.SocketAddress;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
 
@@ -51,17 +52,21 @@
      *
      * Only specific IOException subtypes are supported.
      */
-    public static IOException of(IOException e, InetSocketAddress address) {
+    public static IOException of(IOException e, SocketAddress address) {
         if (!enhancedExceptionText || address == null)
             return e;
-        int port = address.getPort();
-        String host = address.getHostString();
         StringBuilder sb = new StringBuilder();
         sb.append(e.getMessage());
         sb.append(": ");
-        sb.append(host);
-        sb.append(':');
-        sb.append(Integer.toString(port));
+        if (address instanceof InetSocketAddress) {
+            int port = ((InetSocketAddress) address).getPort();
+            String host = ((InetSocketAddress) address).getHostString();
+            sb.append(host);
+            sb.append(':');
+            sb.append(Integer.toString(port));
+        } else {
+            sb.append(address);
+        }
         String enhancedMsg = sb.toString();
         return create(e, enhancedMsg);
     }
diff -r cff8aad2593f src/java.base/share/classes/sun/nio/ch/Net.java
--- a/src/java.base/share/classes/sun/nio/ch/Net.java	Fri Jul 19 16:25:04 2019 +0300
+++ b/src/java.base/share/classes/sun/nio/ch/Net.java	Fri Jul 19 17:11:56 2019 +0200
@@ -38,6 +38,7 @@
 import java.net.SocketOption;
 import java.net.StandardProtocolFamily;
 import java.net.StandardSocketOptions;
+import java.net.UnixSocketAddress;
 import java.net.UnknownHostException;
 import java.nio.channels.AlreadyBoundException;
 import java.nio.channels.ClosedChannelException;
@@ -439,19 +440,43 @@
     }
 
     static FileDescriptor socket(ProtocolFamily family, boolean stream) throws IOException {
-        boolean preferIPv6 = isIPv6Available() &&
-            (family != StandardProtocolFamily.INET);
-        return IOUtil.newFD(socket0(preferIPv6, stream, false, fastLoopback));
+        if (family == StandardProtocolFamily.UNIX) {
+            return IOUtil.newFD(socket1(stream));
+        } else {
+            boolean preferIPv6 = isIPv6Available() &&
+                (family != StandardProtocolFamily.INET);
+            return IOUtil.newFD(socket0(preferIPv6, stream, false, fastLoopback));
+        }
     }
 
+    static FileDescriptor serverSocket(boolean stream, ProtocolFamily family) {
+        if (family == StandardProtocolFamily.UNIX) {
+            return IOUtil.newFD(socket1(stream));
+        } else {
+            return serverSocket(stream);
+        }
+    }
+    
     static FileDescriptor serverSocket(boolean stream) {
         return IOUtil.newFD(socket0(isIPv6Available(), stream, true, fastLoopback));
     }
-
+    
     // Due to oddities SO_REUSEADDR on windows reuse is ignored
     private static native int socket0(boolean preferIPv6, boolean stream, boolean reuse,
                                       boolean fastLoopback);
+    
+    // TODO: rename, refactor, support maybe also SOCK_SEQPACKET, support reuse (delete socket file if exists)?
+    private static native int socket1(boolean stream);
 
+    public static void bind(FileDescriptor fd, UnixSocketAddress addr)
+        throws IOException
+    {
+        bind1(fd, addr.getPathBytes());
+    }
+    
+    // TODO: rename, UnixSocketAddress object instead of byte array?
+    private static native void bind1(FileDescriptor fd, byte[] addr) throws IOException;
+    
     public static void bind(FileDescriptor fd, InetAddress addr, int port)
         throws IOException
     {
@@ -481,6 +506,12 @@
     {
         return connect(UNSPEC, fd, remote, remotePort);
     }
+    
+    static int connect(FileDescriptor fd, UnixSocketAddress remote)
+        throws IOException
+    {
+        return connect1(fd, remote.getPathBytes());
+    }
 
     static int connect(ProtocolFamily family, FileDescriptor fd, InetAddress remote, int remotePort)
         throws IOException
@@ -498,10 +529,26 @@
                                        InetAddress remote,
                                        int remotePort)
         throws IOException;
+    
+    private static native int connect1(FileDescriptor fd, byte[] remote)
+        throws IOException;
 
+    public static int accept(FileDescriptor fd,
+                                    FileDescriptor newfd,
+                                    InetSocketAddress[] isaa)
+        throws IOException {
+        
+        SocketAddress[] array = new SocketAddress[isaa.length];
+        int result = accept(fd, newfd, array);
+        for (int i = 0; i < isaa.length; i++) {
+            isaa[i] = (InetSocketAddress) array[i];
+        }
+        return result;
+    }
+    
     public static native int accept(FileDescriptor fd,
                                     FileDescriptor newfd,
-                                    InetSocketAddress[] isaa)
+                                    SocketAddress[] isaa)
         throws IOException;
 
     public static final int SHUT_RD = 0;
@@ -521,7 +568,10 @@
     {
         return new InetSocketAddress(localInetAddress(fd), localPort(fd));
     }
-
+    
+    public static native SocketAddress localSocketAddress(FileDescriptor fd)
+        throws IOException;
+    
     private static native int remotePort(FileDescriptor fd)
         throws IOException;
 
diff -r cff8aad2593f src/java.base/share/classes/sun/nio/ch/SelectorProviderImpl.java
--- a/src/java.base/share/classes/sun/nio/ch/SelectorProviderImpl.java	Fri Jul 19 16:25:04 2019 +0300
+++ b/src/java.base/share/classes/sun/nio/ch/SelectorProviderImpl.java	Fri Jul 19 17:11:56 2019 +0200
@@ -56,7 +56,17 @@
         return new ServerSocketChannelImpl(this);
     }
 
+    @Override
+    public ServerSocketChannel openServerSocketChannel(ProtocolFamily family) throws IOException {
+        return new ServerSocketChannelImpl(this, family);
+    }
+
     public SocketChannel openSocketChannel() throws IOException {
         return new SocketChannelImpl(this);
     }
+
+    @Override
+    public SocketChannel openSocketChannel(ProtocolFamily family) throws IOException {
+        return new SocketChannelImpl(this, family);
+    }
 }
diff -r cff8aad2593f src/java.base/share/classes/sun/nio/ch/ServerSocketChannelImpl.java
--- a/src/java.base/share/classes/sun/nio/ch/ServerSocketChannelImpl.java	Fri Jul 19 16:25:04 2019 +0300
+++ b/src/java.base/share/classes/sun/nio/ch/ServerSocketChannelImpl.java	Fri Jul 19 17:11:56 2019 +0200
@@ -28,11 +28,13 @@
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.net.InetSocketAddress;
+import java.net.ProtocolFamily;
 import java.net.ServerSocket;
 import java.net.SocketAddress;
 import java.net.SocketOption;
 import java.net.SocketTimeoutException;
 import java.net.StandardSocketOptions;
+import java.net.UnixSocketAddress;
 import java.nio.channels.AlreadyBoundException;
 import java.nio.channels.AsynchronousCloseException;
 import java.nio.channels.ClosedChannelException;
@@ -85,7 +87,7 @@
     private long thread;
 
     // Binding
-    private InetSocketAddress localAddress; // null => unbound
+    private SocketAddress localAddress; // null => unbound
 
     // set true when exclusive binding is on and SO_REUSEADDR is emulated
     private boolean isReuseAddress;
@@ -96,6 +98,12 @@
     // -- End of fields protected by stateLock
 
 
+    ServerSocketChannelImpl(SelectorProvider sp, ProtocolFamily family) {
+        super(sp);
+        this.fd = Net.serverSocket(true, family);
+        this.fdVal = IOUtil.fdVal(fd);
+    }
+    
     ServerSocketChannelImpl(SelectorProvider sp) {
         super(sp);
         this.fd = Net.serverSocket(true);
@@ -136,7 +144,7 @@
             ensureOpen();
             return (localAddress == null)
                     ? null
-                    : Net.getRevealedLocalAddress(localAddress);
+                    : localAddress instanceof InetSocketAddress ? Net.getRevealedLocalAddress((InetSocketAddress)localAddress) : localAddress;
         }
     }
 
@@ -210,16 +218,23 @@
             ensureOpen();
             if (localAddress != null)
                 throw new AlreadyBoundException();
-            InetSocketAddress isa = (local == null)
-                                    ? new InetSocketAddress(0)
-                                    : Net.checkAddress(local);
-            SecurityManager sm = System.getSecurityManager();
-            if (sm != null)
-                sm.checkListen(isa.getPort());
-            NetHooks.beforeTcpBind(fd, isa.getAddress(), isa.getPort());
-            Net.bind(fd, isa.getAddress(), isa.getPort());
-            Net.listen(fd, backlog < 1 ? 50 : backlog);
-            localAddress = Net.localAddress(fd);
+            if (local instanceof UnixSocketAddress) {
+                // TODO: SecurityManager?
+                Net.bind(fd, (UnixSocketAddress)local);
+                Net.listen(fd, backlog < 1 ? 50 : backlog);
+                localAddress = local;
+            } else {
+                InetSocketAddress isa = (local == null)
+                                        ? new InetSocketAddress(0)
+                                        : Net.checkAddress(local);
+                SecurityManager sm = System.getSecurityManager();
+                if (sm != null)
+                    sm.checkListen(isa.getPort());
+                NetHooks.beforeTcpBind(fd, isa.getAddress(), isa.getPort());
+                Net.bind(fd, isa.getAddress(), isa.getPort());
+                Net.listen(fd, backlog < 1 ? 50 : backlog);
+                localAddress = Net.localAddress(fd);
+            }
         }
         return this;
     }
@@ -266,7 +281,7 @@
     public SocketChannel accept() throws IOException {
         int n = 0;
         FileDescriptor newfd = new FileDescriptor();
-        InetSocketAddress[] isaa = new InetSocketAddress[1];
+        SocketAddress[] isaa = new SocketAddress[1];
 
         acceptLock.lock();
         try {
@@ -308,7 +323,7 @@
     SocketChannel blockingAccept(long nanos) throws IOException {
         int n = 0;
         FileDescriptor newfd = new FileDescriptor();
-        InetSocketAddress[] isaa = new InetSocketAddress[1];
+        SocketAddress[] isaa = new SocketAddress[1];
 
         acceptLock.lock();
         try {
@@ -346,7 +361,7 @@
         return finishAccept(newfd, isaa[0]);
     }
 
-    private SocketChannel finishAccept(FileDescriptor newfd, InetSocketAddress isa)
+    private SocketChannel finishAccept(FileDescriptor newfd, SocketAddress isa)
         throws IOException
     {
         try {
@@ -355,8 +370,8 @@
 
             // check permitted to accept connections from the remote address
             SecurityManager sm = System.getSecurityManager();
-            if (sm != null) {
-                sm.checkAccept(isa.getAddress().getHostAddress(), isa.getPort());
+            if (sm != null && isa instanceof InetSocketAddress) { // TODO: SecurityManager: permissions for UDS?
+                sm.checkAccept(((InetSocketAddress) isa).getAddress().getHostAddress(), ((InetSocketAddress) isa).getPort());
             }
             return new SocketChannelImpl(provider(), newfd, isa);
         } catch (Exception e) {
@@ -490,7 +505,7 @@
      */
     InetSocketAddress localAddress() {
         synchronized (stateLock) {
-            return localAddress;
+            return localAddress instanceof InetSocketAddress ? (InetSocketAddress)localAddress : null; // TODO: refactoring/javadoc
         }
     }
 
@@ -549,6 +564,7 @@
         return fdVal;
     }
 
+    @Override
     public String toString() {
         StringBuilder sb = new StringBuilder();
         sb.append(this.getClass().getName());
@@ -557,11 +573,12 @@
             sb.append("closed");
         } else {
             synchronized (stateLock) {
-                InetSocketAddress addr = localAddress;
-                if (addr == null) {
+                if (localAddress == null) {
                     sb.append("unbound");
+                } else if (localAddress instanceof InetSocketAddress) {
+                    sb.append(Net.getRevealedLocalAddressAsString((InetSocketAddress)localAddress));
                 } else {
-                    sb.append(Net.getRevealedLocalAddressAsString(addr));
+                    sb.append(localAddress);
                 }
             }
         }
diff -r cff8aad2593f src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java
--- a/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java	Fri Jul 19 16:25:04 2019 +0300
+++ b/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java	Fri Jul 19 17:11:56 2019 +0200
@@ -37,6 +37,7 @@
 import java.net.SocketTimeoutException;
 import java.net.StandardProtocolFamily;
 import java.net.StandardSocketOptions;
+import java.net.UnixSocketAddress;
 import java.nio.ByteBuffer;
 import java.nio.channels.AlreadyBoundException;
 import java.nio.channels.AlreadyConnectedException;
@@ -110,14 +111,19 @@
     private long writerThread;
 
     // Binding
-    private InetSocketAddress localAddress;
-    private InetSocketAddress remoteAddress;
+    private SocketAddress localAddress;
+    private SocketAddress remoteAddress;
 
     // Socket adaptor, created on demand
     private Socket socket;
 
     // -- End of fields protected by stateLock
 
+    SocketChannelImpl(SelectorProvider sp, ProtocolFamily family) throws IOException {
+        super(sp);
+        this.fd = Net.socket(family, true);
+        this.fdVal = IOUtil.fdVal(fd);
+    }
 
     // Constructor for normal connecting sockets
     //
@@ -126,7 +132,7 @@
         this.fd = Net.socket(true);
         this.fdVal = IOUtil.fdVal(fd);
     }
-
+    
     SocketChannelImpl(SelectorProvider sp, FileDescriptor fd, boolean bound)
         throws IOException
     {
@@ -142,14 +148,14 @@
 
     // Constructor for sockets obtained from server sockets
     //
-    SocketChannelImpl(SelectorProvider sp, FileDescriptor fd, InetSocketAddress isa)
+    SocketChannelImpl(SelectorProvider sp, FileDescriptor fd, SocketAddress isa)
         throws IOException
     {
         super(sp);
         this.fd = fd;
         this.fdVal = IOUtil.fdVal(fd);
         synchronized (stateLock) {
-            this.localAddress = Net.localAddress(fd);
+            this.localAddress = Net.localSocketAddress(fd);
             this.remoteAddress = isa;
             this.state = ST_CONNECTED;
         }
@@ -199,7 +205,7 @@
     public SocketAddress getLocalAddress() throws IOException {
         synchronized (stateLock) {
             ensureOpen();
-            return Net.getRevealedLocalAddress(localAddress);
+            return localAddress instanceof InetSocketAddress ? Net.getRevealedLocalAddress((InetSocketAddress) localAddress) : localAddress;
         }
     }
 
@@ -588,7 +594,7 @@
      */
     InetSocketAddress localAddress() {
         synchronized (stateLock) {
-            return localAddress;
+            return localAddress instanceof InetSocketAddress ? (InetSocketAddress) localAddress : null;
         }
     }
 
@@ -597,7 +603,7 @@
      */
     InetSocketAddress remoteAddress() {
         synchronized (stateLock) {
-            return remoteAddress;
+            return remoteAddress instanceof InetSocketAddress ? (InetSocketAddress) remoteAddress : null;
         }
     }
 
@@ -678,6 +684,39 @@
             }
         }
     }
+    
+    /**
+     * TODO: refactoring, combine with InetSocketAddress method?
+     * 
+     * @param blocking
+     * @param usa
+     * @throws IOException 
+     */
+    private void beginConnect(boolean blocking, UnixSocketAddress usa)
+        throws IOException
+    {
+        if (blocking) {
+            // set hook for Thread.interrupt
+            begin();
+        }
+        synchronized (stateLock) {
+            ensureOpen();
+            int state = this.state;
+            if (state == ST_CONNECTED)
+                throw new AlreadyConnectedException();
+            if (state == ST_CONNECTIONPENDING)
+                throw new ConnectionPendingException();
+            assert state == ST_UNCONNECTED;
+            this.state = ST_CONNECTIONPENDING;
+
+            remoteAddress = usa;
+
+            if (blocking) {
+                // record thread so it can be signalled if needed
+                readerThread = NativeThread.current();
+            }
+        }
+    }
 
     /**
      * Marks the end of a connect operation that may have blocked.
@@ -705,6 +744,11 @@
      * Checks the remote address to which this channel is to be connected.
      */
     private InetSocketAddress checkRemote(SocketAddress sa) throws IOException {
+        if (sa instanceof UnixSocketAddress) {
+            // TODO: SecurityManager, refactor null
+            return null;
+        }
+        
         InetSocketAddress isa = Net.checkAddress(sa);
         SecurityManager sm = System.getSecurityManager();
         if (sm != null) {
@@ -728,8 +772,15 @@
                     boolean blocking = isBlocking();
                     boolean connected = false;
                     try {
-                        beginConnect(blocking, isa);
-                        int n = Net.connect(fd, isa.getAddress(), isa.getPort());
+                        int n;
+                        if (remote instanceof UnixSocketAddress) {
+                            UnixSocketAddress usa = (UnixSocketAddress)remote;
+                            beginConnect(blocking, usa);
+                            n = Net.connect(fd, usa);
+                        } else {
+                            beginConnect(blocking, isa);
+                            n = Net.connect(fd, isa.getAddress(), isa.getPort());
+                        }
                         if (n > 0) {
                             connected = true;
                         } else if (blocking) {
@@ -754,7 +805,7 @@
         } catch (IOException ioe) {
             // connect failed, close the channel
             close();
-            throw SocketExceptions.of(ioe, isa);
+            throw SocketExceptions.of(ioe, remote);
         }
     }
 
@@ -1044,11 +1095,21 @@
                         throw new IllegalBlockingModeException();
                     boolean connected = false;
                     try {
-                        beginConnect(true, isa);
+                        if (remote instanceof UnixSocketAddress) {
+                            UnixSocketAddress usa = (UnixSocketAddress)remote;
+                            beginConnect(true, usa);
+                        } else {
+                            beginConnect(true, isa);
+                        }
                         // change socket to non-blocking
                         lockedConfigureBlocking(false);
                         try {
-                            int n = Net.connect(fd, isa.getAddress(), isa.getPort());
+                            int n;
+                            if (remote instanceof UnixSocketAddress) {
+                                n = Net.connect(fd, (UnixSocketAddress) remote); // TODO: refactor null/remote/isa
+                            } else {
+                                n = Net.connect(fd, isa.getAddress(), isa.getPort());
+                            }
                             connected = (n > 0) ? true : finishTimedConnect(nanos);
                         } finally {
                             // restore socket to blocking mode
diff -r cff8aad2593f src/java.base/unix/classes/sun/nio/ch/InheritedChannel.java
--- a/src/java.base/unix/classes/sun/nio/ch/InheritedChannel.java	Fri Jul 19 16:25:04 2019 +0300
+++ b/src/java.base/unix/classes/sun/nio/ch/InheritedChannel.java	Fri Jul 19 17:11:56 2019 +0200
@@ -30,6 +30,8 @@
 import java.io.IOException;
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.net.UnixSocketAddress;
 import java.nio.channels.Channel;
 import java.nio.channels.SocketChannel;
 import java.nio.channels.ServerSocketChannel;
@@ -77,7 +79,7 @@
 
         InheritedSocketChannelImpl(SelectorProvider sp,
                                    FileDescriptor fd,
-                                   InetSocketAddress remote)
+                                   SocketAddress remote)
             throws IOException
         {
             super(sp, fd, remote);
@@ -182,14 +184,16 @@
 
         Channel c;
         if (st == SOCK_STREAM) {
-            InetAddress ia = peerAddress0(fdVal);
-            if (ia == null) {
+            SocketAddress sa = peerAddress0(fdVal);
+            if (sa == null) {
                c = new InheritedServerSocketChannelImpl(provider, fd);
+            } else if (sa instanceof InetSocketAddress) {
+               c = new InheritedSocketChannelImpl(provider, fd, sa);
+            } else if (sa instanceof UnixSocketAddress) {
+               // TODO: custom class for unix sockets
+               c = new InheritedSocketChannelImpl(provider, fd, sa);
             } else {
-               int port = peerPort0(fdVal);
-               assert port > 0;
-               InetSocketAddress isa = new InetSocketAddress(ia, port);
-               c = new InheritedSocketChannelImpl(provider, fd, isa);
+                throw new IllegalStateException("Unsupported socket type: " + sa.getClass().getName());
             }
         } else {
             c = new InheritedDatagramChannelImpl(provider, fd);
@@ -232,8 +236,7 @@
     private static native int open0(String path, int oflag) throws IOException;
     private static native void close0(int fd) throws IOException;
     private static native int soType0(int fd);
-    private static native InetAddress peerAddress0(int fd);
-    private static native int peerPort0(int fd);
+    private static native SocketAddress peerAddress0(int fd);
 
     static {
         IOUtil.load();
diff -r cff8aad2593f src/java.base/unix/native/libnet/net_util_md.h
--- a/src/java.base/unix/native/libnet/net_util_md.h	Fri Jul 19 16:25:04 2019 +0300
+++ b/src/java.base/unix/native/libnet/net_util_md.h	Fri Jul 19 17:11:56 2019 +0200
@@ -29,6 +29,7 @@
 #include <netdb.h>
 #include <poll.h>
 #include <sys/socket.h>
+#include <sys/un.h>
 
 /************************************************************************
  * Macros and constants
@@ -71,6 +72,7 @@
     struct sockaddr     sa;
     struct sockaddr_in  sa4;
     struct sockaddr_in6 sa6;
+    struct sockaddr_un  sau;
 } SOCKETADDRESS;
 
 /************************************************************************
diff -r cff8aad2593f src/java.base/unix/native/libnio/ch/InheritedChannel.c
--- a/src/java.base/unix/native/libnio/ch/InheritedChannel.c	Fri Jul 19 16:25:04 2019 +0300
+++ b/src/java.base/unix/native/libnio/ch/InheritedChannel.c	Fri Jul 19 17:11:56 2019 +0200
@@ -57,8 +57,18 @@
     jint remote_port;
 
     if (getpeername(fd, &sa.sa, &len) == 0) {
+        // TODO: rename remote_ia variable and matchFamily method
         if (matchFamily(&sa)) {
-            remote_ia = NET_SockaddrToInetAddress(env, &sa, (int *)&remote_port);
+            // TODO: move to some common function
+            jobject inetAddress = NET_SockaddrToInetAddress(env, &sa, (int *)&remote_port);
+            jclass isa_class = (*env)->FindClass(env, "java/net/InetSocketAddress");
+            jmethodID isa_ctor = (*env)->GetMethodID(env, isa_class, "<init>", "(Ljava/net/InetAddress;I)V");
+            remote_ia = (*env)->NewObject(env, isa_class, isa_ctor, inetAddress, remote_port);
+        } else if (sa.sa.sa_family == AF_UNIX) {
+            // TODO: move to some common function
+            jclass usa_class = (*env)->FindClass(env, "java/net/UnixSocketAddress");
+            jmethodID usa_ctor = (*env)->GetMethodID(env, usa_class, "<init>", "()V");
+            remote_ia = (*env)->NewObject(env, usa_class, usa_ctor);
         }
     }
 
@@ -66,22 +76,6 @@
 }
 
 JNIEXPORT jint JNICALL
-Java_sun_nio_ch_InheritedChannel_peerPort0(JNIEnv *env, jclass cla, jint fd)
-{
-    SOCKETADDRESS sa;
-    socklen_t len = sizeof(SOCKETADDRESS);
-    jint remote_port = -1;
-
-    if (getpeername(fd, &sa.sa, &len) == 0) {
-        if (matchFamily(&sa)) {
-            NET_SockaddrToInetAddress(env, &sa, (int *)&remote_port);
-        }
-    }
-
-    return remote_port;
-}
-
-JNIEXPORT jint JNICALL
 Java_sun_nio_ch_InheritedChannel_soType0(JNIEnv *env, jclass cla, jint fd)
 {
     int sotype;
diff -r cff8aad2593f src/java.base/unix/native/libnio/ch/Net.c
--- a/src/java.base/unix/native/libnio/ch/Net.c	Fri Jul 19 16:25:04 2019 +0300
+++ b/src/java.base/unix/native/libnio/ch/Net.c	Fri Jul 19 17:11:56 2019 +0200
@@ -148,6 +148,9 @@
 
 static jclass isa_class;        /* java.net.InetSocketAddress */
 static jmethodID isa_ctorID;    /* InetSocketAddress(InetAddress, int) */
+static jclass usa_class;        /* java.net.UnixSocketAddress */
+static jmethodID usa_ctor;      /* UnixSocketAddress() */
+static jmethodID usa_ctorBytes; /* UnixSocketAddress(byte[]) */
 
 JNIEXPORT void JNICALL
 Java_sun_nio_ch_Net_initIDs(JNIEnv *env, jclass clazz)
@@ -161,6 +164,18 @@
      }
      isa_ctorID = (*env)->GetMethodID(env, cls, "<init>", "(Ljava/net/InetAddress;I)V");
      CHECK_NULL(isa_ctorID);
+     
+     cls = (*env)->FindClass(env, "java/net/UnixSocketAddress");
+     CHECK_NULL(cls);
+     usa_class = (*env)->NewGlobalRef(env, cls);
+     if (usa_class == NULL) {
+         JNU_ThrowOutOfMemoryError(env, NULL);
+         return;
+     }
+     usa_ctor = (*env)->GetMethodID(env, cls, "<init>", "()V");
+     CHECK_NULL(usa_ctor);
+     usa_ctorBytes = (*env)->GetMethodID(env, cls, "<init>", "([B)V");
+     CHECK_NULL(usa_ctorBytes);
 
      initInetAddressIDs(env);
 }
@@ -273,6 +288,17 @@
     return fd;
 }
 
+JNIEXPORT jint JNICALL 
+Java_sun_nio_ch_Net_socket1 (JNIEnv *env, jclass clazz, jboolean stream) 
+{
+    int type = (stream ? SOCK_STREAM : SOCK_DGRAM);
+    int fd = socket(AF_UNIX, type, 0);
+    if (fd < 0) {
+        return handleSocketError(env, errno);
+    }
+    return fd;
+}
+
 JNIEXPORT void JNICALL
 Java_sun_nio_ch_Net_bind0(JNIEnv *env, jclass clazz, jobject fdo, jboolean preferIPv6,
                           jboolean useExclBind, jobject iao, int port)
@@ -293,6 +319,25 @@
 }
 
 JNIEXPORT void JNICALL
+Java_sun_nio_ch_Net_bind1(JNIEnv *env, jclass clazz, jobject fdo, jbyteArray uao)
+{
+    SOCKETADDRESS sa;
+    memset(&sa, 0x00, sizeof (sa));
+    sa.sau.sun_family = AF_UNIX;
+    size_t uao_len = (* env) -> GetArrayLength(env, uao);
+    size_t sa_len = sizeof (sa.sau.sun_path);
+    if (uao_len > sa_len) {
+        // TODO: throw propper exception
+        return;
+    }
+    (* env) -> GetByteArrayRegion(env, uao, 0, uao_len, (jbyte *) sa.sau.sun_path);
+    int rv = bind(fdval(env, fdo), &sa.sa, sa_len);
+    if (rv != 0) {
+        handleSocketError(env, errno);
+    }
+}
+
+JNIEXPORT void JNICALL
 Java_sun_nio_ch_Net_listen(JNIEnv *env, jclass cl, jobject fdo, jint backlog)
 {
     if (listen(fdval(env, fdo), backlog) < 0)
@@ -324,6 +369,33 @@
 }
 
 JNIEXPORT jint JNICALL
+Java_sun_nio_ch_Net_connect1(JNIEnv *env, jclass clazz, jobject fdo, jbyteArray uao)
+{
+    SOCKETADDRESS sa;
+    memset(&sa, 0x00, sizeof (sa));
+    sa.sau.sun_family = AF_UNIX;
+    size_t uao_len = (* env) -> GetArrayLength(env, uao);
+    size_t sa_len = sizeof (sa.sau.sun_path);
+    if (uao_len > sa_len) {
+        // TODO: throw propper exception
+        return IOS_UNAVAILABLE;
+    }
+    (* env) -> GetByteArrayRegion(env, uao, 0, uao_len, (jbyte *) sa.sau.sun_path);
+    
+    // TODO: refactoring, common method
+    int rv = connect(fdval(env, fdo), &sa.sa, sa_len);
+    if (rv != 0) {
+        if (errno == EINPROGRESS) {
+            return IOS_UNAVAILABLE;
+        } else if (errno == EINTR) {
+            return IOS_INTERRUPTED;
+        }
+        return handleSocketError(env, errno);
+    }
+    return 1;
+}
+
+JNIEXPORT jint JNICALL
 Java_sun_nio_ch_Net_accept(JNIEnv *env, jclass clazz, jobject fdo, jobject newfdo,
                            jobjectArray isaa)
 {
@@ -357,15 +429,24 @@
     }
 
     setfdval(env, newfdo, newfd);
+    
+    if (sa.sa.sa_family == AF_INET || sa.sa.sa_family == AF_INET6) {
+        remote_ia = NET_SockaddrToInetAddress(env, &sa, (int *)&remote_port);
+        CHECK_NULL_RETURN(remote_ia, IOS_THROWN);
 
-    remote_ia = NET_SockaddrToInetAddress(env, &sa, (int *)&remote_port);
-    CHECK_NULL_RETURN(remote_ia, IOS_THROWN);
+        isa = (*env)->NewObject(env, isa_class, isa_ctorID, remote_ia, remote_port);
+        CHECK_NULL_RETURN(isa, IOS_THROWN);
+        (*env)->SetObjectArrayElement(env, isaa, 0, isa);
 
-    isa = (*env)->NewObject(env, isa_class, isa_ctorID, remote_ia, remote_port);
-    CHECK_NULL_RETURN(isa, IOS_THROWN);
-    (*env)->SetObjectArrayElement(env, isaa, 0, isa);
-
-    return 1;
+        return 1;
+    } else if (sa.sa.sa_family == AF_UNIX) {
+        isa = (*env)->NewObject(env, usa_class, usa_ctor);
+        CHECK_NULL_RETURN(isa, IOS_THROWN);
+        (*env)->SetObjectArrayElement(env, isaa, 0, isa);
+        return 1;
+    } else {
+        return -6; // UNSUPPORTED_CASE
+    }
 }
 
 JNIEXPORT jint JNICALL
@@ -433,6 +514,29 @@
     return NET_SockaddrToInetAddress(env, &sa, &port);
 }
 
+JNIEXPORT jobject JNICALL
+Java_sun_nio_ch_Net_localSocketAddress(JNIEnv *env, jclass clazz, jobject fdo) 
+{
+    SOCKETADDRESS sa;
+    socklen_t sa_len = sizeof (SOCKETADDRESS);
+    memset(&sa, 0x00, sa_len);
+    if (getsockname(fdval(env, fdo), &sa.sa, &sa_len) != 0) {
+        return NULL; // TODO: exception?
+    }
+
+    if (sa.sa.sa_family == AF_UNIX) {
+        jbyteArray addr = (*env)->NewByteArray(env, sizeof(sa.sau.sun_path));
+        (*env)->SetByteArrayRegion(env, addr, 0, sizeof(sa.sau.sun_path), (jbyte *) sa.sau.sun_path);
+        return (*env)->NewObject(env, usa_class, usa_ctorBytes, addr);
+    } else if (sa.sa.sa_family == AF_INET || sa.sa.sa_family == AF_INET6) {
+        jobject address = Java_sun_nio_ch_Net_localInetAddress(env, clazz, fdo);
+        jint port = Java_sun_nio_ch_Net_localPort(env, clazz, fdo);
+        return (*env)->NewObject(env, isa_class, isa_ctorID, address, port);
+    } else {
+        return NULL; // TODO: exception?
+    }
+}
+
 JNIEXPORT jint JNICALL
 Java_sun_nio_ch_Net_remotePort(JNIEnv *env, jclass clazz, jobject fdo)
 {

Reply via email to