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.File;
21  import java.io.FileNotFoundException;
22  import java.io.IOException;
23  import java.net.InetAddress;
24  import java.net.SocketAddress;
25  import java.net.SocketException;
26  import java.net.URI;
27  import java.nio.ByteBuffer;
28  import java.util.Arrays;
29  import java.util.HashSet;
30  import java.util.Objects;
31  import java.util.Set;
32  
33  import org.eclipse.jdt.annotation.NonNull;
34  import org.eclipse.jdt.annotation.NonNullByDefault;
35  import org.newsclub.net.unix.pool.ObjectPool.Lease;
36  
37  /**
38   * An {@link AFSocketAddress} for AF_SYSTEM sockets.
39   *
40   * @author Christian Kohlschütter
41   */
42  public final class AFSYSTEMSocketAddress extends AFSocketAddress {
43    private static final long serialVersionUID = 1L; // do not change!
44  
45    private static AFAddressFamily<AFSYSTEMSocketAddress> afSystem;
46    private static final String SELECTOR_PROVIDER_CLASS =
47        "org.newsclub.net.unix.darwin.system.AFSYSTEMSelectorProvider";
48  
49    /**
50     * The AF_SYSTEM system address.
51     *
52     * @author Christian Kohlschütter
53     */
54    @NonNullByDefault
55    public static final class SysAddr extends NamedInteger {
56      private static final long serialVersionUID = 1L;
57  
58      /**
59       * The sysaddr AF_SYS_CONTROL, also known as AF_SYS_KERN_CONTROL.
60       */
61      public static final SysAddr AF_SYS_CONTROL;
62  
63      private static final @NonNull SysAddr[] VALUES = init(new @NonNull SysAddr[] {
64          AF_SYS_CONTROL = new SysAddr("AF_SYS_CONTROL", 2) //
65      });
66  
67      private SysAddr(int id) {
68        super(id);
69      }
70  
71      private SysAddr(String name, int id) {
72        super(name, id);
73      }
74  
75      /**
76       * Returns a {@link SysAddr} for the given custom value.
77       *
78       * @param v The value.
79       * @return The {@link SysAddr} object.
80       */
81      public static SysAddr ofValue(int v) {
82        return ofValue(VALUES, SysAddr::new, v);
83      }
84    }
85  
86    private AFSYSTEMSocketAddress(int port, final byte[] socketAddress,
87        Lease<ByteBuffer> nativeAddress) throws SocketException {
88      super(port, socketAddress, nativeAddress, addressFamily());
89    }
90  
91    private static AFSYSTEMSocketAddress newAFSocketAddress(int port, final byte[] socketAddress,
92        Lease<ByteBuffer> nativeAddress) throws SocketException {
93      return newDeserializedAFSocketAddress(port, socketAddress, nativeAddress, addressFamily(),
94          AFSYSTEMSocketAddress::new);
95    }
96  
97    /**
98     * Returns an {@link AFSYSTEMSocketAddress} that refers to a given AF_SYSTEM socket address (i.e.,
99     * referring to a particular socket instance instead of a service address). A Java-only "IP port
100    * number" is stored along the instance for compatibility reasons.
101    *
102    * @param javaPort The emulated "port" number (not part of AF_SYSTEM).
103    * @param sysAddr 16-bit system address (e.g., AF_SYS_KERNCONTROL)
104    * @param id Controller unique identifier
105    * @param unit Developer private unit number, 0 means "unspecified".
106    * @return A corresponding {@link AFSYSTEMSocketAddress} instance.
107    * @throws SocketException if the operation fails.
108    */
109   public static AFSYSTEMSocketAddress ofSysAddrIdUnit(int javaPort, SysAddr sysAddr, int id,
110       int unit) throws SocketException {
111     return resolveAddress(toBytes(sysAddr, id, unit), javaPort, addressFamily());
112   }
113 
114   /**
115    * Returns an {@link AFSYSTEMSocketAddress} that refers to a given AF_SYSTEM socket address (i.e.,
116    * referring to a particular socket instance instead of a service address).
117    *
118    * @param sysAddr 16-bit system address (e.g., AF_SYS_KERNCONTROL)
119    * @param id Controller unique identifier
120    * @param unit Developer private unit number
121    * @return A corresponding {@link AFSYSTEMSocketAddress} instance.
122    * @throws SocketException if the operation fails.
123    */
124   public static AFSYSTEMSocketAddress ofSysAddrIdUnit(SysAddr sysAddr, int id, int unit)
125       throws SocketException {
126     return ofSysAddrIdUnit(0, sysAddr, id, unit);
127   }
128 
129   /**
130    * Returns an {@link AFSYSTEMSocketAddress} given a special {@link InetAddress} that encodes the
131    * byte sequence of an AF_SYSTEM socket address, like those returned by {@link #wrapAddress()}.
132    *
133    * @param address The "special" {@link InetAddress}.
134    * @param port The port (use 0 for "none").
135    * @return The {@link AFSYSTEMSocketAddress} instance.
136    * @throws SocketException if the operation fails, for example when an unsupported address is
137    *           specified.
138    */
139   public static AFSYSTEMSocketAddress unwrap(InetAddress address, int port) throws SocketException {
140     return AFSocketAddress.unwrap(address, port, addressFamily());
141   }
142 
143   /**
144    * Returns an {@link AFSYSTEMSocketAddress} given a special {@link InetAddress} hostname that
145    * encodes the byte sequence of an AF_SYSTEM socket address, like those returned by
146    * {@link #wrapAddress()}.
147    *
148    * @param hostname The "special" hostname, as provided by {@link InetAddress#getHostName()}.
149    * @param port The port (use 0 for "none").
150    * @return The {@link AFSYSTEMSocketAddress} instance.
151    * @throws SocketException if the operation fails, for example when an unsupported address is
152    *           specified.
153    */
154   public static AFSYSTEMSocketAddress unwrap(String hostname, int port) throws SocketException {
155     return AFSocketAddress.unwrap(hostname, port, addressFamily());
156   }
157 
158   /**
159    * Returns an {@link AFSYSTEMSocketAddress} given a generic {@link SocketAddress}.
160    *
161    * @param address The address to unwrap.
162    * @return The {@link AFSYSTEMSocketAddress} instance.
163    * @throws SocketException if the operation fails, for example when an unsupported address is
164    *           specified.
165    */
166   public static AFSYSTEMSocketAddress unwrap(SocketAddress address) throws SocketException {
167     Objects.requireNonNull(address);
168     if (!isSupportedAddress(address)) {
169       throw new SocketException("Unsupported address");
170     }
171     return (AFSYSTEMSocketAddress) address;
172   }
173 
174   @Override
175   public String toString() {
176     int port = getPort();
177 
178     byte[] bytes = getBytes();
179     if (bytes.length != (8 * 4)) {
180       return getClass().getName() + "[" + (port == 0 ? "" : "port=" + port) + ";UNKNOWN" + "]";
181     }
182 
183     ByteBuffer bb = ByteBuffer.wrap(bytes);
184     SysAddr sysAddr = SysAddr.ofValue(bb.getInt());
185     int id = bb.getInt();
186     int unit = bb.getInt();
187 
188     return getClass().getName() + "[" + (port == 0 ? "" : "port=" + port + ";") + sysAddr + ";id="
189         + id + ";unit=" + unit + "]";
190   }
191 
192   /**
193    * Returns the "SysAddr" part of the address.
194    *
195    * @return The SysAddr part.
196    */
197   public @NonNull SysAddr getSysAddr() {
198     ByteBuffer bb = ByteBuffer.wrap(getBytes());
199     return SysAddr.ofValue(bb.getInt(0));
200   }
201 
202   /**
203    * Returns the "id" part of the address.
204    *
205    * @return The id part.
206    */
207   public int getId() {
208     ByteBuffer bb = ByteBuffer.wrap(getBytes());
209     return bb.getInt(4);
210   }
211 
212   /**
213    * Returns the "unit" part of the address.
214    *
215    * @return The unit part.
216    */
217   public int getUnit() {
218     ByteBuffer bb = ByteBuffer.wrap(getBytes());
219     return bb.getInt(8);
220   }
221 
222   @Override
223   public boolean hasFilename() {
224     return false;
225   }
226 
227   @Override
228   public File getFile() throws FileNotFoundException {
229     throw new FileNotFoundException("no file");
230   }
231 
232   /**
233    * Checks if an {@link InetAddress} can be unwrapped to an {@link AFSYSTEMSocketAddress}.
234    *
235    * @param addr The instance to check.
236    * @return {@code true} if so.
237    * @see #wrapAddress()
238    * @see #unwrap(InetAddress, int)
239    */
240   public static boolean isSupportedAddress(InetAddress addr) {
241     return AFSocketAddress.isSupportedAddress(addr, addressFamily());
242   }
243 
244   /**
245    * Checks if a {@link SocketAddress} can be unwrapped to an {@link AFSYSTEMSocketAddress}.
246    *
247    * @param addr The instance to check.
248    * @return {@code true} if so.
249    * @see #unwrap(InetAddress, int)
250    */
251   public static boolean isSupportedAddress(SocketAddress addr) {
252     return (addr instanceof AFSYSTEMSocketAddress);
253   }
254 
255   @SuppressWarnings("cast")
256   private static byte[] toBytes(SysAddr sysAddr, int id, int unit) {
257     ByteBuffer bb = ByteBuffer.allocate(8 * 4);
258     bb.putInt(sysAddr.value());
259     bb.putInt(id);
260     bb.putInt(unit);
261     bb.putInt(0);
262     bb.putInt(0);
263     bb.putInt(0);
264     bb.putInt(0);
265     bb.putInt(0);
266     return (byte[]) bb.flip().array();
267   }
268 
269   /**
270    * Returns the corresponding {@link AFAddressFamily}.
271    *
272    * @return The address family instance.
273    */
274   @SuppressWarnings("null")
275   public static synchronized AFAddressFamily<AFSYSTEMSocketAddress> addressFamily() {
276     if (afSystem == null) {
277       afSystem = AFAddressFamily.registerAddressFamily("system", //
278           AFSYSTEMSocketAddress.class, new AFSocketAddressConfig<AFSYSTEMSocketAddress>() {
279 
280             private final AFSocketAddressConstructor<AFSYSTEMSocketAddress> addrConstr =
281                 isUseDeserializationForInit() ? AFSYSTEMSocketAddress::newAFSocketAddress
282                     : AFSYSTEMSocketAddress::new;
283 
284             @Override
285             protected AFSYSTEMSocketAddress parseURI(URI u, int port) throws SocketException {
286               return AFSYSTEMSocketAddress.of(u, port);
287             }
288 
289             @Override
290             protected AFSocketAddressConstructor<AFSYSTEMSocketAddress> addressConstructor() {
291               return addrConstr;
292             }
293 
294             @Override
295             protected String selectorProviderClassname() {
296               return SELECTOR_PROVIDER_CLASS;
297             }
298 
299             @Override
300             protected Set<String> uriSchemes() {
301               return new HashSet<>(Arrays.asList("afsystem"));
302             }
303           });
304       try {
305         Class.forName(SELECTOR_PROVIDER_CLASS);
306       } catch (ClassNotFoundException e) {
307         // ignore
308       }
309     }
310     return afSystem;
311   }
312 
313   /**
314    * Returns an {@link AFSYSTEMSocketAddress} for the given URI, if possible.
315    *
316    * @param uri The URI.
317    * @return The address.
318    * @throws SocketException if the operation fails.
319    */
320   @SuppressWarnings("PMD.ShortMethodName")
321   public static AFSYSTEMSocketAddress of(URI uri) throws SocketException {
322     return of(uri, -1);
323   }
324 
325   /**
326    * Returns an {@link AFSYSTEMSocketAddress} for the given URI, if possible.
327    *
328    * @param uri The URI.
329    * @param overridePort The port to forcibly use, or {@code -1} for "don't override".
330    * @return The address.
331    * @throws SocketException if the operation fails.
332    */
333   @SuppressWarnings({
334       "PMD.CognitiveComplexity", "PMD.CyclomaticComplexity", "PMD.ExcessiveMethodLength",
335       "PMD.NcssCount", "PMD.NPathComplexity", "PMD.ShortMethodName"})
336   public static AFSYSTEMSocketAddress of(URI uri, int overridePort) throws SocketException {
337     switch (uri.getScheme()) {
338       case "afsystem":
339         break;
340       default:
341         throw new SocketException("Unsupported URI scheme: " + uri.getScheme());
342     }
343 
344     String host;
345     if ((host = uri.getHost()) == null || host.isEmpty()) {
346       String ssp = uri.getSchemeSpecificPart();
347       if (ssp == null || !ssp.startsWith("//")) {
348         throw new SocketException("Unsupported URI: " + uri);
349       }
350       ssp = ssp.substring(2);
351       int i = ssp.indexOf('/');
352       host = i == -1 ? ssp : ssp.substring(0, i);
353       if (host.isEmpty()) {
354         throw new SocketException("Unsupported URI: " + uri);
355       }
356     }
357 
358     ByteBuffer bb = ByteBuffer.allocate(8 * 4);
359     for (String p : host.split("\\.")) {
360       int v;
361       try {
362         v = parseUnsignedInt(p, 10);
363       } catch (NumberFormatException e) {
364         throw (SocketException) new SocketException("Unsupported URI: " + uri).initCause(e);
365       }
366       bb.putInt(v);
367     }
368     bb.flip();
369     if (bb.remaining() > 8 * 4) {
370       throw new SocketException("Unsupported URI: " + uri);
371     }
372     /* SysAddr sa = */ SysAddr.ofValue(bb.getInt());
373     /* int id = */ bb.getInt();
374     /* int unit = */ bb.getInt();
375 
376     while (bb.remaining() > 0) {
377       if (bb.getInt() != 0) {
378         throw new SocketException("Unsupported URI: " + uri);
379       }
380     }
381 
382     return
383 
384     resolveAddress(bb.array(), uri.getPort(), addressFamily());
385   }
386 
387   @Override
388   @SuppressWarnings({"PMD.CognitiveComplexity", "PMD.CompareObjectsWithEquals"})
389   public URI toURI(String scheme, URI template) throws IOException {
390     switch (scheme) {
391       case "afsystem":
392         break;
393       default:
394         return super.toURI(scheme, template);
395     }
396 
397     ByteBuffer bb = ByteBuffer.wrap(getBytes());
398     StringBuilder sb = new StringBuilder();
399     while (bb.remaining() > 0) {
400       sb.append(toUnsignedString(bb.getInt()));
401       if (bb.remaining() > 0) {
402         sb.append('.');
403       }
404     }
405 
406     return new HostAndPort(sb.toString(), getPort()).toURI(scheme, template);
407   }
408 }