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