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