make datagram unix sockets work for sd_notify
This commit is contained in:
parent
96f47650b5
commit
75d5f6b2e6
23 changed files with 448 additions and 466 deletions
|
@ -1,3 +1,3 @@
|
|||
group = org.xbib
|
||||
name = net
|
||||
version = 4.3.0
|
||||
version = 4.3.1
|
||||
|
|
|
@ -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'
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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<String> 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");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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() {
|
||||
|
|
|
@ -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<String> getFieldOrder() {
|
||||
return List.of("family", "addr");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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<String> getFieldOrder() {
|
||||
return Arrays.asList("sin_family", "sin_port", "sin_addr", "sin_zero");
|
|
@ -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<String> getFieldOrder() {
|
||||
return List.of("family", "path");
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
//
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
//
|
||||
}
|
||||
}
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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<String> 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.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
Loading…
Reference in a new issue