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