View Javadoc
1   /*
2    * junixsocket
3    *
4    * Copyright 2009-2026 Christian Kohlschütter
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.newsclub.net.unix;
19  
20  import java.io.ByteArrayInputStream;
21  import java.io.File;
22  import java.io.FileDescriptor;
23  import java.io.FileNotFoundException;
24  import java.io.IOException;
25  import java.io.ObjectInputStream;
26  import java.io.ObjectOutputStream;
27  import java.net.DatagramSocket;
28  import java.net.InetAddress;
29  import java.net.InetSocketAddress;
30  import java.net.ServerSocket;
31  import java.net.SocketAddress;
32  import java.net.SocketException;
33  import java.net.URI;
34  import java.nio.ByteBuffer;
35  import java.nio.charset.StandardCharsets;
36  import java.util.HashMap;
37  import java.util.Locale;
38  import java.util.Map;
39  import java.util.Objects;
40  
41  import org.eclipse.jdt.annotation.NonNull;
42  import org.eclipse.jdt.annotation.Nullable;
43  import org.newsclub.net.unix.pool.ObjectPool;
44  import org.newsclub.net.unix.pool.ObjectPool.Lease;
45  
46  import com.google.errorprone.annotations.Immutable;
47  import com.kohlschutter.annotations.compiletime.SuppressFBWarnings;
48  
49  /**
50   * Some {@link SocketAddress} that is supported by junixsocket, such as {@link AFUNIXSocketAddress}.
51   *
52   * @author Christian Kohlschütter
53   */
54  @Immutable
55  @SuppressWarnings({"PMD.CouplingBetweenObjects", "PMD.CyclomaticComplexity"})
56  public abstract class AFSocketAddress extends InetSocketAddress {
57    private static final long serialVersionUID = 1L; // do not change!
58  
59    /**
60     * Just a marker for "don't actually bind" (checked with "=="). Used in combination with a
61     * superclass' bind method, which should trigger "setBound()", etc.
62     */
63    static final AFSocketAddress INTERNAL_DUMMY_BIND = new SentinelSocketAddress(0);
64    static final AFSocketAddress INTERNAL_DUMMY_CONNECT = new SentinelSocketAddress(1);
65    static final AFSocketAddress INTERNAL_DUMMY_DONT_CONNECT = new SentinelSocketAddress(2);
66  
67    private static final int SOCKADDR_NATIVE_FAMILY_OFFSET = NativeUnixSocket.isLoaded() //
68        ? NativeUnixSocket.sockAddrNativeFamilyOffset() : -1;
69  
70    private static final int SOCKADDR_NATIVE_DATA_OFFSET = NativeUnixSocket.isLoaded() //
71        ? NativeUnixSocket.sockAddrNativeDataOffset() : -1;
72  
73    private static final int SOCKADDR_MAX_LEN = NativeUnixSocket.isLoaded() //
74        ? NativeUnixSocket.sockAddrLength(0) : 256;
75  
76    private static final Map<AFAddressFamily<?>, Map<Integer, Map<ByteBuffer, AFSocketAddress>>> ADDRESS_CACHE =
77        new HashMap<>();
78  
79    static final ObjectPool<ByteBuffer> SOCKETADDRESS_BUFFER_TL = ObjectPool.newThreadLocalPool(
80        () -> {
81          return AFSocketAddress.newSockAddrDirectBuffer(SOCKADDR_MAX_LEN);
82        }, (o) -> {
83          o.clear();
84          return true;
85        });
86  
87    private static final boolean USE_DESERIALIZATION_FOR_INIT;
88  
89    static {
90      String v = System.getProperty("org.newsclub.net.unix.AFSocketAddress.deserialize", "");
91      USE_DESERIALIZATION_FOR_INIT = v.isEmpty() ? NativeLibraryLoader.isAndroid() : Boolean
92          .parseBoolean(v);
93    }
94  
95    /**
96     * Some byte-level representation of this address, which can only be converted to a native
97     * representation in combination with the domain ID.
98     */
99    private byte[] bytes;
100 
101   /**
102    * An {@link InetAddress}-wrapped representation of this address. Only created upon demand.
103    */
104   private InetAddress inetAddress = null; // derived from bytes
105 
106   /**
107    * The system-native representation of this address, or {@code null}.
108    */
109   private transient ByteBuffer nativeAddress;
110 
111   /**
112    * The address family.
113    */
114   private transient AFAddressFamily<?> addressFamily;
115 
116   /**
117    * Creates a new socket address.
118    *
119    * @param port The port.
120    * @param socketAddress The socket address in junixsocket-specific byte-array representation.
121    * @param nativeAddress The socket address in system-native representation.
122    * @param af The address family.
123    * @throws SocketException on error.
124    */
125   @SuppressFBWarnings("CT_CONSTRUCTOR_THROW")
126   protected AFSocketAddress(int port, final byte[] socketAddress, Lease<ByteBuffer> nativeAddress,
127       AFAddressFamily<?> af) throws SocketException {
128     /*
129      * Initializing the superclass with an unresolved hostname helps us pass the #equals and
130      * #hashCode checks, which unfortunately are declared final in InetSocketAddress.
131      *
132      * Using a resolved address (with the address bit initialized) would be ideal, but resolved
133      * addresses can only be IPv4 or IPv6 (at least as of Java 16 and earlier).
134      */
135     super(AFInetAddress.createUnresolvedHostname(socketAddress, af), port >= 0 && port <= 0xffff
136         ? port : 0);
137     initAFSocketAddress(this, port, socketAddress, nativeAddress, af);
138   }
139 
140   /**
141    * Only for {@link SentinelSocketAddress}.
142    *
143    * @param clazz The {@link SentinelSocketAddress} class.
144    * @param port A sentinel port number.
145    */
146   @SuppressWarnings("PMD.UnusedFormalParameter")
147   AFSocketAddress(Class<SentinelSocketAddress> clazz, int port) {
148     super(InetAddress.getLoopbackAddress(), port);
149     this.nativeAddress = null;
150     this.bytes = new byte[0];
151     this.addressFamily = null;
152   }
153 
154   @SuppressWarnings({"cast", "this-escape"})
155   private static void initAFSocketAddress(AFSocketAddress addr, int port,
156       final byte[] socketAddress, Lease<ByteBuffer> nativeAddress, AFAddressFamily<?> af)
157       throws SocketException {
158     if (socketAddress.length == 0) {
159       throw new SocketException("Illegal address length: " + socketAddress.length);
160     }
161 
162     addr.nativeAddress = nativeAddress == null ? null : (ByteBuffer) (Object) nativeAddress.get()
163         .duplicate().rewind();
164     if (port < -1) {
165       throw new IllegalArgumentException("port out of range");
166     } else if (port > 0xffff) {
167       if (!NativeUnixSocket.isLoaded()) {
168         throw (SocketException) new SocketException(
169             "Cannot set SocketAddress port - junixsocket JNI library is not available").initCause(
170                 NativeUnixSocket.unsupportedException());
171       }
172       NativeUnixSocket.setPort1(addr, port);
173     }
174 
175     addr.bytes = socketAddress.clone();
176     addr.addressFamily = af;
177   }
178 
179   /**
180    * Returns a new {@link AFSocketAddress} instance via deserialization. This is a trick to
181    * workaround certain environments that do not allow the construction of {@link InetSocketAddress}
182    * instances without trying DNS resolution.
183    *
184    * @param <A> The subclass (must be a direct subclass of {@link AFSocketAddress}).
185    * @param port The port to use.
186    * @param socketAddress The junixsocket representation of the socket address.
187    * @param nativeAddress The system-native representation of the socket address, or {@code null}.
188    * @param af The address family, corresponding to the subclass
189    * @param constructor The constructor to use as fallback
190    * @return The new instance.
191    * @throws SocketException on error.
192    */
193   @SuppressFBWarnings("OBJECT_DESERIALIZATION") // we craft the serialized data
194   protected static <A extends AFSocketAddress> A newDeserializedAFSocketAddress(int port,
195       final byte[] socketAddress, Lease<ByteBuffer> nativeAddress, AFAddressFamily<A> af,
196       AFSocketAddressConstructor<A> constructor) throws SocketException {
197     String hostname = AFInetAddress.createUnresolvedHostname(socketAddress, af);
198     if (hostname == null || hostname.isEmpty()) {
199       return constructor.newAFSocketAddress(port, socketAddress, nativeAddress);
200     }
201     try (ObjectInputStream oin = new ObjectInputStream(new ByteArrayInputStream(AFSocketAddress
202         .craftSerializedObject(af.getSocketAddressClass(), hostname, (port >= 0 && port <= 0xffff
203             ? port : 0))))) {
204       @SuppressWarnings("unchecked")
205       A addr = (A) oin.readObject();
206       initAFSocketAddress(addr, port, socketAddress, nativeAddress, af);
207       return addr;
208     } catch (SocketException e) {
209       throw e;
210     } catch (ClassNotFoundException | IOException e) {
211       throw (SocketException) new SocketException("Unexpected deserialization problem").initCause(
212           e);
213     }
214   }
215 
216   /**
217    * Creates a byte-representation of a serialized {@link AFSocketAddress} instance, overriding
218    * hostname and port, which allows bypassing DNS resolution.
219    *
220    * @param className The actual subclass.
221    * @param hostname The hostname to use (must not be empty or null).
222    * @param port The port to use.
223    * @return The byte representation.
224    */
225   private static byte[] craftSerializedObject(Class<? extends AFSocketAddress> className,
226       String hostname, int port) {
227     ByteBuffer bb = ByteBuffer.allocate(768);
228     bb.putShort((short) 0xaced); // STREAM_MAGIC
229     bb.putShort((short) 5); // STREAM_VERSION
230     bb.put((byte) 0x73); // TC_OBJECT
231     bb.put((byte) 0x72); // TC_CLASSDESC
232 
233     putShortLengthUtf8(bb, className.getName());
234     bb.putLong(1); // serialVersionUID of subclass (expected to be 1)
235     bb.putInt(0x02000078);
236     bb.put((byte) 0x72);
237 
238     putShortLengthUtf8(bb, AFSocketAddress.class.getName());
239     bb.putLong(serialVersionUID); // serialVersionUID of AFSocketAddress
240     bb.putInt(0x0300025B);
241     putShortLengthUtf8(bb, "bytes");
242 
243     bb.putInt(0x7400025B);
244     bb.putShort((short) 0x424C);
245 
246     putShortLengthUtf8(bb, "inetAddress");
247     bb.put((byte) 0x74);
248 
249     putShortLengthEncodedClassName(bb, InetAddress.class);
250 
251     bb.putShort((short) 0x7872);
252     putShortLengthUtf8(bb, InetSocketAddress.class.getName());
253     bb.putLong(5076001401234631237L); // NOPMD InetSocketAddress serialVersionUID
254 
255     bb.putInt(0x03000349);
256     putShortLengthUtf8(bb, "port");
257 
258     bb.put((byte) 0x4C);
259     putShortLengthUtf8(bb, "addr");
260 
261     bb.putInt(0x71007E00);
262     bb.putShort((short) 0x034C);
263     putShortLengthUtf8(bb, "hostname");
264     bb.put((byte) 0x74);
265 
266     putShortLengthEncodedClassName(bb, String.class);
267 
268     bb.putShort((short) 0x7872);
269     putShortLengthUtf8(bb, SocketAddress.class.getName());
270     bb.putLong(5215720748342549866L); // NOPMD SocketAddress serialVersionUID
271 
272     bb.putInt(0x02000078);
273     bb.put((byte) 0x70);
274     bb.putInt(port);
275 
276     bb.putShort((short) 0x7074);
277     putShortLengthUtf8(bb, hostname);
278 
279     bb.putInt(0x78707077);
280     bb.put((byte) 0x0B);
281 
282     putShortLengthUtf8(bb, "undefined");
283 
284     bb.put((byte) 0x78); // TC_ENDBLOCKDATA
285     bb.flip();
286 
287     byte[] buf = new byte[bb.remaining()];
288     bb.get(buf);
289     return buf;
290   }
291 
292   private static void putShortLengthEncodedClassName(ByteBuffer bb, Class<?> klazz) {
293     putShortLengthUtf8(bb, "L" + klazz.getName().replace('.', '/') + ";");
294   }
295 
296   private static void putShortLengthUtf8(ByteBuffer bb, String s) {
297     byte[] utf8 = s.getBytes(StandardCharsets.UTF_8);
298     bb.putShort((short) utf8.length);
299     bb.put(utf8);
300   }
301 
302   /**
303    * Checks if {@link AFSocketAddress} instantiation should be performed via deserialization.
304    *
305    * @return {@code true} if so.
306    * @see #newDeserializedAFSocketAddress(int, byte[], Lease, AFAddressFamily,
307    *      AFSocketAddressConstructor)
308    */
309   protected static boolean isUseDeserializationForInit() {
310     return USE_DESERIALIZATION_FOR_INIT;
311   }
312 
313   /**
314    * Checks if the address can be resolved to a {@link File}.
315    *
316    * @return {@code true} if the address has a filename.
317    */
318   public abstract boolean hasFilename();
319 
320   /**
321    * Returns the {@link File} corresponding with this address, if possible.
322    *
323    * A {@link FileNotFoundException} is thrown if there is no filename associated with the address,
324    * which applies to addresses in the abstract namespace, for example.
325    *
326    * @return The filename.
327    * @throws FileNotFoundException if the address is not associated with a filename.
328    */
329   public abstract File getFile() throws FileNotFoundException;
330 
331   /**
332    * Returns the corresponding {@link AFAddressFamily}.
333    *
334    * @return The address family instance.
335    */
336   public final AFAddressFamily<?> getAddressFamily() {
337     return addressFamily;
338   }
339 
340   /**
341    * Wraps the socket name/peer name of a file descriptor as an {@link InetAddress}.
342    *
343    * @param fdesc The file descriptor.
344    * @param peerName If {@code true}, the remote peer name (instead of the local name) is retrieved.
345    * @param af The address family.
346    * @return The {@link InetAddress}.
347    */
348   protected static final InetAddress getInetAddress(FileDescriptor fdesc, boolean peerName,
349       AFAddressFamily<?> af) {
350     if (!fdesc.valid()) {
351       return null;
352     }
353     byte[] addr = NativeUnixSocket.sockname(af.getDomain(), fdesc, peerName);
354     if (addr == null) {
355       return null;
356     }
357     return AFInetAddress.wrapAddress(addr, af);
358   }
359 
360   /**
361    * Gets the socket name/peer name of a file descriptor as an {@link AFSocketAddress}.
362    *
363    * @param <A> The corresponding address type.
364    * @param fdesc The file descriptor.
365    * @param requestPeerName If {@code true}, the remote peer name (instead of the local name) is
366    *          retrieved.
367    * @param port The port.
368    * @param af The address family.
369    * @return The {@link InetAddress}.
370    */
371   protected static final <A extends AFSocketAddress> @Nullable A getSocketAddress(
372       FileDescriptor fdesc, boolean requestPeerName, int port, AFAddressFamily<A> af) {
373     if (!fdesc.valid()) {
374       return null;
375     }
376     byte[] addr = NativeUnixSocket.sockname(af.getDomain(), fdesc, requestPeerName);
377     if (addr == null) {
378       return null;
379     }
380     try {
381       // FIXME we could infer the "port" from the path if the socket factory supports that
382       return AFSocketAddress.unwrap(AFInetAddress.wrapAddress(addr, af), port, af);
383     } catch (SocketException e) {
384       throw new IllegalStateException(e);
385     }
386   }
387 
388   /**
389    * Gets the socket name/peer name of a file descriptor as bytes.
390    *
391    * @param <A> The corresponding address type.
392    * @param fdesc The file descriptor.
393    * @param requestPeerName If {@code true}, the remote peer name (instead of the local name) is
394    *          retrieved.
395    * @param af The address family.
396    * @return The address bytes.
397    */
398   @SuppressWarnings("PMD.ReturnEmptyCollectionRatherThanNull")
399   static final <A extends AFSocketAddress> byte[] getSocketAddressBytes(FileDescriptor fdesc,
400       boolean requestPeerName, AFAddressFamily<A> af) {
401     if (!fdesc.valid()) {
402       return null;
403     }
404     byte[] addr = NativeUnixSocket.sockname(af.getDomain(), fdesc, requestPeerName);
405     if (addr == null) {
406       return null;
407     }
408     return addr.clone();
409   }
410 
411   static final AFSocketAddress preprocessSocketAddress(
412       Class<? extends AFSocketAddress> supportedAddressClass, SocketAddress endpoint,
413       AFSocketAddressFromHostname<?> afh) throws SocketException {
414     Objects.requireNonNull(endpoint);
415     if (endpoint instanceof SentinelSocketAddress) {
416       return (SentinelSocketAddress) endpoint;
417     }
418 
419     if (!(endpoint instanceof AFSocketAddress)) {
420       if (afh != null) {
421         if (endpoint instanceof InetSocketAddress) {
422           InetSocketAddress isa = (InetSocketAddress) endpoint;
423 
424           String hostname = isa.getHostString();
425           if (afh.isHostnameSupported(hostname)) {
426             try {
427               endpoint = afh.addressFromHost(hostname, isa.getPort());
428             } catch (SocketException e) {
429               throw e;
430             }
431           }
432         }
433       }
434       endpoint = mapOrFail(endpoint, supportedAddressClass);
435     }
436 
437     Objects.requireNonNull(endpoint);
438 
439     if (!supportedAddressClass.isAssignableFrom(endpoint.getClass())) {
440       throw new IllegalArgumentException("Can only connect to endpoints of type "
441           + supportedAddressClass.getName() + ", got: " + endpoint.getClass() + ": " + endpoint);
442     }
443 
444     return (AFSocketAddress) endpoint;
445   }
446 
447   /**
448    * Returns the (non-native) byte-level representation of this address.
449    *
450    * @return The byte array.
451    */
452   protected final byte[] getBytes() {
453     return bytes; // NOPMD
454   }
455 
456   /**
457    * Returns a "special" {@link InetAddress} that contains information about this
458    * {@link AFSocketAddress}.
459    *
460    * IMPORTANT: This {@link InetAddress} does not properly compare (using
461    * {@link InetAddress#equals(Object)} and {@link InetAddress#hashCode()}). It should be used
462    * exclusively to circumvent existing APIs like {@link DatagramSocket} that only accept/return
463    * {@link InetAddress} and not arbitrary {@link SocketAddress} types.
464    *
465    * @return The "special" {@link InetAddress}.
466    */
467   public final InetAddress wrapAddress() {
468     return AFInetAddress.wrapAddress(bytes, getAddressFamily());
469   }
470 
471   /**
472    * A reference to the constructor of an AFSocketAddress subclass.
473    *
474    * @param <T> The actual subclass.
475    * @author Christian Kohlschütter
476    */
477   @FunctionalInterface
478   protected interface AFSocketAddressConstructor<T extends AFSocketAddress> {
479     /**
480      * Constructs a new AFSocketAddress instance.
481      *
482      * @param port The port.
483      * @param socketAddress The socket address in junixsocket-specific byte-array representation.
484      * @param nativeAddress The socket address in system-native representation.
485      * @return The instance.
486      * @throws SocketException on error.
487      */
488     @NonNull
489     T newAFSocketAddress(int port, byte[] socketAddress, Lease<ByteBuffer> nativeAddress)
490         throws SocketException;
491   }
492 
493   /**
494    * Resolves a junixsocket-specific byte-array representation of an {@link AFSocketAddress} to an
495    * actual {@link AFSocketAddress} instance, possibly reusing a cached instance.
496    *
497    * @param <A> The concrete {@link AFSocketAddress} that is supported by this type.
498    * @param socketAddress The socket address in junixsocket-specific byte-array representation.
499    * @param port The port.
500    * @param af The address family.
501    * @return The instance.
502    * @throws SocketException on error.
503    */
504   @SuppressWarnings({"unchecked", "null"})
505   protected static final <A extends AFSocketAddress> A resolveAddress(final byte[] socketAddress,
506       int port, AFAddressFamily<A> af) throws SocketException {
507     if (socketAddress.length == 0) {
508       throw new SocketException("Address cannot be empty");
509     }
510 
511     if (port == -1) {
512       port = 0;
513     }
514 
515     try (Lease<ByteBuffer> lease = SOCKETADDRESS_BUFFER_TL.take()) {
516       ByteBuffer direct = lease.get();
517       int limit = NativeUnixSocket.isLoaded() ? NativeUnixSocket.bytesToSockAddr(af.getDomain(),
518           direct, socketAddress) : -1;
519       if (limit == -1) {
520         // not supported, but we can still create an address
521         return af.getAddressConstructor().newAFSocketAddress(port, socketAddress, null);
522       } else if (limit > SOCKADDR_MAX_LEN) {
523         throw new IllegalStateException("Unexpected address length");
524       }
525       direct.rewind();
526       direct.limit(limit);
527 
528       A instance;
529       synchronized (AFSocketAddress.class) {
530         Map<ByteBuffer, AFSocketAddress> map;
531         Map<Integer, Map<ByteBuffer, AFSocketAddress>> mapPorts = ADDRESS_CACHE.get(af);
532         if (mapPorts == null) {
533           instance = null;
534           mapPorts = new HashMap<>();
535           map = new HashMap<>();
536           mapPorts.put(port, map);
537           ADDRESS_CACHE.put(af, mapPorts);
538         } else {
539           map = mapPorts.get(port);
540           if (map == null) {
541             instance = null;
542             map = new HashMap<>();
543             mapPorts.put(port, map);
544           } else {
545             instance = (A) map.get(direct);
546           }
547         }
548 
549         if (instance == null) {
550           ByteBuffer key = newSockAddrKeyBuffer(limit);
551           key.put(direct);
552           key = key.asReadOnlyBuffer();
553 
554           instance = af.getAddressConstructor().newAFSocketAddress(port, socketAddress, ObjectPool
555               .unpooledLease(key));
556 
557           map.put(key, instance);
558         }
559       }
560       return instance;
561     }
562   }
563 
564   @SuppressWarnings("null")
565   static final <A extends AFSocketAddress> A ofInternal(ByteBuffer socketAddressBuffer,
566       AFAddressFamily<A> af) throws SocketException {
567     synchronized (AFSocketAddress.class) {
568       socketAddressBuffer.rewind();
569 
570       Map<Integer, Map<ByteBuffer, AFSocketAddress>> mapPorts = ADDRESS_CACHE.get(af);
571       if (mapPorts != null) {
572         Map<ByteBuffer, AFSocketAddress> map = mapPorts.get(0); // FIXME get port, something like
573                                                                 // sockAddrToPort
574         if (map != null) {
575           @SuppressWarnings("unchecked")
576           A address = (A) map.get(socketAddressBuffer);
577           if (address != null) {
578             return address;
579           }
580         }
581       }
582 
583       try (Lease<ByteBuffer> leasedBuffer = socketAddressBuffer.isDirect() ? null
584           : getNativeAddressDirectBuffer(Math.min(socketAddressBuffer.limit(), SOCKADDR_MAX_LEN))) {
585         if (leasedBuffer != null) {
586           ByteBuffer buf = leasedBuffer.get();
587           buf.put(socketAddressBuffer);
588           socketAddressBuffer = buf;
589         }
590 
591         byte[] sockAddrToBytes = NativeUnixSocket.sockAddrToBytes(af.getDomain(),
592             socketAddressBuffer);
593         if (sockAddrToBytes == null) {
594           return null;
595         } else {
596           return AFSocketAddress.resolveAddress(sockAddrToBytes, 0, af);
597         }
598       }
599     }
600   }
601 
602   /**
603    * Wraps an address as an {@link InetAddress}.
604    *
605    * @param af The address family.
606    * @return The {@link InetAddress}.
607    */
608   protected final synchronized InetAddress getInetAddress(AFAddressFamily<?> af) {
609     if (inetAddress == null) {
610       inetAddress = AFInetAddress.wrapAddress(bytes, af);
611     }
612     return inetAddress;
613   }
614 
615   /**
616    * Wraps this address as an {@link InetAddress}.
617    *
618    * @return The {@link InetAddress}.
619    */
620   protected final InetAddress getInetAddress() {
621     return getInetAddress(getAddressFamily());
622   }
623 
624   @SuppressWarnings("null")
625   static final @NonNull ByteBuffer newSockAddrDirectBuffer(int length) {
626     return ByteBuffer.allocateDirect(length);
627   }
628 
629   @SuppressWarnings("null")
630   static final @NonNull ByteBuffer newSockAddrKeyBuffer(int length) {
631     return ByteBuffer.allocate(length);
632   }
633 
634   /**
635    * Returns an {@link AFSocketAddress} given a special {@link InetAddress} that encodes the byte
636    * sequence of an AF_UNIX etc. socket address, like those returned by {@link #wrapAddress()}.
637    *
638    * @param <A> The corresponding address type.
639    * @param address The "special" {@link InetAddress}.
640    * @param port The port (use 0 for "none").
641    * @param af The address family.
642    * @return The {@link AFSocketAddress} instance.
643    * @throws SocketException if the operation fails, for example when an unsupported address is
644    *           specified.
645    */
646   @SuppressWarnings("null")
647   @NonNull
648   protected static final <A extends AFSocketAddress> A unwrap(InetAddress address, int port,
649       AFAddressFamily<A> af) throws SocketException {
650     Objects.requireNonNull(address);
651     return resolveAddress(AFInetAddress.unwrapAddress(address, af), port, af);
652   }
653 
654   /**
655    * Returns an {@link AFSocketAddress} given a special {@link InetAddress} hostname that encodes
656    * the byte sequence of an AF_UNIX etc. socket address, like those returned by
657    * {@link #wrapAddress()}.
658    *
659    * @param <A> The corresponding address type.
660    * @param hostname The "special" hostname, as provided by {@link InetAddress#getHostName()}.
661    * @param port The port (use 0 for "none").
662    * @param af The address family.
663    * @return The {@link AFSocketAddress} instance.
664    * @throws SocketException if the operation fails, for example when an unsupported address is
665    *           specified.
666    */
667   @SuppressWarnings("null")
668   @NonNull
669   protected static final <A extends AFSocketAddress> A unwrap(String hostname, int port,
670       AFAddressFamily<A> af) throws SocketException {
671     Objects.requireNonNull(hostname);
672     return resolveAddress(AFInetAddress.unwrapAddress(hostname, af), port, af);
673   }
674 
675   static final int unwrapAddressDirectBufferInternal(ByteBuffer socketAddressBuffer,
676       SocketAddress address) throws SocketException {
677     if (!NativeUnixSocket.isLoaded()) {
678       throw new SocketException("Unsupported operation; junixsocket native library is not loaded");
679     }
680     Objects.requireNonNull(address);
681 
682     address = AFSocketAddress.mapOrFail(address, AFSocketAddress.class);
683     AFSocketAddress socketAddress = (AFSocketAddress) address;
684 
685     byte[] addr = socketAddress.getBytes();
686     int domain = socketAddress.getAddressFamily().getDomain();
687 
688     int len = NativeUnixSocket.bytesToSockAddr(domain, socketAddressBuffer, addr);
689     if (len == -1) {
690       throw new SocketException("Unsupported domain");
691     }
692     return len;
693   }
694 
695   /**
696    * Returns a thread-local direct ByteBuffer containing the native socket address representation of
697    * this {@link AFSocketAddress}.
698    *
699    * @return The direct {@link ByteBuffer}.
700    */
701   final Lease<ByteBuffer> getNativeAddressDirectBuffer() throws SocketException {
702     ByteBuffer address = nativeAddress;
703     if (address == null) {
704       throw (SocketException) new SocketException("Cannot access native address").initCause(
705           NativeUnixSocket.unsupportedException());
706     }
707     address = address.duplicate();
708 
709     Lease<ByteBuffer> lease = getNativeAddressDirectBuffer(address.limit());
710     ByteBuffer direct = lease.get();
711     address.position(0);
712     direct.put(address);
713 
714     return lease;
715   }
716 
717   static final Lease<ByteBuffer> getNativeAddressDirectBuffer(int limit) {
718     Lease<ByteBuffer> lease = SOCKETADDRESS_BUFFER_TL.take();
719     ByteBuffer direct = lease.get();
720     direct.position(0);
721     direct.limit(limit);
722     return lease;
723   }
724 
725   /**
726    * Checks if the given address is supported by this address family.
727    *
728    * @param addr The address.
729    * @param af The address family.
730    * @return {@code true} if supported.
731    */
732   protected static final boolean isSupportedAddress(InetAddress addr, AFAddressFamily<?> af) {
733     return AFInetAddress.isSupportedAddress(addr, af);
734   }
735 
736   /**
737    * Writes the native (system-level) representation of this address to the given buffer.
738    *
739    * The position of the target buffer will be at the end (i.e., after) the written data.
740    *
741    * @param buf The target buffer.
742    * @throws IOException on error.
743    */
744   public final void writeNativeAddressTo(ByteBuffer buf) throws IOException {
745     if (nativeAddress == null) {
746       throw (SocketException) new SocketException("Cannot access native address").initCause(
747           NativeUnixSocket.unsupportedException());
748     }
749     buf.put(nativeAddress);
750   }
751 
752   /**
753    * Creates a new socket connected to this address.
754    *
755    * @return The socket instance.
756    * @throws IOException on error.
757    */
758   public AFSocket<?> newConnectedSocket() throws IOException {
759     AFSocket<?> socket = getAddressFamily().newSocket();
760     socket.connect(this);
761     return socket;
762   }
763 
764   /**
765    * Creates a new server socket bound to this address.
766    *
767    * @return The server socket instance.
768    * @throws IOException on error.
769    */
770   public AFServerSocket<?> newBoundServerSocket() throws IOException {
771     AFServerSocket<?> serverSocket = getAddressFamily().newServerSocket();
772     serverSocket.bind(this);
773     return serverSocket;
774   }
775 
776   /**
777    * Creates a new server socket force-bound to this address (i.e., any additional call to
778    * {@link ServerSocket#bind(SocketAddress)} will ignore the passed address and use this one
779    * instead.
780    *
781    * @return The server socket instance.
782    * @throws IOException on error.
783    */
784   public AFServerSocket<?> newForceBoundServerSocket() throws IOException {
785     AFServerSocket<?> serverSocket = getAddressFamily().newServerSocket();
786     serverSocket.forceBindAddress(this).bind(this);
787     return serverSocket;
788   }
789 
790   /**
791    * Tries to parse the given URI and return a corresponding {@link AFSocketAddress} for it.
792    *
793    * NOTE: Only certain URI schemes are supported, such as {@code unix://} (for
794    * {@link AFUNIXSocketAddress}) and {@code tipc://} for {@link AFTIPCSocketAddress}.
795    *
796    * @param u The URI.
797    * @return The address.
798    * @throws SocketException on error.
799    * @see AFAddressFamily#uriSchemes()
800    */
801   @SuppressWarnings("PMD.ShortMethodName")
802   public static AFSocketAddress of(URI u) throws SocketException {
803     return of(u, -1);
804   }
805 
806   /**
807    * Tries to parse the given URI and return a corresponding {@link AFSocketAddress} for it.
808    *
809    * NOTE: Only certain URI schemes are supported, such as {@code unix://} (for
810    * {@link AFUNIXSocketAddress}) and {@code tipc://} for {@link AFTIPCSocketAddress}.
811    *
812    * @param u The URI.
813    * @param overridePort The port to forcibly use, or {@code -1} for "don't override".
814    * @return The address.
815    * @throws SocketException on error.
816    * @see AFAddressFamily#uriSchemes()
817    */
818   @SuppressWarnings("PMD.ShortMethodName")
819   public static AFSocketAddress of(URI u, int overridePort) throws SocketException {
820     AFAddressFamily<?> af = AFAddressFamily.getAddressFamily(u);
821     if (af == null) {
822       throw new SocketException("Cannot resolve AFSocketAddress from URI scheme: " + u.getScheme());
823     }
824     return af.parseURI(u, overridePort);
825   }
826 
827   /**
828    * Tries to create a URI based on this {@link AFSocketAddress}.
829    *
830    * @param scheme The target scheme.
831    * @param template An optional template to reuse certain parameters (e.g., the "path" component
832    *          for an {@code http} request), or {@code null}.
833    * @return The URI.
834    * @throws IOException on error.
835    */
836   public URI toURI(String scheme, URI template) throws IOException {
837     throw new IOException("Unsupported operation");
838   }
839 
840   /**
841    * Returns a address string that can be used with {@code socat}'s {@code SOCKET-CONNECT},
842    * {@code SOCKET-LISTEN}, {@code SOCKET-DATAGRAM}, etc., address types, or {@code null} if the
843    * address type is not natively supported by this platform.
844    *
845    * This call is mostly suited for debugging purposes. The resulting string is specific to the
846    * platform the code is executed on, and thus may be different among platforms.
847    *
848    * @param socketType The socket type, or {@code null} to omit from string.
849    * @param socketProtocol The socket protocol, or {@code null} to omit from string.
850    * @return The string (such as 1:0:x2f746d702f796f).
851    * @throws IOException on error (a {@link SocketException} is thrown if the native address cannot
852    *           be accessed).
853    */
854   public @Nullable String toSocatAddressString(AFSocketType socketType,
855       AFSocketProtocol socketProtocol) throws IOException {
856 
857     if (SOCKADDR_NATIVE_FAMILY_OFFSET == -1 || SOCKADDR_NATIVE_DATA_OFFSET == -1) {
858       return null;
859     }
860     if (nativeAddress == null) {
861       throw (SocketException) new SocketException("Cannot access native address").initCause(
862           NativeUnixSocket.unsupportedException());
863     }
864     if (socketProtocol != null && socketProtocol.getId() != 0) {
865       throw new IOException("Protocol not (yet) supported"); // FIXME support additional protocols
866     }
867 
868     int family = (nativeAddress.get(SOCKADDR_NATIVE_FAMILY_OFFSET) & 0xFF);
869     int type = socketType == null ? -1 : NativeUnixSocket.sockTypeToNative(socketType.getId());
870     StringBuilder sb = new StringBuilder();
871     sb.append(family);
872     if (type != -1) {
873       sb.append(':');
874       sb.append(type);
875     }
876     if (socketProtocol != null) {
877       sb.append(':');
878       sb.append(socketProtocol.getId()); // FIXME needs native conversion
879     }
880     sb.append(":x");
881     int n = nativeAddress.limit();
882     while (n > 1 && nativeAddress.get(n - 1) == 0) {
883       n--;
884     }
885     for (int pos = SOCKADDR_NATIVE_DATA_OFFSET; pos < n; pos++) {
886       byte b = nativeAddress.get(pos);
887       sb.append(String.format(Locale.ENGLISH, "%02x", b));
888     }
889     return sb.toString();
890   }
891 
892   /**
893    * Checks if the given address could cover another address.
894    *
895    * By default, this is only true if both addresses are regarded equal using
896    * {@link #equals(Object)}.
897    *
898    * However, implementations may support "wildcard" addresses, and this method would compare a
899    * wildcard address against some non-wildcard address, for example.
900    *
901    * @param other The other address that could be covered by this address.
902    * @return {@code true} if the other address could be covered.
903    */
904   public boolean covers(AFSocketAddress other) {
905     return this.equals(other);
906   }
907 
908   /**
909    * Custom serialization: Reference {@link AFAddressFamily} instance by identifier string.
910    *
911    * @param in The {@link ObjectInputStream}.
912    * @throws ClassNotFoundException on error.
913    * @throws IOException on error.
914    */
915   private void readObject(ObjectInputStream in) throws ClassNotFoundException, IOException {
916     in.defaultReadObject();
917 
918     String af = in.readUTF();
919     if ("undefined".equals(af)) {
920       this.addressFamily = null;
921     } else {
922       this.addressFamily = Objects.requireNonNull(AFAddressFamily.getAddressFamily(af),
923           "address family");
924     }
925   }
926 
927   /**
928    * Custom serialization: Reference {@link AFAddressFamily} instance by identifier string.
929    *
930    * @param out The {@link ObjectOutputStream}.
931    * @throws IOException on error.
932    */
933   private void writeObject(ObjectOutputStream out) throws IOException {
934     out.defaultWriteObject();
935     out.writeUTF(addressFamily == null ? "undefined" : addressFamily.getJuxString());
936   }
937 
938   /**
939    * Returns a string representation of the argument as an unsigned decimal value.
940    * <p>
941    * Works like {@link Integer#toUnsignedString(int)}; added to allow execution on Java 1.7.
942    *
943    * @param i The value.
944    * @return The string.
945    */
946   static String toUnsignedString(int i) {
947     return Long.toString(toUnsignedLong(i));
948   }
949 
950   /**
951    * Returns a string representation of the first argument as an unsigned integer value in the radix
952    * specified by the second argument; added to allow execution on Java 1.7.
953    *
954    * @param i The value.
955    * @param radix The radix.
956    * @return The string.
957    */
958   static String toUnsignedString(int i, int radix) {
959     return Long.toUnsignedString(toUnsignedLong(i), radix);
960   }
961 
962   private static long toUnsignedLong(long x) {
963     return x & 0xffffffffL;
964   }
965 
966   /**
967    * Parses the string argument as an unsigned integer in the radix specified by the second
968    * argument. Works like {@link Integer#parseUnsignedInt(String, int)}; added to allow execution on
969    * Java 1.7.
970    *
971    * @param s The string.
972    * @param radix The radix.
973    * @return The integer.
974    * @throws NumberFormatException on parse error.
975    */
976   protected static int parseUnsignedInt(String s, int radix) throws NumberFormatException {
977     if (s == null || s.isEmpty()) {
978       throw new NumberFormatException("Cannot parse null or empty string");
979     }
980 
981     int len = s.length();
982     if (s.startsWith("-")) {
983       throw new NumberFormatException("Illegal leading minus sign on unsigned string " + s);
984     }
985 
986     if (len <= 5 || (radix == 10 && len <= 9)) {
987       return Integer.parseInt(s, radix);
988     } else {
989       long ell = Long.parseLong(s, radix);
990       if ((ell & 0xffff_ffff_0000_0000L) == 0) {
991         return (int) ell;
992       } else {
993         throw new NumberFormatException("String value exceeds " + "range of unsigned int: " + s);
994       }
995     }
996   }
997 
998   /**
999    * Checks if the given {@link SocketAddress} can be mapped to an {@link AFSocketAddress}. This is
1000    * the case if the address either already is an {@link AFSocketAddress}, {@code null}, or
1001    * something that has an equivalent representation, such as {@code UnixDomainSocketAddress}.
1002    *
1003    * @param addr The address.
1004    * @return {@code true} if mappable.
1005    */
1006   public static boolean canMap(SocketAddress addr) {
1007     return canMap(addr, AFSocketAddress.class);
1008   }
1009 
1010   /**
1011    * Checks if the given {@link SocketAddress} can be mapped to a specific {@link AFSocketAddress}
1012    * subclass. This is the case if the address either already is such an {@link AFSocketAddress},
1013    * {@code null}, or something that has an equivalent representation, such as
1014    * {@code UnixDomainSocketAddress}.
1015    *
1016    * @param addr The address.
1017    * @param targetAddressClass The target address class to map to.
1018    * @return {@code true} if mappable.
1019    */
1020   public static boolean canMap(SocketAddress addr,
1021       Class<? extends AFSocketAddress> targetAddressClass) {
1022     if (addr == null) {
1023       return true;
1024     } else if (targetAddressClass.isAssignableFrom(addr.getClass())) {
1025       return true;
1026     }
1027     AFSupplier<? extends AFSocketAddress> supplier = SocketAddressUtil.supplyAFSocketAddress(addr);
1028     if (supplier == null) {
1029       return false;
1030     }
1031     AFSocketAddress afAddr = supplier.get();
1032     if (afAddr == null) {
1033       return false;
1034     }
1035     return (targetAddressClass.isAssignableFrom(afAddr.getClass()));
1036   }
1037 
1038   /**
1039    * Maps the given address to an {@link AFSocketAddress}.
1040    *
1041    * @param addr The address.
1042    * @return The {@link AFSocketAddress}.
1043    * @throws IllegalArgumentException if the address could not be mapped.
1044    * @see #canMap(SocketAddress,Class)
1045    */
1046   public static AFSocketAddress mapOrFail(SocketAddress addr) {
1047     return mapOrFail(addr, AFSocketAddress.class);
1048   }
1049 
1050   /**
1051    * Maps the given address to a specific {@link AFSocketAddress} type.
1052    *
1053    * @param addr The address.
1054    * @param targetAddressClass The target address class.
1055    * @param <A> The target address type.
1056    * @return The {@link AFSocketAddress}.
1057    * @throws IllegalArgumentException if the address could not be mapped.
1058    * @see #canMap(SocketAddress,Class)
1059    */
1060   @SuppressWarnings("null")
1061   public static <A extends AFSocketAddress> A mapOrFail(SocketAddress addr,
1062       Class<A> targetAddressClass) {
1063     if (addr == null) {
1064       return null;
1065     } else if (targetAddressClass.isAssignableFrom(addr.getClass())) {
1066       return targetAddressClass.cast(addr);
1067     }
1068 
1069     AFSupplier<? extends AFSocketAddress> supplier = SocketAddressUtil.supplyAFSocketAddress(addr);
1070     if (supplier == null) {
1071       throw new IllegalArgumentException("Can only bind to endpoints of type "
1072           + AFSocketAddress.class.getName() + ": " + addr);
1073     }
1074     AFSocketAddress afAddr = supplier.get();
1075     if (afAddr == null || !targetAddressClass.isAssignableFrom(afAddr.getClass())) {
1076       throw new IllegalArgumentException("Can only bind to endpoints of type "
1077           + AFSocketAddress.class.getName() + ", and this specific address is unsupported: "
1078           + addr);
1079     }
1080     return targetAddressClass.cast(afAddr);
1081   }
1082 }