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.IOException;
21  import java.lang.reflect.InvocationTargetException;
22  import java.net.SocketException;
23  import java.net.URI;
24  import java.nio.channels.ServerSocketChannel;
25  import java.nio.channels.SocketChannel;
26  import java.nio.channels.UnsupportedAddressTypeException;
27  import java.nio.channels.spi.SelectorProvider;
28  import java.util.Collections;
29  import java.util.HashMap;
30  import java.util.HashSet;
31  import java.util.Map;
32  import java.util.Objects;
33  import java.util.Set;
34  import java.util.concurrent.atomic.AtomicBoolean;
35  
36  import org.eclipse.jdt.annotation.NonNull;
37  import org.eclipse.jdt.annotation.Nullable;
38  import org.newsclub.net.unix.AFSocketAddress.AFSocketAddressConstructor;
39  
40  /**
41   * Describes an address family supported by junixsocket.
42   *
43   * @param <A> The corresponding {@link AFSocketAddress} subclass.
44   * @author Christian Kohlschütter
45   */
46  public final class AFAddressFamily<A extends AFSocketAddress> {
47    private static final Map<String, AFAddressFamily<?>> AF_MAP = Collections.synchronizedMap(
48        new HashMap<>());
49    private static final Map<String, AFAddressFamily<?>> URI_SCHEMES = Collections.synchronizedMap(
50        new HashMap<>());
51    private static final AtomicBoolean DEFERRED_INIT_DONE = new AtomicBoolean(false);
52  
53    private final int domain;
54    private AFSocketAddressConstructor<A> addressConstructor;
55    private @Nullable Class<A> addressClass;
56    private final String juxString;
57    private final String juxInetAddressSuffix;
58    private final String addressClassname;
59  
60    private String selectorProviderClassname;
61  
62    private AFSocket.Constructor<A> socketConstructor;
63    private AFServerSocket.Constructor<A> serverSocketConstructor;
64    private AFSocketAddressConfig<A> addressConfig;
65  
66    private SelectorProvider selectorProvider = null;
67  
68    static {
69      NativeUnixSocket.isLoaded(); // trigger init
70    }
71  
72    private AFAddressFamily(String juxString, int domain, String addressClassname) {
73      this.juxString = juxString;
74      this.domain = domain; // FIXME validate
75      this.addressClassname = addressClassname;
76      this.juxInetAddressSuffix = "." + juxString + AFInetAddress.INETADDR_SUFFIX;
77    }
78  
79    @SuppressWarnings("unchecked")
80    static synchronized <A extends AFSocketAddress> @NonNull AFAddressFamily<A> registerAddressFamily(
81        String juxString, int domain, String addressClassname) {
82      AFAddressFamily<?> af = AF_MAP.get(juxString);
83      if (af != null) {
84        if (af.getDomain() != domain) {
85          throw new IllegalStateException("Wrong domain for address family " + juxString + ": " + af
86              .getDomain() + " vs. " + domain);
87        }
88        return (AFAddressFamily<A>) af;
89      }
90  
91      af = new AFAddressFamily<>(juxString, domain, addressClassname);
92      AF_MAP.put(juxString, af);
93  
94      return (AFAddressFamily<A>) af;
95    }
96  
97    static synchronized void triggerInit() {
98      for (AFAddressFamily<?> af : new HashSet<>(AF_MAP.values())) {
99        if (af.addressClassname != null) {
100         try {
101           Class<?> clz = Class.forName(af.addressClassname);
102           clz.getMethod("addressFamily").invoke(null);
103         } catch (Exception e) {
104           // ignore
105         }
106       }
107     }
108   }
109 
110   static synchronized AFAddressFamily<?> getAddressFamily(String juxString) {
111     return AF_MAP.get(juxString);
112   }
113 
114   static AFAddressFamily<?> getAddressFamily(URI uri) {
115     checkDeferredInit();
116     Objects.requireNonNull(uri, "uri");
117     String scheme = uri.getScheme();
118     return URI_SCHEMES.get(scheme);
119   }
120 
121   static void checkDeferredInit() {
122     if (DEFERRED_INIT_DONE.compareAndSet(false, true)) {
123       NativeUnixSocket.isLoaded();
124       AFAddressFamily.triggerInit();
125     }
126   }
127 
128   int getDomain() {
129     return domain;
130   }
131 
132   String getJuxString() {
133     return juxString;
134   }
135 
136   AFSocketAddressConstructor<A> getAddressConstructor() {
137     if (addressConstructor == null) {
138       throw new UnsupportedAddressTypeException();
139     }
140     return addressConstructor;
141   }
142 
143   private synchronized void checkProvider() {
144     if (socketConstructor == null && selectorProvider == null) {
145       try {
146         getSelectorProvider();
147       } catch (IllegalStateException e) {
148         // ignore
149       }
150     }
151   }
152 
153   AFSocket.Constructor<A> getSocketConstructor() {
154     checkProvider();
155     if (socketConstructor == null) {
156       throw new UnsupportedAddressTypeException();
157     }
158     return socketConstructor;
159   }
160 
161   AFServerSocket.Constructor<A> getServerSocketConstructor() {
162     checkProvider();
163     if (serverSocketConstructor == null) {
164       throw new UnsupportedAddressTypeException();
165     }
166     return serverSocketConstructor;
167   }
168 
169   Class<A> getSocketAddressClass() {
170     if (addressClass == null) {
171       throw new UnsupportedAddressTypeException();
172     }
173     return addressClass;
174   }
175 
176   String getJuxInetAddressSuffix() {
177     return juxInetAddressSuffix;
178   }
179 
180   /**
181    * Registers an address family.
182    *
183    * @param <A> The supported address type.
184    * @param juxString The sockaddr_* identifier as registered in native code.
185    * @param addressClass The supported address subclass.
186    * @param config The address-specific config object.
187    * @return The corresponding {@link AFAddressFamily} instance.
188    */
189   @SuppressWarnings({"unchecked", "rawtypes"})
190   public static synchronized <A extends AFSocketAddress> AFAddressFamily<A> registerAddressFamily(
191       String juxString, //
192       Class<A> addressClass, AFSocketAddressConfig<A> config) {
193     AFAddressFamily<?> af = getAddressFamily(juxString);
194     if (af == null) {
195       throw new IllegalStateException("Address family not supported by native code: " + juxString);
196     }
197     if (af.addressClassname != null && !addressClass.getName().equals(af.addressClassname)) {
198       throw new IllegalStateException("Unexpected classname for address family " + juxString + ": "
199           + addressClass.getName() + "; expected: " + af.addressClassname);
200     }
201     if (af.addressConstructor != null || af.addressClass != null) {
202       throw new IllegalStateException("Already registered: " + juxString);
203     }
204     af.addressConfig = (AFSocketAddressConfig) config;
205     af.addressConstructor = (AFSocketAddressConstructor) config.addressConstructor();
206     af.addressClass = (Class) addressClass;
207     synchronized (af) { // work-around for likely false positive Spotbugs error
208       af.selectorProviderClassname = config.selectorProviderClassname();
209     }
210 
211     for (String scheme : config.uriSchemes()) {
212       if (scheme.isEmpty()) {
213         throw new IllegalStateException("Invalid URI scheme; cannot register " + scheme + " for "
214             + juxString);
215 
216       }
217       if (URI_SCHEMES.containsKey(scheme)) {
218         throw new IllegalStateException("URI scheme already registered; cannot register " + scheme
219             + " for " + juxString);
220       }
221       URI_SCHEMES.put(scheme, af);
222     }
223 
224     return (AFAddressFamily<A>) af;
225   }
226 
227   /**
228    * Registers an implementation.
229    *
230    * @param <A> The supported address type.
231    * @param juxString The sockaddr_* identifier as registered in native code.
232    * @param addressFamily The supported address family as registered via
233    *          {@link #registerAddressFamily(String, Class, AFSocketAddressConfig)}.
234    * @param config The address family-specific configuration object.
235    * @return The corresponding {@link AFAddressFamily} instance.
236    */
237   @SuppressWarnings({"unchecked", "rawtypes", "PMD.ExcessiveParameterList"})
238   public static synchronized <A extends AFSocketAddress> AFAddressFamily<A> registerAddressFamilyImpl(
239       String juxString, //
240       AFAddressFamily<A> addressFamily, //
241       AFAddressFamilyConfig<A> config) {
242     Objects.requireNonNull(addressFamily);
243     Objects.requireNonNull(config);
244 
245     AFAddressFamily<?> af = getAddressFamily(juxString);
246     if (af == null) {
247       throw new IllegalStateException("Unknown address family: " + juxString);
248     }
249     if (addressFamily != af) { // NOPMD.CompareObjectsWithEquals
250       throw new IllegalStateException("Address family inconsistency: " + juxString);
251     }
252     if (af.socketConstructor != null) {
253       throw new IllegalStateException("Already registered: " + juxString);
254     }
255     af.socketConstructor = (AFSocket.Constructor) config.socketConstructor();
256     af.serverSocketConstructor = (AFServerSocket.Constructor) config.serverSocketConstructor();
257 
258     FileDescriptorCast.registerCastingProviders(config);
259 
260     return (AFAddressFamily<A>) af;
261   }
262 
263   @SuppressWarnings("unchecked")
264   AFSocketImplExtensions<A> initImplExtensions(AncillaryDataSupport ancillaryDataSupport) {
265     switch (getDomain()) {
266       case NativeUnixSocket.DOMAIN_TIPC:
267         return (AFSocketImplExtensions<A>) new AFTIPCSocketImplExtensions(ancillaryDataSupport);
268       case NativeUnixSocket.DOMAIN_VSOCK:
269         return (AFSocketImplExtensions<A>) new AFVSOCKSocketImplExtensions(ancillaryDataSupport);
270       case NativeUnixSocket.DOMAIN_SYSTEM:
271         return (AFSocketImplExtensions<A>) new AFSYSTEMSocketImplExtensions(ancillaryDataSupport);
272       default:
273         throw new UnsupportedOperationException();
274     }
275   }
276 
277   /**
278    * Creates a new, unconnected, unbound socket compatible with this socket address.
279    *
280    * @return The socket instance.
281    * @throws IOException on error.
282    */
283   public AFSocket<?> newSocket() throws IOException {
284     try {
285       return getSocketConstructor().newInstance(null, null);
286     } catch (UnsupportedOperationException e) {
287       throw (SocketException) new SocketException().initCause(e);
288     }
289   }
290 
291   /**
292    * Creates a new, unconnected, unbound server socket compatible with this socket address.
293    *
294    * @return The server socket instance.
295    * @throws IOException on error.
296    */
297   public AFServerSocket<?> newServerSocket() throws IOException {
298     try {
299       return getServerSocketConstructor().newInstance(null);
300     } catch (UnsupportedOperationException e) {
301       throw (SocketException) new SocketException().initCause(e);
302     }
303   }
304 
305   /**
306    * Creates a new, unconnected, unbound {@link SocketChannel} compatible with this socket address.
307    *
308    * @return The socket instance.
309    * @throws IOException on error.
310    */
311   public AFSocketChannel<?> newSocketChannel() throws IOException {
312     return newSocket().getChannel();
313   }
314 
315   /**
316    * Creates a new, unconnected, unbound {@link ServerSocketChannel} compatible with this socket
317    * address.
318    *
319    * @return The socket instance.
320    * @throws IOException on error.
321    */
322   public AFServerSocketChannel<?> newServerSocketChannel() throws IOException {
323     return newServerSocket().getChannel();
324   }
325 
326   AFSocketAddress parseURI(URI u, int overridePort) throws SocketException {
327     if (addressConfig == null) {
328       throw new SocketException("Cannot instantiate addresses of type " + addressClass);
329     }
330     return addressConfig.parseURI(u, overridePort);
331   }
332 
333   /**
334    * Returns the set of supported URI schemes that can be parsed to some {@link AFSocketAddress}.
335    *
336    * The set is dependent on which {@link AFSocketAddress} implementations are registered with
337    * junixsocket.
338    *
339    * @return The set of supported URI schemes.
340    */
341   public static synchronized Set<String> uriSchemes() {
342     checkDeferredInit();
343     return Collections.unmodifiableSet(URI_SCHEMES.keySet());
344   }
345 
346   /**
347    * Returns the {@link SelectorProvider} associated with this address family, or {@code null} if no
348    * such instance is registered.
349    *
350    * @return The {@link SelectorProvider}.
351    * @throws IllegalStateException on error.
352    */
353   public synchronized SelectorProvider getSelectorProvider() {
354     if (selectorProvider != null) {
355       return selectorProvider;
356     }
357     if (selectorProviderClassname == null) {
358       return null;
359     }
360     try {
361       selectorProvider = (SelectorProvider) Class.forName(selectorProviderClassname).getMethod(
362           "provider", new Class<?>[0]).invoke(null);
363     } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException
364         | ClassNotFoundException | RuntimeException e) {
365       throw new IllegalStateException("Cannot instantiate selector provider for "
366           + addressClassname, e);
367     }
368     return selectorProvider;
369   }
370 }