Unix domain sockets (UDS, AF_UNIX) in System.inheritedChannel() and elsewhere in Java
František Kučera
franta-java at frantovo.cz
Mon Jul 22 08:16:06 UTC 2019
Dne 21. 07. 19 v 20:13 Alan Bateman napsal(a):
> On 21/07/2019 11:01, František Kučera wrote:
>> I would also prefer String or Path in the Java API but
>> sockaddr_un.sun_path is defined in the POSIX standard as a byte array
>> and it might be not only a file path but there are also "abstract"
>> paths which have no representation on the filesystem – they start
>> with \0 null-byte and the abstract path is the whole rest of the
>> array. So lossless mapping between byte arrays and strings will be
>> difficult or maybe impossible (the "@" character is sometimes used in
>> the UI instead of the \0 but "@" is also valid character on the
>> filesystem path, so there are ambiguities). Although Java Sting can
>> hold the \0 bytes, depending on local encoding there might be edge
>> cases where the path is a valid byte array (sun_path) and is
>> addressable from the POSIX API but is not a valid String in given
>> encoding. Maybe there might be subclasses for abstract and
>> non-abstract addresses... but in the prototype, I looked for the
>> simplest solution (but buggy, I know, it e.g. includes all the \0s in
>> the toString() representation).
> The implementation of Path on Unix systems uses bytes so will preserve
> the original file path, you just need to be careful to avoid Path ->
> String round trips.
OK, I will try doing it with Path.
>> The System.inheritedChannel() is in the Java base – there might be
>> some indirect/optional dependency from the base to the module. Idea:
>> create an SPI interface with methods canCreateChannel(fd) and
>> createChannel(fd); the new module will implement this interface for
>> AF_UNIX file descriptors; and the System.inheritedChannel() will
>> lookup implementations of this interface and try to get the channel
>> from them; and if no implementation will return a channel, it will
>> fallback to the current implementation. Is it possible solution?
>>
>> May the java.net.UnixSocketAddress and
>> java.net.StandardProtocolFamily.UNIX stay where they are?
> For the prototype then you should be able to put UnixSocketAddress and
> UnixProtocolFamily in jdk.net too.
And the new prototype module should be jdk.net.uds or some other name?
> On naming, one of the discussion points will be whether it should be
> LocalSocketAddress as PF_UNIX is deprecated in favor of PF_LOCAL on
> some systems.
I considered both variants: UnixSocketAddress and LocalUnixAddress. I
choose the first one because words "local" and "remote" are used to
describe the sides of the socket and then we would have a local local
address and remote local address, which sounds bit weird and confusing.
On the other hand, PF_UNIX might be deprecated somewhere and UNIX is a
registered trademark and was particular product, but now these sockets
are used also on many not-unix system (just unix-like or even
unix-unlike systems). I am not sure which variant is better – none is
perfect and none is terribly wrong.
> A bigger discussion point will be whether to introduce new channel
> types rather than trying to have alternative implementations of
> SocketChannel/ServerSocketChannel.
Most of the code and the logic is the same for local sockets (stream and
datagram UDS) as for current internet sockets (TCP and UDP). My patch
extends the current code, because classes like ServerSocketChannelImpl
and SocketChannelImpl are not inherently linked to the AF_INET and
AF_INET6 – the names and interfaces are generic enough to cover
AF_UNIX/AF_LOCAL sockets. There are methods like "bind(SocketAddress
local)" instead of "bind(InetSocketAddress local)" and "SocketAddress
getRemoteAddress()" instead of "InetSocketAddress getRemoteAddress()"
which implies that it is not constrained to just the IP (INET, INET6)
and that there might be other types of addresses.
Other approach could be copy&paste&cleanup or from-the-scratch
re-implementation but it leads to code duplication. Maybe there might be
some common classes for most of the logic and then sub-classes for the
INET-specific and UNIX-specific parts...
> The approach that we've been taking with the RDMA API is to use the
> existing API and have the factory methods that that create the RDMA
> socket channel specify the differences.
UDS and RDMA might be different cases because RDMA has completely
different C API while UDS sockets use the same C functions as INET or
INET6 sockets.
>> My primary goal was to fix the wrong addresses (random IP and port)
>> returned from System.inheritedChannel()
> If fd 0 is a Unix domain socket then inheritedChannel should return
> null. Are you sure you it's returning a SocketChannel to something
> that is not a stream or datagram socket?
It is a stream or a datagram socket – just from different domain (UNIX
instead of INET or INET6) – but they work the same way. So yes, when I
create a stream or datagram unix domain socket e.g. in systemd (parent
process), I can inherit it in Java and get ServerSocketChannel or
DatagramChannel from System.inheritedChannel() and it really works.
Recently, I wrote an article about it: https://blog.frantovo.cz/c/372/ –
it is in Czech, but code and config examples should be readable to anyone.
I see this as a very useful feature. You can run e.g. Jetty or Tomcat as
a sub-process of your native application and communicate with it without
exposing it on TCP/IP and making that internal interface visible and
accessible to others. Or you can use Apache HTTPD as a reverse proxy and
connect it with Tomcat/Jetty without additional TCP ports (Apache
config: ProxyPass unix:/run/jetty|http://localhost/). Access rights (who
can connect) can be easily managed – just set the file permissions for
that socket (super-server like systemd can do this for you: SocketUser,
SocketGroup and SocketMode options) or just put it in a directory which
is accessible only by some users.
I used it in Java 8, but it works also in 14. And today I just tested
the 1.5.0_21 and I can confirm: it works even in Java 5.
> If so then then this is a bug that should be looked into.
The bug is that the channel returns wrong address in getRemoteAddress()
and getLocalAddress() methods. I fixed it in my patch so it returns
correct type of SocketAddress.
BTW: the inheritedChannel() JavaDoc says:
> In addition to the network-oriented channels described, this method
may return other kinds of channels in the future.
So inheriting of other sockets like UDS ones is IMHO not against the
specification or anything.
Franta
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.java.net/pipermail/net-dev/attachments/20190722/1cfa6fd6/attachment.html>
More information about the net-dev
mailing list