From 75d5f6b2e6c2a335880f8857a1ccb7956ff071cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Prante?= Date: Thu, 4 Apr 2024 19:11:50 +0200 Subject: [PATCH] make datagram unix sockets work for sd_notify --- gradle.properties | 2 +- gradle/test/junit5.gradle | 1 + net-security/src/test/java/module-info.java | 1 + net-socket/src/main/java/module-info.java | 2 - .../xbib/net/socket/notify/SystemdNotify.java | 22 ++-- .../org/xbib/net/socket/v4/Constants.java | 2 + .../org/xbib/net/socket/v4/SocketFactory.java | 12 +- .../socket/v4/bsd/NativeDatagramSocket.java | 117 ------------------ .../net/socket/v4/bsd/SocketStructure.java | 89 ------------- .../socket/v4/datagram/DatagramPacket.java | 6 +- .../org/xbib/net/socket/v4/unix/CLibrary.java | 97 +++++++++++++++ .../socket/v4/unix/NativeDatagramSocket.java | 14 +-- .../net/socket/v4/unix/NativeUnixSocket.java | 62 ++++++++++ ...ucture.java => SocketAddressInternet.java} | 11 +- .../net/socket/v4/unix/SocketAddressUnix.java | 36 ++++++ .../socket/v4/unix/UnixDatagramSocket.java | 101 +++++++++++++++ .../socket/v4/unix/UnixSocketInputStream.java | 69 +++++++++++ .../v4/unix/UnixSocketOutputStream.java | 45 +++++++ .../org/xbib/net/socket/v6/SocketFactory.java | 13 +- .../socket/v6/bsd/NativeDatagramSocket.java | 115 ----------------- .../net/socket/v6/bsd/SocketStructure.java | 90 -------------- net-socket/src/test/java/module-info.java | 2 +- ...ystemdTest.java => SystemdNotifyTest.java} | 5 +- 23 files changed, 448 insertions(+), 466 deletions(-) delete mode 100644 net-socket/src/main/java/org/xbib/net/socket/v4/bsd/NativeDatagramSocket.java delete mode 100644 net-socket/src/main/java/org/xbib/net/socket/v4/bsd/SocketStructure.java create mode 100644 net-socket/src/main/java/org/xbib/net/socket/v4/unix/CLibrary.java create mode 100644 net-socket/src/main/java/org/xbib/net/socket/v4/unix/NativeUnixSocket.java rename net-socket/src/main/java/org/xbib/net/socket/v4/unix/{SocketStructure.java => SocketAddressInternet.java} (86%) create mode 100644 net-socket/src/main/java/org/xbib/net/socket/v4/unix/SocketAddressUnix.java create mode 100644 net-socket/src/main/java/org/xbib/net/socket/v4/unix/UnixDatagramSocket.java create mode 100644 net-socket/src/main/java/org/xbib/net/socket/v4/unix/UnixSocketInputStream.java create mode 100644 net-socket/src/main/java/org/xbib/net/socket/v4/unix/UnixSocketOutputStream.java delete mode 100644 net-socket/src/main/java/org/xbib/net/socket/v6/bsd/NativeDatagramSocket.java delete mode 100644 net-socket/src/main/java/org/xbib/net/socket/v6/bsd/SocketStructure.java rename net-socket/src/test/java/org/xbib/net/socket/test/notify/{SystemdTest.java => SystemdNotifyTest.java} (78%) diff --git a/gradle.properties b/gradle.properties index 45f8bf5..e36b33a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,3 @@ group = org.xbib name = net -version = 4.3.0 +version = 4.3.1 diff --git a/gradle/test/junit5.gradle b/gradle/test/junit5.gradle index 6aaed77..395214d 100644 --- a/gradle/test/junit5.gradle +++ b/gradle/test/junit5.gradle @@ -19,6 +19,7 @@ test { '--add-opens=java.base/java.nio=ALL-UNNAMED', '--add-opens=java.base/java.util=ALL-UNNAMED' systemProperty 'java.util.logging.config.file', 'src/test/resources/logging.properties' + environment 'NOTIFY_SOCKET', '/run/systemd/notify' testLogging { events 'STARTED', 'PASSED', 'FAILED', 'SKIPPED' } diff --git a/net-security/src/test/java/module-info.java b/net-security/src/test/java/module-info.java index 2801713..05b1191 100644 --- a/net-security/src/test/java/module-info.java +++ b/net-security/src/test/java/module-info.java @@ -11,4 +11,5 @@ module org.xbib.net.security.test { exports org.xbib.net.security.test.eddsa.math.bigint; exports org.xbib.net.security.test.eddsa.math.ed25519; exports org.xbib.net.security.test.eddsa.spec; + uses org.xbib.net.security.CertificateProvider; } diff --git a/net-socket/src/main/java/module-info.java b/net-socket/src/main/java/module-info.java index 7db8f39..7c978ea 100644 --- a/net-socket/src/main/java/module-info.java +++ b/net-socket/src/main/java/module-info.java @@ -4,14 +4,12 @@ module org.xbib.net.socket { exports org.xbib.net.socket; exports org.xbib.net.socket.notify; exports org.xbib.net.socket.v4; - exports org.xbib.net.socket.v4.bsd; exports org.xbib.net.socket.v4.datagram; exports org.xbib.net.socket.v4.icmp; exports org.xbib.net.socket.v4.ip; exports org.xbib.net.socket.v4.ping; exports org.xbib.net.socket.v4.unix; exports org.xbib.net.socket.v6; - exports org.xbib.net.socket.v6.bsd; exports org.xbib.net.socket.v6.datagram; exports org.xbib.net.socket.v6.icmp; exports org.xbib.net.socket.v6.ping; diff --git a/net-socket/src/main/java/org/xbib/net/socket/notify/SystemdNotify.java b/net-socket/src/main/java/org/xbib/net/socket/notify/SystemdNotify.java index ced2f72..d9cdcde 100644 --- a/net-socket/src/main/java/org/xbib/net/socket/notify/SystemdNotify.java +++ b/net-socket/src/main/java/org/xbib/net/socket/notify/SystemdNotify.java @@ -1,16 +1,15 @@ package org.xbib.net.socket.notify; import java.io.IOException; -import java.net.StandardProtocolFamily; -import java.net.UnixDomainSocketAddress; -import java.nio.CharBuffer; -import java.nio.channels.SocketChannel; import java.nio.charset.StandardCharsets; -import java.nio.file.Path; -import java.nio.file.Paths; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.xbib.net.socket.v4.unix.UnixDatagramSocket; public class SystemdNotify { + private static final Logger logger = Logger.getLogger(SystemdNotify.class.getName()); + public SystemdNotify() { } @@ -21,12 +20,11 @@ public class SystemdNotify { public static void sendNotify(String text) throws IOException { String socketName = System.getenv("NOTIFY_SOCKET"); if (socketName != null) { - Path path = Paths.get(socketName); - UnixDomainSocketAddress socketAddress = UnixDomainSocketAddress.of(path); - try (SocketChannel channel = SocketChannel.open(StandardProtocolFamily.UNIX)) { - channel.connect(socketAddress); - channel.write(StandardCharsets.US_ASCII.encode(CharBuffer.wrap(text))); - } + UnixDatagramSocket socket = new UnixDatagramSocket(socketName); + socket.send(text.getBytes(StandardCharsets.US_ASCII), text.length()); + socket.close(); + } else { + logger.log(Level.WARNING, "no socket path in NOTIFY_SOCKET environment variable"); } } } diff --git a/net-socket/src/main/java/org/xbib/net/socket/v4/Constants.java b/net-socket/src/main/java/org/xbib/net/socket/v4/Constants.java index 62199b3..8d98ee5 100644 --- a/net-socket/src/main/java/org/xbib/net/socket/v4/Constants.java +++ b/net-socket/src/main/java/org/xbib/net/socket/v4/Constants.java @@ -2,6 +2,8 @@ package org.xbib.net.socket.v4; public interface Constants { + int AF_UNIX = 1; + int AF_INET = 2; int IPPROTO_IP = 0; diff --git a/net-socket/src/main/java/org/xbib/net/socket/v4/SocketFactory.java b/net-socket/src/main/java/org/xbib/net/socket/v4/SocketFactory.java index c416516..33b7545 100644 --- a/net-socket/src/main/java/org/xbib/net/socket/v4/SocketFactory.java +++ b/net-socket/src/main/java/org/xbib/net/socket/v4/SocketFactory.java @@ -1,13 +1,12 @@ package org.xbib.net.socket.v4; -import com.sun.jna.Platform; import org.xbib.net.socket.v4.datagram.DatagramSocket; import java.lang.reflect.InvocationTargetException; public class SocketFactory { - public static final int SOCK_DGRAM = Platform.isSolaris() ? 1 : 2; + public static final int SOCK_DGRAM = 2; private SocketFactory() { } @@ -23,13 +22,6 @@ public class SocketFactory { private static String getImplementationClassName() { - return "org.xbib.net.socket.v4." + getArchName() + ".NativeDatagramSocket"; - } - - private static String getArchName() { - return Platform.isWindows() ? "win32" - : Platform.isSolaris() ? "sun" - : (Platform.isMac() || Platform.isFreeBSD() || Platform.isOpenBSD()) ? "bsd" - : "unix"; + return "org.xbib.net.socket.v4.unix.NativeDatagramSocket"; } } diff --git a/net-socket/src/main/java/org/xbib/net/socket/v4/bsd/NativeDatagramSocket.java b/net-socket/src/main/java/org/xbib/net/socket/v4/bsd/NativeDatagramSocket.java deleted file mode 100644 index 5e86753..0000000 --- a/net-socket/src/main/java/org/xbib/net/socket/v4/bsd/NativeDatagramSocket.java +++ /dev/null @@ -1,117 +0,0 @@ -package org.xbib.net.socket.v4.bsd; - -import com.sun.jna.LastErrorException; -import com.sun.jna.Native; -import com.sun.jna.Pointer; -import com.sun.jna.ptr.IntByReference; -import org.xbib.net.socket.v4.datagram.DatagramPacket; -import org.xbib.net.socket.v4.datagram.DatagramSocket; -import java.io.IOException; -import java.nio.Buffer; -import java.nio.ByteBuffer; - -import static org.xbib.net.socket.v4.Constants.AF_INET; -import static org.xbib.net.socket.v4.Constants.IPPROTO_IP; -import static org.xbib.net.socket.v4.Constants.IP_MTU_DISCOVER; - -public class NativeDatagramSocket implements DatagramSocket, AutoCloseable { - - static { - Native.register((String) null); - } - - private static final int IP_TOS = 3; - - private final int socket; - - private volatile boolean closed; - - public NativeDatagramSocket(int type, int protocol, int port) { - this.socket = socket(AF_INET, type, protocol); - if (socket < 0) { - throw new IllegalStateException("socket < 0"); - } - SocketStructure socketStructure = new SocketStructure(port); - bind(socket, socketStructure, socketStructure.size()); - closed = false; - } - - public native int bind(int socket, SocketStructure address, int address_len) throws LastErrorException; - - public native int socket(int family, int type, int protocol) throws LastErrorException; - - public native int setsockopt(int socket, int level, int option_name, Pointer value, int len); - - public native int sendto(int socket, Buffer buffer, int buflen, int flags, SocketStructure address, int len) throws LastErrorException; - - public native int recvfrom(int socket, Buffer buffer, int buflen, int flags, SocketStructure address, int[] len) throws LastErrorException; - - public native int close(int socket) throws LastErrorException; - - public native String strerror(int errnum); - - @Override - public int setTrafficClass(int tc) throws IOException { - IntByReference ptr = new IntByReference(tc); - try { - return setsockopt(socket, IPPROTO_IP, IP_TOS, ptr.getPointer(), Native.POINTER_SIZE); - } catch (LastErrorException e) { - throw new IOException("setsockopt: " + strerror(e.getErrorCode())); - } - } - - @Override - public int setFragmentation(boolean frag) throws IOException { - return allowFragmentation(IPPROTO_IP, IP_MTU_DISCOVER, frag); - } - - private int allowFragmentation(int level, int option_name, boolean frag) throws IOException { - if (closed) { - return -1; - } - IntByReference dontfragment = new IntByReference(frag ? 0 : 1); - try { - return setsockopt(socket, level, option_name, dontfragment.getPointer(), Native.POINTER_SIZE); - } catch (LastErrorException e) { - throw new IOException("setsockopt: " + strerror(e.getErrorCode())); - } - } - - @Override - public int receive(DatagramPacket datagramPacket) { - if (closed) { - return -1; - } - try { - SocketStructure socketStructure = new SocketStructure(); - int[] szRef = new int[]{socketStructure.size()}; - ByteBuffer buf = datagramPacket.getContent(); - int n = recvfrom(socket, buf, buf.capacity(), 0, socketStructure, szRef); - datagramPacket.setLength(n); - datagramPacket.setAddressable(socketStructure); - return n; - } catch (LastErrorException e) { - if (e.getMessage().contains("[9]")) { - // bad file descriptor - return -1; - } - throw e; - } - } - - @Override - public int send(DatagramPacket datagramPacket) { - if (closed) { - return -1; - } - ByteBuffer buf = datagramPacket.getContent(); - SocketStructure socketStructure = new SocketStructure(datagramPacket.getAddress(), datagramPacket.getPort()); - return sendto(socket, buf, buf.remaining(), 0, socketStructure, socketStructure.size()); - } - - @Override - public void close() { - closed = true; - close(socket); - } -} diff --git a/net-socket/src/main/java/org/xbib/net/socket/v4/bsd/SocketStructure.java b/net-socket/src/main/java/org/xbib/net/socket/v4/bsd/SocketStructure.java deleted file mode 100644 index a89009e..0000000 --- a/net-socket/src/main/java/org/xbib/net/socket/v4/bsd/SocketStructure.java +++ /dev/null @@ -1,89 +0,0 @@ -package org.xbib.net.socket.v4.bsd; - -import static org.xbib.net.socket.v4.Constants.AF_INET; -import com.sun.jna.Structure; -import org.xbib.net.socket.v4.Addressable; -import java.net.Inet4Address; -import java.net.UnknownHostException; -import java.util.Arrays; -import java.util.List; - -public class SocketStructure extends Structure implements Addressable { - - public byte sin_len; - - public byte sin_family; - - public byte[] sin_port; - - public byte[] sin_addr; - - public byte[] sin_zero; - - public SocketStructure() { - this(0); - } - - public SocketStructure(int port) { - this(null, port); - } - - public SocketStructure(Inet4Address address, int port) { - this(AF_INET, address, port); - } - - public SocketStructure(int family, Inet4Address address, int port) { - this.sin_family = (byte) (0xff & family); - this.sin_zero = new byte[8]; - this.sin_len = (byte) (0xff & 16); - setAddress(address); - setPort(port); - } - - @Override - protected List getFieldOrder() { - return Arrays.asList("sin_len", "sin_family", "sin_port", "sin_addr", "sin_zero"); - } - - @Override - public Inet4Address getAddress() { - try { - return (Inet4Address) Inet4Address.getByAddress(sin_addr); - } catch (UnknownHostException e) { - return null; - } - } - - public void setAddress(Inet4Address address) { - if (address != null) { - byte[] addr = address.getAddress(); - assertLen("address", addr, 4); - sin_addr = addr; - } else { - sin_addr = new byte[] { 0, 0, 0, 0 }; - } - } - - @Override - public int getPort() { - int port = 0; - for (int i = 0; i < 2; i++) { - port = ((port << 8) | (sin_port[i] & 0xff)); - } - return port; - } - - public void setPort(int port) { - if (port >= 0) { - byte[] p = new byte[]{(byte) (0xff & (port >> 8)), (byte) (0xff & port)}; - assertLen("port", p, 2); - sin_port = p; - } - } - - private void assertLen(String field, byte[] addr, int len) { - if (addr.length != len) { - throw new IllegalArgumentException(field + " length must be " + len + " bytes"); - } - } -} diff --git a/net-socket/src/main/java/org/xbib/net/socket/v4/datagram/DatagramPacket.java b/net-socket/src/main/java/org/xbib/net/socket/v4/datagram/DatagramPacket.java index 96bf72b..495cdeb 100644 --- a/net-socket/src/main/java/org/xbib/net/socket/v4/datagram/DatagramPacket.java +++ b/net-socket/src/main/java/org/xbib/net/socket/v4/datagram/DatagramPacket.java @@ -41,12 +41,8 @@ public class DatagramPacket implements Addressable { }; } - public Addressable getAddressable() { - return addressable; - } - public Inet4Address getAddress() { - return addressable != null ?addressable.getAddress() : null; + return addressable != null ? addressable.getAddress() : null; } public int getPort() { diff --git a/net-socket/src/main/java/org/xbib/net/socket/v4/unix/CLibrary.java b/net-socket/src/main/java/org/xbib/net/socket/v4/unix/CLibrary.java new file mode 100644 index 0000000..ab41642 --- /dev/null +++ b/net-socket/src/main/java/org/xbib/net/socket/v4/unix/CLibrary.java @@ -0,0 +1,97 @@ +package org.xbib.net.socket.v4.unix; + +import com.sun.jna.LastErrorException; +import com.sun.jna.Native; +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.ptr.IntByReference; + +import java.nio.Buffer; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class CLibrary { + + private static final Logger logger = Logger.getLogger(CLibrary.class.getName()); + + public static final int AF_UNIX = 1; + + public static final int AF_INET = 2; + + public static final int SOCK_STREAM = 1; + + public static final int SOCK_DGRAM = 2; + + public static final int PROTOCOL = 0; + + public static final CLibrary clib = new CLibrary(); + + native public int fcntl(int fd, int cmd, int arg) throws LastErrorException; + + native public int ioctl(int fd, int cmd, byte[] arg) throws LastErrorException; + + native public int ioctl(int fd, int cmd, Pointer p) throws LastErrorException; + + native public int open(String path, int flags) throws LastErrorException; + + native public int close(int fd) throws LastErrorException; + + native public int write(int fd, Buffer buffer, int count) throws LastErrorException; + + native public int read(int fd, Buffer buffer, int count) throws LastErrorException; + + native public int socket(int domain, int type, int protocol) throws LastErrorException; + + native public int connect(int sockfd, SockAddrUn sockaddr, int addrlen) throws LastErrorException; + + native public int bind(int sockfd, SockAddrUn sockaddr, int addrlen) throws LastErrorException; + + native public int accept(int sockfd, SockAddrUn rem_addr, Pointer opt) throws LastErrorException; + + native public int listen(int sockfd, int channel) throws LastErrorException; + + native public int getsockopt(int s, int level, int optname, byte[] optval, IntByReference optlen); + + native public int setsockopt(int s, int level, int optname, byte[] optval, int optlen); + + native public int recv(int s, Buffer buf, int len, int flags) throws LastErrorException; + + native public int recvfrom(int s, Buffer buf, int len, int flags, SockAddrUn from, IntByReference fromlen); + + native public int send(int s, Buffer msg, int len, int flags) throws LastErrorException; + + native public int getpid() throws LastErrorException; + + static { + try { + Native.register("c"); + } catch (Exception e) { + logger.log(Level.WARNING, "Native.register(\"c\") failed", e); + } + } + + private CLibrary() { + } + + public static CLibrary getInstance() { + return clib; + } + + public static class SockAddrUn extends Structure implements Structure.ByReference { + + public short family = AF_UNIX; + + public byte[] addr = new byte[108]; + + public SockAddrUn(String path) { + System.arraycopy(path.getBytes(StandardCharsets.US_ASCII), 0, addr, 0, Math.min(108, path.length())); + } + + @Override + protected List getFieldOrder() { + return List.of("family", "addr"); + } + } +} diff --git a/net-socket/src/main/java/org/xbib/net/socket/v4/unix/NativeDatagramSocket.java b/net-socket/src/main/java/org/xbib/net/socket/v4/unix/NativeDatagramSocket.java index d2ce375..4c53b23 100644 --- a/net-socket/src/main/java/org/xbib/net/socket/v4/unix/NativeDatagramSocket.java +++ b/net-socket/src/main/java/org/xbib/net/socket/v4/unix/NativeDatagramSocket.java @@ -37,8 +37,8 @@ public class NativeDatagramSocket implements DatagramSocket, AutoCloseable { if (socket < 0) { throw new IllegalStateException("socket < 0"); } - SocketStructure socketStructure = new SocketStructure(port); - bind(socket, socketStructure, socketStructure.size()); + SocketAddressInternet socketAddressInternet = new SocketAddressInternet(port); + bind(socket, socketAddressInternet, socketAddressInternet.size()); closed = false; } catch (LastErrorException e) { logger.log(Level.SEVERE, e.getMessage() + ": check if sysctl -w net.ipv4.ping_group_range=\"0 65535\" and check for selinux security:\n" + @@ -48,15 +48,15 @@ public class NativeDatagramSocket implements DatagramSocket, AutoCloseable { } } - public native int bind(int socket, SocketStructure address, int address_len) throws LastErrorException; + public native int bind(int socket, SocketAddressInternet address, int address_len) throws LastErrorException; public native int socket(int domain, int type, int protocol) throws LastErrorException; public native int setsockopt(int socket, int level, int option_name, Pointer value, int option_len); - public native int sendto(int socket, Buffer buffer, int buflen, int flags, SocketStructure dest_addr, int dest_addr_len) throws LastErrorException; + public native int sendto(int socket, Buffer buffer, int buflen, int flags, SocketAddressInternet dest_addr, int dest_addr_len) throws LastErrorException; - public native int recvfrom(int socket, Buffer buffer, int buflen, int flags, SocketStructure in_addr, int[] in_addr_len) throws LastErrorException; + public native int recvfrom(int socket, Buffer buffer, int buflen, int flags, SocketAddressInternet in_addr, int[] in_addr_len) throws LastErrorException; public native int close(int socket) throws LastErrorException; @@ -97,7 +97,7 @@ public class NativeDatagramSocket implements DatagramSocket, AutoCloseable { if (closed) { return -1; } - SocketStructure in_addr = new SocketStructure(); + SocketAddressInternet in_addr = new SocketAddressInternet(); int[] szRef = new int[]{in_addr.size()}; ByteBuffer buf = datagramPacket.getContent(); int n = recvfrom(socket, buf, buf.capacity(), 0, in_addr, szRef); @@ -111,7 +111,7 @@ public class NativeDatagramSocket implements DatagramSocket, AutoCloseable { if (closed) { return -1; } - SocketStructure destAddr = new SocketStructure(datagramPacket.getAddress(), datagramPacket.getPort()); + SocketAddressInternet destAddr = new SocketAddressInternet(datagramPacket.getAddress(), datagramPacket.getPort()); ByteBuffer buf = datagramPacket.getContent(); try { return sendto(socket, buf, buf.remaining(), 0, destAddr, destAddr.size()); diff --git a/net-socket/src/main/java/org/xbib/net/socket/v4/unix/NativeUnixSocket.java b/net-socket/src/main/java/org/xbib/net/socket/v4/unix/NativeUnixSocket.java new file mode 100644 index 0000000..8f3ff90 --- /dev/null +++ b/net-socket/src/main/java/org/xbib/net/socket/v4/unix/NativeUnixSocket.java @@ -0,0 +1,62 @@ +package org.xbib.net.socket.v4.unix; + +import com.sun.jna.LastErrorException; +import com.sun.jna.Native; +import com.sun.jna.Pointer; +import java.io.IOException; +import java.net.Socket; +import java.nio.Buffer; +import java.nio.ByteBuffer; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.xbib.net.socket.NetworkUnreachableException; +import org.xbib.net.socket.v4.datagram.DatagramPacket; +import static org.xbib.net.socket.v4.Constants.AF_UNIX; + +public class NativeUnixSocket extends Socket { + + static { + Native.register((String) null); + } + + private static final Logger logger = Logger.getLogger(NativeUnixSocket.class.getName()); + + private final int socket; + + private volatile boolean closed; + + public NativeUnixSocket(String path, int type, int protocol) { + try { + this.socket = socket(AF_UNIX, type, protocol); + if (socket < 0) { + throw new IllegalStateException("socket < 0"); + } + SocketAddressUnix socketAddressUnix = new SocketAddressUnix(path); + connect(socket, socketAddressUnix); + closed = false; + } catch (LastErrorException e) { + logger.log(Level.SEVERE, e.getMessage(), e); + throw e; + } + } + + public native int connect(int socket, SocketAddressUnix socketAddressUnix) throws LastErrorException; + + public native int socket(int domain, int type, int protocol) throws LastErrorException; + + public native int setsockopt(int socket, int level, int option_name, Pointer value, int option_len); + + public native int sendto(int socket, Buffer buffer, int buflen, int flags, SocketAddressUnix socketAddressUnix) throws LastErrorException; + + public native int recvfrom(int socket, Buffer buffer, int buflen, int flags, SocketAddressInternet in_addr, int[] in_addr_len) throws LastErrorException; + + public native int close(int socket) throws LastErrorException; + + public native String strerror(int errnum); + @Override + public void close() throws IOException { + super.close(); + closed = true; + close(socket); + } +} diff --git a/net-socket/src/main/java/org/xbib/net/socket/v4/unix/SocketStructure.java b/net-socket/src/main/java/org/xbib/net/socket/v4/unix/SocketAddressInternet.java similarity index 86% rename from net-socket/src/main/java/org/xbib/net/socket/v4/unix/SocketStructure.java rename to net-socket/src/main/java/org/xbib/net/socket/v4/unix/SocketAddressInternet.java index 9fe3386..7beb0ed 100644 --- a/net-socket/src/main/java/org/xbib/net/socket/v4/unix/SocketStructure.java +++ b/net-socket/src/main/java/org/xbib/net/socket/v4/unix/SocketAddressInternet.java @@ -8,7 +8,7 @@ import java.net.UnknownHostException; import java.util.Arrays; import java.util.List; -public class SocketStructure extends Structure implements Addressable { +public class SocketAddressInternet extends Structure implements Addressable { public static final int AF_INET = 2; @@ -20,24 +20,25 @@ public class SocketStructure extends Structure implements Addressable { public byte[] sin_zero = new byte[8]; - public SocketStructure() { + public SocketAddressInternet() { this(AF_INET, null, 0); } - public SocketStructure(int port) { + public SocketAddressInternet(int port) { this(AF_INET, null, port); } - public SocketStructure(Inet4Address address, int port) { + public SocketAddressInternet(Inet4Address address, int port) { this(AF_INET, address, port); } - public SocketStructure(int family, Inet4Address address, int port) { + public SocketAddressInternet(int family, Inet4Address address, int port) { this.sin_family = (short) (0xffff & family); setAddress(address); setPort(port); } + @Override protected List getFieldOrder() { return Arrays.asList("sin_family", "sin_port", "sin_addr", "sin_zero"); diff --git a/net-socket/src/main/java/org/xbib/net/socket/v4/unix/SocketAddressUnix.java b/net-socket/src/main/java/org/xbib/net/socket/v4/unix/SocketAddressUnix.java new file mode 100644 index 0000000..8cc1365 --- /dev/null +++ b/net-socket/src/main/java/org/xbib/net/socket/v4/unix/SocketAddressUnix.java @@ -0,0 +1,36 @@ +package org.xbib.net.socket.v4.unix; + +import com.sun.jna.Structure; +import java.util.List; + +/** + * sockaddr_un + */ +public class SocketAddressUnix extends Structure { + + public short family; // AF_UNIX + + public final byte[] path = new byte[108]; + + public SocketAddressUnix(String path) { + this.family = 1; + setPath(path); + } + + public void setPath(String sunPath) { + System.arraycopy((sunPath + "\u0000").getBytes(), 0, this.path, 0, sunPath.length()); + } + + public short getFamily() { + return family; + } + + public byte[] getPath() { + return path; + } + + @Override + protected List getFieldOrder() { + return List.of("family", "path"); + } +} diff --git a/net-socket/src/main/java/org/xbib/net/socket/v4/unix/UnixDatagramSocket.java b/net-socket/src/main/java/org/xbib/net/socket/v4/unix/UnixDatagramSocket.java new file mode 100644 index 0000000..f03a967 --- /dev/null +++ b/net-socket/src/main/java/org/xbib/net/socket/v4/unix/UnixDatagramSocket.java @@ -0,0 +1,101 @@ +package org.xbib.net.socket.v4.unix; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; +import java.net.SocketException; +import java.nio.ByteBuffer; +import com.sun.jna.LastErrorException; +import static org.xbib.net.socket.v4.unix.CLibrary.AF_UNIX; +import static org.xbib.net.socket.v4.unix.CLibrary.PROTOCOL; +import static org.xbib.net.socket.v4.unix.CLibrary.SOCK_DGRAM; + +public class UnixDatagramSocket extends Socket { + + private final CLibrary library; + + private final int sockfd; + + private final CLibrary.SockAddrUn sockAddrUn; + + private InputStream is; + + private OutputStream os; + + public UnixDatagramSocket(String path) throws SocketException { + this.library = CLibrary.getInstance(); + this.sockfd = library.socket(AF_UNIX, SOCK_DGRAM, PROTOCOL); + this.sockAddrUn = new CLibrary.SockAddrUn(path); + int rc = connect(sockfd, sockAddrUn, sockAddrUn.size()); + if (rc != 0) { + throw new SocketException("can not connect"); + } + this.is = new UnixSocketInputStream(this); + this.os = new UnixSocketOutputStream(this); + } + + private int connect(int sockfd, CLibrary.SockAddrUn sockaddr, int addrlen) throws SocketException { + try { + return library.connect(sockfd, sockaddr, addrlen); + } catch (LastErrorException lee) { + throw new SocketException("connect: could not connect to socket", lee); + } + } + + public int read(byte[] buf, int count) throws IOException { + try { + ByteBuffer buffer = ByteBuffer.wrap(buf); + return library.read(sockfd, buffer, count); + } catch (LastErrorException lee) { + throw new IOException("read: could not read from socket", lee); + } + } + + public int write(byte[] buf, int count) throws IOException { + try { + return library.write(sockfd, ByteBuffer.wrap(buf), count); + } catch (LastErrorException lee) { + throw new IOException("write: could not write to socket", lee); + } + } + + public int send(byte[] buf, int count) throws IOException { + try { + return library.send(sockfd, ByteBuffer.wrap(buf), count, 0); + } catch (LastErrorException lee) { + throw new IOException("send: could not send to socket", lee); + } + } + + @Override + public InputStream getInputStream() throws IOException { + return is; + } + + @Override + public OutputStream getOutputStream() throws IOException { + return os; + } + + @Override + public void shutdownInput() throws IOException { + is = null; + } + + @Override + public void shutdownOutput() throws IOException { + os = null; + } + + @Override + public synchronized void close() throws IOException { + try { + shutdownInput(); + shutdownOutput(); + library.close(sockfd); + } catch (LastErrorException lee) { + throw new IOException("close: could not close socket", lee); + } + } +} diff --git a/net-socket/src/main/java/org/xbib/net/socket/v4/unix/UnixSocketInputStream.java b/net-socket/src/main/java/org/xbib/net/socket/v4/unix/UnixSocketInputStream.java new file mode 100644 index 0000000..adb4b6c --- /dev/null +++ b/net-socket/src/main/java/org/xbib/net/socket/v4/unix/UnixSocketInputStream.java @@ -0,0 +1,69 @@ +package org.xbib.net.socket.v4.unix; + +import java.io.IOException; +import java.io.InputStream; + +public class UnixSocketInputStream extends InputStream { + + private final UnixDatagramSocket unixDatagramSocket; + + public UnixSocketInputStream(UnixDatagramSocket unixDatagramSocket) { + this.unixDatagramSocket = unixDatagramSocket; + } + + @Override + public long skip(long n) throws IOException { + return -1; + } + + @Override + public synchronized void mark(int readlimit) { + // + } + + @Override + public synchronized void reset() throws IOException { + // + } + + @Override + public boolean markSupported() { + return false; + } + + @Override + public int available() throws IOException { + return -1; + } + + @Override + public int read() throws IOException { + byte[] data = new byte[1]; + int read = read(data); + if (read != 1) { + throw new IOException("read(..): could not read one byte"); + } + return data[0]; + } + + @Override + public int read(byte[] data) throws IOException { + return read(data, 0, data.length); + } + + @Override + public int read(byte[] data, int offset, int length) throws IOException { + int readLength = 0; + if (offset == 0) { + readLength = this.unixDatagramSocket.read(data, length); + } else { + throw new IOException("read(..): offset not supported"); + } + return readLength; + } + + @Override + public void close() throws IOException { + // + } +} diff --git a/net-socket/src/main/java/org/xbib/net/socket/v4/unix/UnixSocketOutputStream.java b/net-socket/src/main/java/org/xbib/net/socket/v4/unix/UnixSocketOutputStream.java new file mode 100644 index 0000000..100f6e0 --- /dev/null +++ b/net-socket/src/main/java/org/xbib/net/socket/v4/unix/UnixSocketOutputStream.java @@ -0,0 +1,45 @@ +package org.xbib.net.socket.v4.unix; + +import java.io.IOException; +import java.io.OutputStream; + +public class UnixSocketOutputStream extends OutputStream { + + private final UnixDatagramSocket unixDatagramSocket; + + public UnixSocketOutputStream(UnixDatagramSocket unixDatagramSocket) { + this.unixDatagramSocket = unixDatagramSocket; + } + + @Override + public void write(byte[] data) throws IOException { + write(data, 0, data.length); + } + + @Override + public void write(int data) throws IOException { + write(new byte[] { (byte) data }, 0, 1); + } + + @Override + public void write(byte[] data, int offset, int length) throws IOException { + if (offset == 0) { + int rc = unixDatagramSocket.write(data, length); + if (rc >= 0) { + throw new IOException("write failed"); + } + } else { + throw new IOException("write: offset not supported"); + } + } + + @Override + public void flush() throws IOException { + // + } + + @Override + public void close() throws IOException { + // + } +} \ No newline at end of file diff --git a/net-socket/src/main/java/org/xbib/net/socket/v6/SocketFactory.java b/net-socket/src/main/java/org/xbib/net/socket/v6/SocketFactory.java index 5a3c0f5..9aaf4dd 100644 --- a/net-socket/src/main/java/org/xbib/net/socket/v6/SocketFactory.java +++ b/net-socket/src/main/java/org/xbib/net/socket/v6/SocketFactory.java @@ -1,12 +1,11 @@ package org.xbib.net.socket.v6; -import com.sun.jna.Platform; import org.xbib.net.socket.v6.datagram.DatagramSocket; import java.lang.reflect.InvocationTargetException; public class SocketFactory { - public static final int SOCK_DGRAM = Platform.isSolaris() ? 1 : 2; + public static final int SOCK_DGRAM = 2; private SocketFactory() { } @@ -20,15 +19,7 @@ public class SocketFactory { .newInstance(SOCK_DGRAM, protocol, port); } - private static String getImplementationClassName() { - return "org.xbib.net.socket.v6." + getArch() + ".NativeDatagramSocket"; - } - - private static String getArch() { - return Platform.isWindows() ? "win32" - : Platform.isSolaris() ? "sun" - : (Platform.isMac() || Platform.isFreeBSD() || Platform.isOpenBSD()) ? "bsd" - : "unix"; + return "org.xbib.net.socket.v6.unix.NativeDatagramSocket"; } } diff --git a/net-socket/src/main/java/org/xbib/net/socket/v6/bsd/NativeDatagramSocket.java b/net-socket/src/main/java/org/xbib/net/socket/v6/bsd/NativeDatagramSocket.java deleted file mode 100644 index e49f8d9..0000000 --- a/net-socket/src/main/java/org/xbib/net/socket/v6/bsd/NativeDatagramSocket.java +++ /dev/null @@ -1,115 +0,0 @@ -package org.xbib.net.socket.v6.bsd; - -import static org.xbib.net.socket.v6.Constants.IPPROTO_IPV6; -import static org.xbib.net.socket.v6.Constants.IPV6_DONTFRAG; -import static org.xbib.net.socket.v6.Constants.IPV6_TCLASS; -import static org.xbib.net.socket.v6.Constants.AF_INET6; -import java.io.IOException; -import java.nio.Buffer; -import java.nio.ByteBuffer; -import com.sun.jna.LastErrorException; -import com.sun.jna.Native; -import com.sun.jna.Pointer; -import com.sun.jna.ptr.IntByReference; -import org.xbib.net.socket.v6.datagram.DatagramPacket; -import org.xbib.net.socket.v6.datagram.DatagramSocket; - -public class NativeDatagramSocket implements DatagramSocket, AutoCloseable { - - static { - Native.register((String) null); - } - - private final int socket; - - private volatile boolean closed; - - public NativeDatagramSocket(int type, int protocol, int port) { - this.socket = socket(AF_INET6, type, protocol); - if (socket < 0) { - throw new IllegalStateException("socket < 0"); - } - SocketStructure socketStructure = new SocketStructure(port); - bind(socket, socketStructure, socketStructure.size()); - closed = false; - } - - public native int bind(int socket, SocketStructure address, int address_len) throws LastErrorException; - - public native int socket(int family, int type, int protocol) throws LastErrorException; - - public native int setsockopt(int socket, int level, int option_name, Pointer value, int option_len); - - public native int sendto(int socket, Buffer buffer, int buflen, int flags, SocketStructure address, int dest_addr_len) throws LastErrorException; - - public native int recvfrom(int socket, Buffer buffer, int buflen, int flags, SocketStructure address, int[] in_addr_len) throws LastErrorException; - - public native int close(int socket) throws LastErrorException; - - public native String strerror(int errnum); - - @Override - public int setTrafficClass(int trafficClass) throws IOException { - IntByReference ptr = new IntByReference(trafficClass); - try { - return setsockopt(socket, IPPROTO_IPV6, IPV6_TCLASS, ptr.getPointer(), Native.POINTER_SIZE); - } catch (LastErrorException e) { - throw new IOException("setsockopt: " + strerror(e.getErrorCode())); - } - } - - @Override - public int setFragmentation(boolean frag) throws IOException { - return allowFragmentation(IPPROTO_IPV6, IPV6_DONTFRAG, frag); - } - - private int allowFragmentation(int level, int optionName, boolean frag) throws IOException { - if (closed) { - return -1; - } - IntByReference ptr = new IntByReference(frag ? 0 : 1); - try { - return setsockopt(socket, level, optionName, ptr.getPointer(), Native.POINTER_SIZE); - } catch (LastErrorException e) { - throw new IOException("setsockopt: " + strerror(e.getErrorCode())); - } - } - - @Override - public int receive(DatagramPacket datagramPacket) { - if (closed) { - return -1; - } - try { - SocketStructure socketStructure = new SocketStructure(); - int[] szRef = new int[]{socketStructure.size()}; - ByteBuffer buf = datagramPacket.getContent(); - int n = recvfrom(socket, buf, buf.capacity(), 0, socketStructure, szRef); - datagramPacket.setLength(n); - datagramPacket.setAddressable(socketStructure); - return n; - } catch (LastErrorException e) { - if (e.getMessage().contains("[9]")) { - // bad file descriptor - return -1; - } - throw e; - } - } - - @Override - public int send(DatagramPacket datagramPacket) { - if (closed) { - return -1; - } - ByteBuffer buf = datagramPacket.getContent(); - SocketStructure socketStructure = new SocketStructure(datagramPacket.getAddress(), datagramPacket.getPort()); - return sendto(socket, buf, buf.remaining(), 0, socketStructure, socketStructure.size()); - } - - @Override - public void close() { - closed = true; - close(socket); - } -} diff --git a/net-socket/src/main/java/org/xbib/net/socket/v6/bsd/SocketStructure.java b/net-socket/src/main/java/org/xbib/net/socket/v6/bsd/SocketStructure.java deleted file mode 100644 index 0e526ca..0000000 --- a/net-socket/src/main/java/org/xbib/net/socket/v6/bsd/SocketStructure.java +++ /dev/null @@ -1,90 +0,0 @@ -package org.xbib.net.socket.v6.bsd; - -import static org.xbib.net.socket.v6.Constants.AF_INET6; -import com.sun.jna.Structure; -import org.xbib.net.socket.v6.Addressable; -import java.net.Inet6Address; -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.util.Arrays; -import java.util.List; - -public class SocketStructure extends Structure implements Addressable { - - public byte sin6_len; - - public byte sin6_family; - - public byte[] sin6_port; - - public byte[] sin6_flowinfo; - - public byte[] sin6_addr; - - public byte[] sin6_scope_id; - - public SocketStructure() { - this(0); - } - - public SocketStructure(int port) { - this(null, port); - } - - public SocketStructure(Inet6Address address, int port) { - this(AF_INET6, address, port); - } - - public SocketStructure(int family, Inet6Address address, int port) { - sin6_family = (byte) (0xff & family); - sin6_scope_id = new byte[4]; - sin6_len = (byte) (0xff & 16); - sin6_flowinfo = new byte[4]; - setAddress(address); - setPort(port); - } - - @Override - protected List getFieldOrder() { - return Arrays.asList("sin6_len", "sin6_family", "sin6_port", "sin6_flowinfo", "sin6_addr", "sin6_scope_id"); - } - - public Inet6Address getAddress() { - try { - return (Inet6Address) InetAddress.getByAddress(sin6_addr); - } catch (UnknownHostException ex) { - return null; - } - } - - public void setAddress(Inet6Address address) { - if (address != null) { - byte[] addr = address.getAddress(); - assertLen("address", addr, 16); - sin6_addr = addr; - } else { - sin6_addr = new byte[] { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; - } - } - - public int getPort() { - int port = 0; - for (int i = 0; i < 2; i++) { - port = ((port << 8) | (sin6_port[i] & 0xff)); - } - return port; - } - - public void setPort(int port) { - byte[] p = new byte[]{(byte) (0xff & (port >> 8)), (byte) (0xff & port)}; - assertLen("port", p, 2); - sin6_port = p; - } - - private void assertLen(String field, byte[] addr, int len) { - if (addr.length != len) { - throw new IllegalArgumentException(field + " length must be " + len + " bytes but was " + addr.length + " bytes."); - } - } - -} diff --git a/net-socket/src/test/java/module-info.java b/net-socket/src/test/java/module-info.java index cf5007b..3547191 100644 --- a/net-socket/src/test/java/module-info.java +++ b/net-socket/src/test/java/module-info.java @@ -1,6 +1,6 @@ module org.xbib.net.socket.test { requires java.logging; - requires org.junit.jupiter.api; + requires transitive org.junit.jupiter.api; requires org.xbib.net.socket; exports org.xbib.net.socket.test.notify; exports org.xbib.net.socket.test.v4; diff --git a/net-socket/src/test/java/org/xbib/net/socket/test/notify/SystemdTest.java b/net-socket/src/test/java/org/xbib/net/socket/test/notify/SystemdNotifyTest.java similarity index 78% rename from net-socket/src/test/java/org/xbib/net/socket/test/notify/SystemdTest.java rename to net-socket/src/test/java/org/xbib/net/socket/test/notify/SystemdNotifyTest.java index f24ef2b..201f8f2 100644 --- a/net-socket/src/test/java/org/xbib/net/socket/test/notify/SystemdTest.java +++ b/net-socket/src/test/java/org/xbib/net/socket/test/notify/SystemdNotifyTest.java @@ -5,7 +5,10 @@ import org.xbib.net.socket.notify.SystemdNotify; import java.io.IOException; -public class SystemdTest { +public class SystemdNotifyTest { + + public SystemdNotifyTest() { + } @Test public void testSsystemd() throws IOException {