AFSYSTEMSocketAddress.java
/*
* junixsocket
*
* Copyright 2009-2024 Christian Kohlschütter
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.newsclub.net.unix;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.InetAddress;
import java.net.SocketAddress;
import java.net.SocketException;
import java.net.URI;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.newsclub.net.unix.pool.ObjectPool.Lease;
/**
* An {@link AFSocketAddress} for AF_SYSTEM sockets.
*
* @author Christian Kohlschütter
*/
public final class AFSYSTEMSocketAddress extends AFSocketAddress {
private static final long serialVersionUID = 1L; // do not change!
private static AFAddressFamily<AFSYSTEMSocketAddress> afSystem;
private static final String SELECTOR_PROVIDER_CLASS =
"org.newsclub.net.unix.darwin.system.AFSYSTEMSelectorProvider";
/**
* The AF_SYSTEM system address.
*
* @author Christian Kohlschütter
*/
@NonNullByDefault
public static final class SysAddr extends NamedInteger {
private static final long serialVersionUID = 1L;
/**
* The sysaddr AF_SYS_CONTROL, also known as AF_SYS_KERN_CONTROL.
*/
public static final SysAddr AF_SYS_CONTROL;
private static final @NonNull SysAddr[] VALUES = init(new @NonNull SysAddr[] {
AF_SYS_CONTROL = new SysAddr("AF_SYS_CONTROL", 2) //
});
private SysAddr(int id) {
super(id);
}
private SysAddr(String name, int id) {
super(name, id);
}
/**
* Returns a {@link SysAddr} for the given custom value.
*
* @param v The value.
* @return The {@link SysAddr} object.
*/
public static SysAddr ofValue(int v) {
return ofValue(VALUES, SysAddr::new, v);
}
}
private AFSYSTEMSocketAddress(int port, final byte[] socketAddress,
Lease<ByteBuffer> nativeAddress) throws SocketException {
super(port, socketAddress, nativeAddress, addressFamily());
}
private static AFSYSTEMSocketAddress newAFSocketAddress(int port, final byte[] socketAddress,
Lease<ByteBuffer> nativeAddress) throws SocketException {
return newDeserializedAFSocketAddress(port, socketAddress, nativeAddress, addressFamily(),
AFSYSTEMSocketAddress::new);
}
/**
* Returns an {@link AFSYSTEMSocketAddress} that refers to a given AF_SYSTEM socket address (i.e.,
* referring to a particular socket instance instead of a service address). A Java-only "IP port
* number" is stored along the instance for compatibility reasons.
*
* @param javaPort The emulated "port" number (not part of AF_SYSTEM).
* @param sysAddr 16-bit system address (e.g., AF_SYS_KERNCONTROL)
* @param id Controller unique identifier
* @param unit Developer private unit number, 0 means "unspecified".
* @return A corresponding {@link AFSYSTEMSocketAddress} instance.
* @throws SocketException if the operation fails.
*/
public static AFSYSTEMSocketAddress ofSysAddrIdUnit(int javaPort, SysAddr sysAddr, int id,
int unit) throws SocketException {
return resolveAddress(toBytes(sysAddr, id, unit), javaPort, addressFamily());
}
/**
* Returns an {@link AFSYSTEMSocketAddress} that refers to a given AF_SYSTEM socket address (i.e.,
* referring to a particular socket instance instead of a service address).
*
* @param sysAddr 16-bit system address (e.g., AF_SYS_KERNCONTROL)
* @param id Controller unique identifier
* @param unit Developer private unit number
* @return A corresponding {@link AFSYSTEMSocketAddress} instance.
* @throws SocketException if the operation fails.
*/
public static AFSYSTEMSocketAddress ofSysAddrIdUnit(SysAddr sysAddr, int id, int unit)
throws SocketException {
return ofSysAddrIdUnit(0, sysAddr, id, unit);
}
/**
* Returns an {@link AFSYSTEMSocketAddress} given a special {@link InetAddress} that encodes the
* byte sequence of an AF_SYSTEM socket address, like those returned by {@link #wrapAddress()}.
*
* @param address The "special" {@link InetAddress}.
* @param port The port (use 0 for "none").
* @return The {@link AFSYSTEMSocketAddress} instance.
* @throws SocketException if the operation fails, for example when an unsupported address is
* specified.
*/
public static AFSYSTEMSocketAddress unwrap(InetAddress address, int port) throws SocketException {
return AFSocketAddress.unwrap(address, port, addressFamily());
}
/**
* Returns an {@link AFSYSTEMSocketAddress} given a special {@link InetAddress} hostname that
* encodes the byte sequence of an AF_SYSTEM socket address, like those returned by
* {@link #wrapAddress()}.
*
* @param hostname The "special" hostname, as provided by {@link InetAddress#getHostName()}.
* @param port The port (use 0 for "none").
* @return The {@link AFSYSTEMSocketAddress} instance.
* @throws SocketException if the operation fails, for example when an unsupported address is
* specified.
*/
public static AFSYSTEMSocketAddress unwrap(String hostname, int port) throws SocketException {
return AFSocketAddress.unwrap(hostname, port, addressFamily());
}
/**
* Returns an {@link AFSYSTEMSocketAddress} given a generic {@link SocketAddress}.
*
* @param address The address to unwrap.
* @return The {@link AFSYSTEMSocketAddress} instance.
* @throws SocketException if the operation fails, for example when an unsupported address is
* specified.
*/
public static AFSYSTEMSocketAddress unwrap(SocketAddress address) throws SocketException {
Objects.requireNonNull(address);
if (!isSupportedAddress(address)) {
throw new SocketException("Unsupported address");
}
return (AFSYSTEMSocketAddress) address;
}
@Override
public String toString() {
int port = getPort();
byte[] bytes = getBytes();
if (bytes.length != (8 * 4)) {
return getClass().getName() + "[" + (port == 0 ? "" : "port=" + port) + ";UNKNOWN" + "]";
}
ByteBuffer bb = ByteBuffer.wrap(bytes);
SysAddr sysAddr = SysAddr.ofValue(bb.getInt());
int id = bb.getInt();
int unit = bb.getInt();
return getClass().getName() + "[" + (port == 0 ? "" : "port=" + port + ";") + sysAddr + ";id="
+ id + ";unit=" + unit + "]";
}
/**
* Returns the "SysAddr" part of the address.
*
* @return The SysAddr part.
*/
public @NonNull SysAddr getSysAddr() {
ByteBuffer bb = ByteBuffer.wrap(getBytes());
return SysAddr.ofValue(bb.getInt(0));
}
/**
* Returns the "id" part of the address.
*
* @return The id part.
*/
public int getId() {
ByteBuffer bb = ByteBuffer.wrap(getBytes());
return bb.getInt(4);
}
/**
* Returns the "unit" part of the address.
*
* @return The unit part.
*/
public int getUnit() {
ByteBuffer bb = ByteBuffer.wrap(getBytes());
return bb.getInt(8);
}
@Override
public boolean hasFilename() {
return false;
}
@Override
public File getFile() throws FileNotFoundException {
throw new FileNotFoundException("no file");
}
/**
* Checks if an {@link InetAddress} can be unwrapped to an {@link AFSYSTEMSocketAddress}.
*
* @param addr The instance to check.
* @return {@code true} if so.
* @see #wrapAddress()
* @see #unwrap(InetAddress, int)
*/
public static boolean isSupportedAddress(InetAddress addr) {
return AFSocketAddress.isSupportedAddress(addr, addressFamily());
}
/**
* Checks if a {@link SocketAddress} can be unwrapped to an {@link AFSYSTEMSocketAddress}.
*
* @param addr The instance to check.
* @return {@code true} if so.
* @see #unwrap(InetAddress, int)
*/
public static boolean isSupportedAddress(SocketAddress addr) {
return (addr instanceof AFSYSTEMSocketAddress);
}
@SuppressWarnings("cast")
private static byte[] toBytes(SysAddr sysAddr, int id, int unit) {
ByteBuffer bb = ByteBuffer.allocate(8 * 4);
bb.putInt(sysAddr.value());
bb.putInt(id);
bb.putInt(unit);
bb.putInt(0);
bb.putInt(0);
bb.putInt(0);
bb.putInt(0);
bb.putInt(0);
return (byte[]) bb.flip().array();
}
/**
* Returns the corresponding {@link AFAddressFamily}.
*
* @return The address family instance.
*/
@SuppressWarnings("null")
public static synchronized AFAddressFamily<AFSYSTEMSocketAddress> addressFamily() {
if (afSystem == null) {
afSystem = AFAddressFamily.registerAddressFamily("system", //
AFSYSTEMSocketAddress.class, new AFSocketAddressConfig<AFSYSTEMSocketAddress>() {
private final AFSocketAddressConstructor<AFSYSTEMSocketAddress> addrConstr =
isUseDeserializationForInit() ? AFSYSTEMSocketAddress::newAFSocketAddress
: AFSYSTEMSocketAddress::new;
@Override
protected AFSYSTEMSocketAddress parseURI(URI u, int port) throws SocketException {
return AFSYSTEMSocketAddress.of(u, port);
}
@Override
protected AFSocketAddressConstructor<AFSYSTEMSocketAddress> addressConstructor() {
return addrConstr;
}
@Override
protected String selectorProviderClassname() {
return SELECTOR_PROVIDER_CLASS;
}
@Override
protected Set<String> uriSchemes() {
return new HashSet<>(Arrays.asList("afsystem"));
}
});
try {
Class.forName(SELECTOR_PROVIDER_CLASS);
} catch (ClassNotFoundException e) {
// ignore
}
}
return afSystem;
}
/**
* Returns an {@link AFSYSTEMSocketAddress} for the given URI, if possible.
*
* @param uri The URI.
* @return The address.
* @throws SocketException if the operation fails.
*/
@SuppressWarnings("PMD.ShortMethodName")
public static AFSYSTEMSocketAddress of(URI uri) throws SocketException {
return of(uri, -1);
}
/**
* Returns an {@link AFSYSTEMSocketAddress} for the given URI, if possible.
*
* @param uri The URI.
* @param overridePort The port to forcibly use, or {@code -1} for "don't override".
* @return The address.
* @throws SocketException if the operation fails.
*/
@SuppressWarnings({
"PMD.CognitiveComplexity", "PMD.CyclomaticComplexity", "PMD.ExcessiveMethodLength",
"PMD.NcssCount", "PMD.NPathComplexity", "PMD.ShortMethodName"})
public static AFSYSTEMSocketAddress of(URI uri, int overridePort) throws SocketException {
switch (uri.getScheme()) {
case "afsystem":
break;
default:
throw new SocketException("Unsupported URI scheme: " + uri.getScheme());
}
String host;
if ((host = uri.getHost()) == null || host.isEmpty()) {
String ssp = uri.getSchemeSpecificPart();
if (ssp == null || !ssp.startsWith("//")) {
throw new SocketException("Unsupported URI: " + uri);
}
ssp = ssp.substring(2);
int i = ssp.indexOf('/');
host = i == -1 ? ssp : ssp.substring(0, i);
if (host.isEmpty()) {
throw new SocketException("Unsupported URI: " + uri);
}
}
ByteBuffer bb = ByteBuffer.allocate(8 * 4);
for (String p : host.split("\\.")) {
int v;
try {
v = parseUnsignedInt(p, 10);
} catch (NumberFormatException e) {
throw (SocketException) new SocketException("Unsupported URI: " + uri).initCause(e);
}
bb.putInt(v);
}
bb.flip();
if (bb.remaining() > 8 * 4) {
throw new SocketException("Unsupported URI: " + uri);
}
/* SysAddr sa = */ SysAddr.ofValue(bb.getInt());
/* int id = */ bb.getInt();
/* int unit = */ bb.getInt();
while (bb.remaining() > 0) {
if (bb.getInt() != 0) {
throw new SocketException("Unsupported URI: " + uri);
}
}
return
resolveAddress(bb.array(), uri.getPort(), addressFamily());
}
@Override
@SuppressWarnings({"PMD.CognitiveComplexity", "PMD.CompareObjectsWithEquals"})
public URI toURI(String scheme, URI template) throws IOException {
switch (scheme) {
case "afsystem":
break;
default:
return super.toURI(scheme, template);
}
ByteBuffer bb = ByteBuffer.wrap(getBytes());
StringBuilder sb = new StringBuilder();
while (bb.remaining() > 0) {
sb.append(toUnsignedString(bb.getInt()));
if (bb.remaining() > 0) {
sb.append('.');
}
}
return new HostAndPort(sb.toString(), getPort()).toURI(scheme, template);
}
}