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.Closeable;
21  import java.io.FileDescriptor;
22  import java.io.IOException;
23  import java.lang.ProcessBuilder.Redirect;
24  import java.net.InetSocketAddress;
25  import java.net.ServerSocket;
26  import java.net.Socket;
27  import java.net.SocketAddress;
28  import java.net.SocketException;
29  import java.nio.ByteBuffer;
30  import java.nio.channels.SelectionKey;
31  import java.nio.channels.spi.AbstractSelectableChannel;
32  import java.util.concurrent.atomic.AtomicBoolean;
33  
34  import org.newsclub.net.unix.AFSelector.PollFd;
35  
36  import com.kohlschutter.annotations.compiletime.ExcludeFromCodeCoverageGeneratedReport;
37  import com.kohlschutter.annotations.compiletime.SuppressFBWarnings;
38  
39  /**
40   * JNI connector to native JNI C code.
41   *
42   * @author Christian Kohlschütter
43   */
44  final class NativeUnixSocket {
45    private static final AtomicBoolean LOADED = new AtomicBoolean(false);
46  
47    static final int DOMAIN_GENERIC = -1;
48    static final int DOMAIN_UNIX = 1;
49    static final int DOMAIN_TIPC = 30;
50    static final int DOMAIN_VSOCK = 40;
51    static final int DOMAIN_SYSTEM = 32;
52  
53    static final int SOCK_STREAM = 1;
54    static final int SOCK_DGRAM = 2;
55    static final int SOCK_RAW = 3;
56    static final int SOCK_RDM = 4;
57    static final int SOCK_SEQPACKET = 5;
58  
59    static final int OPT_LOOKUP_SENDER = 1;
60    static final int OPT_PEEK = 2;
61    static final int OPT_NON_BLOCKING = 4;
62  
63    /**
64     * Indicator that the handle isn't referring to a socket but some other file descriptor.
65     */
66    static final int OPT_NON_SOCKET = 8;
67  
68    static final int OPT_DGRAM_MODE = 16;
69  
70    static final int BIND_OPT_REUSE = 1;
71  
72    static final int SOCKETSTATUS_INVALID = -1;
73    static final int SOCKETSTATUS_UNKNOWN = 0;
74    static final int SOCKETSTATUS_BOUND = 1;
75    static final int SOCKETSTATUS_CONNECTED = 2;
76  
77    @SuppressWarnings("StaticAssignmentOfThrowable" /* errorprone */)
78    private static Throwable initError = null;
79  
80    static final int SHUT_RD = 0;
81    static final int SHUT_WR = 1;
82    static final int SHUT_RD_WR = 2;
83  
84    @ExcludeFromCodeCoverageGeneratedReport(reason = "unreachable")
85    private NativeUnixSocket() {
86      throw new UnsupportedOperationException("No instances");
87    }
88  
89    static {
90      boolean loadSuccessful = false;
91      try (NativeLibraryLoader nll = new NativeLibraryLoader()) {
92        nll.loadLibrary();
93        loadSuccessful = true;
94      } catch (RuntimeException | Error e) {
95        initError = e;
96        StackTraceUtil.printStackTraceSevere(e);
97      } finally {
98        setLoaded(loadSuccessful);
99      }
100 
101     AFAddressFamily.registerAddressFamily("generic", NativeUnixSocket.DOMAIN_GENERIC,
102         "org.newsclub.net.unix.AFGenericSocketAddress");
103     AFAddressFamily.registerAddressFamily("un", NativeUnixSocket.DOMAIN_UNIX,
104         "org.newsclub.net.unix.AFUNIXSocketAddress");
105     AFAddressFamily.registerAddressFamily("tipc", NativeUnixSocket.DOMAIN_TIPC,
106         "org.newsclub.net.unix.AFTIPCSocketAddress");
107     AFAddressFamily.registerAddressFamily("vsock", NativeUnixSocket.DOMAIN_VSOCK,
108         "org.newsclub.net.unix.AFVSOCKSocketAddress");
109     AFAddressFamily.registerAddressFamily("system", NativeUnixSocket.DOMAIN_SYSTEM,
110         "org.newsclub.net.unix.AFSYSTEMSocketAddress");
111   }
112 
113   static boolean isLoaded() {
114     return LOADED.get();
115   }
116 
117   static void ensureSupported() throws UnsupportedOperationException {
118     if (!isLoaded()) {
119       throw unsupportedException();
120     }
121   }
122 
123   static UnsupportedOperationException unsupportedException() {
124     if (!isLoaded()) {
125       return (UnsupportedOperationException) new UnsupportedOperationException(
126           "junixsocket may not be fully supported on this platform").initCause(initError);
127     } else {
128       return null;
129     }
130   }
131 
132   static Throwable retrieveInitError() {
133     return initError;
134   }
135 
136   static void initPre() {
137     // in some environments, JNI FindClass won't find these classes unless we resolve them first
138     tryResolveClass(AbstractSelectableChannel.class.getName());
139     tryResolveClass("java.lang.ProcessBuilder$RedirectPipeImpl");
140     tryResolveClass(InetSocketAddress.class.getName());
141     tryResolveClass(InvalidArgumentSocketException.class.getName());
142     tryResolveClass(AddressUnavailableSocketException.class.getName());
143     tryResolveClass(OperationNotSupportedSocketException.class.getName());
144     tryResolveClass(NoSuchDeviceSocketException.class.getName());
145     tryResolveClass(BrokenPipeSocketException.class.getName());
146     tryResolveClass(ConnectionResetSocketException.class.getName());
147     tryResolveClass(SocketClosedException.class.getName());
148   }
149 
150   private static void tryResolveClass(String className) {
151     try {
152       Class.forName(className);
153     } catch (Exception e) {
154       // ignore
155     }
156   }
157 
158   @SuppressFBWarnings("THROWS_METHOD_THROWS_CLAUSE_BASIC_EXCEPTION")
159   static native void init() throws Exception;
160 
161   @SuppressFBWarnings("THROWS_METHOD_THROWS_CLAUSE_BASIC_EXCEPTION")
162   static native void destroy() throws Exception;
163 
164   /**
165    * Can be used to check (without side-effects) if the library has been loaded.
166    *
167    * Terminates normally if so; throws {@link UnsatisfiedLinkError} if not.
168    */
169   static native void noop();
170 
171   static native int capabilities();
172 
173   static native byte[] sockname(int domain, FileDescriptor fd, boolean peer);
174 
175   static native long bind(ByteBuffer sockaddr, int sockaddrLen, FileDescriptor fd, int options)
176       throws IOException;
177 
178   static native void listen(FileDescriptor fd, int backlog) throws IOException;
179 
180   static native boolean accept(ByteBuffer sockaddr, int sockaddrLen, FileDescriptor fdServer,
181       FileDescriptor fd, long inode, int timeout) throws IOException;
182 
183   static native boolean connect(ByteBuffer sockaddr, int sockaddrLen, FileDescriptor fd, long inode)
184       throws IOException;
185 
186   static native boolean finishConnect(FileDescriptor fd) throws IOException;
187 
188   static native void disconnect(FileDescriptor fd) throws IOException;
189 
190   static native int socketStatus(FileDescriptor fd) throws IOException;
191 
192   static native FileDescriptor duplicate(FileDescriptor fdSource, FileDescriptor fdTarget)
193       throws IOException;
194 
195   static native Class<?> primaryType(FileDescriptor fd) throws IOException;
196 
197   /**
198    * Reads data from an {@link AFSocketImpl}.
199    *
200    * @param fd The corresponding file descriptor.
201    * @param buf The buffer to read into, or {@code null} if a single byte should be read.
202    * @param off The buffer offset.
203    * @param len The maximum number of bytes to read. Must be 1 if {@code buf} is {@code null}.
204    * @param options Options.
205    * @param ancillaryDataSupport The ancillary data support instance, or {@code null}.
206    * @return The number of bytes read, -1 if nothing could be read, or the byte itself iff
207    *         {@code buf} was {@code null}.
208    * @throws IOException upon error.
209    */
210   static native int read(FileDescriptor fd, byte[] buf, int off, int len, int options,
211       AncillaryDataSupport ancillaryDataSupport, int timeoutMillis) throws IOException;
212 
213   /**
214    * Writes data to an {@link AFSocketImpl}.
215    *
216    * @param fd The corresponding file descriptor.
217    * @param buf The buffer to write from, or {@code null} if a single byte should be written.
218    * @param off The buffer offset, or the byte to write if {@code buf} is {@code null}.
219    * @param len The number of bytes to write. Must be 1 if {@code buf} is {@code null}.
220    * @param options Options.
221    * @param ancillaryDataSupport The ancillary data support instance, or {@code null}.
222    * @return The number of bytes written (which could be 0).
223    * @throws IOException upon error.
224    */
225   static native int write(FileDescriptor fd, byte[] buf, int off, int len, int options,
226       AncillaryDataSupport ancillaryDataSupport) throws IOException;
227 
228   static native int receive(FileDescriptor fd, ByteBuffer directBuffer, int offset, int length,
229       ByteBuffer directSocketAddressOut, int options, AncillaryDataSupport ancillaryDataSupport,
230       int timeoutMillis) throws IOException;
231 
232   static native int send(FileDescriptor fd, ByteBuffer directBuffer, int offset, int length,
233       ByteBuffer directSocketAddress, int addrLen, int options,
234       AncillaryDataSupport ancillaryDataSupport) throws IOException;
235 
236   static native void close(FileDescriptor fd) throws IOException;
237 
238   static native void shutdown(FileDescriptor fd, int mode) throws IOException;
239 
240   static native int getSocketOptionInt(FileDescriptor fd, int optionId) throws IOException;
241 
242   static native void setSocketOptionInt(FileDescriptor fd, int optionId, int value)
243       throws IOException;
244 
245   static native <T> T getSocketOption(FileDescriptor fd, int level, int optionName,
246       Class<T> valueType) throws IOException;
247 
248   static native void setSocketOption(FileDescriptor fd, int level, int optionName, Object value)
249       throws IOException;
250 
251   static native int available(FileDescriptor fd, ByteBuffer buf) throws IOException;
252 
253   static native AFUNIXSocketCredentials peerCredentials(FileDescriptor fd,
254       AFUNIXSocketCredentials creds) throws IOException;
255 
256   static native void initServerImpl(ServerSocket serverSocket, AFSocketImpl<?> impl)
257       throws IOException;
258 
259   static native void createSocket(FileDescriptor fdesc, int domain, int type) throws IOException;
260 
261   static native void setPort(SocketAddress addr, int port);
262 
263   static native void initFD(FileDescriptor fdesc, int fd) throws IOException;
264 
265   static native int getFD(FileDescriptor fdesc) throws IOException;
266 
267   static native void copyFileDescriptor(FileDescriptor source, FileDescriptor target)
268       throws IOException;
269 
270   static native void attachCloseable(FileDescriptor fdesc, Closeable closeable)
271       throws SocketException;
272 
273   static native int maxAddressLength();
274 
275   static native int sockAddrLength(int domain);
276 
277   static native int ancillaryBufMinLen();
278 
279   static native byte[] sockAddrToBytes(int domain, ByteBuffer sockAddrDirect);
280 
281   static native int bytesToSockAddr(int domain, ByteBuffer sockAddrDirectBuf, byte[] address);
282 
283   @SuppressFBWarnings("THROWS_METHOD_THROWS_RUNTIMEEXCEPTION")
284   static void setPort1(SocketAddress addr, int port) throws SocketException {
285     if (port < 0) {
286       throw new IllegalArgumentException("port out of range:" + port);
287     }
288 
289     try {
290       setPort(addr, port);
291     } catch (RuntimeException e) {
292       throw e;
293     } catch (Exception e) {
294       throw (SocketException) new SocketException("Could not set port").initCause(e);
295     }
296   }
297 
298   static native Socket currentRMISocket();
299 
300   static native boolean initPipe(FileDescriptor source, FileDescriptor sink, boolean selectable)
301       throws IOException;
302 
303   static native int poll(PollFd pollFd, int timeout) throws IOException;
304 
305   static native void configureBlocking(FileDescriptor fd, boolean blocking) throws IOException;
306 
307   /**
308    * Checks if the given file descriptor describes a blocking socket.
309    *
310    * @param fd The file descriptor to check
311    * @return 0 = non-blocking, 1 = blocking, 2 = indeterminate (needs reconfiguration)
312    * @throws IOException on error.
313    */
314   static native int checkBlocking(FileDescriptor fd) throws IOException;
315 
316   static native void socketPair(int domain, int type, FileDescriptor fd, FileDescriptor fd2);
317 
318   static native Redirect initRedirect(FileDescriptor fd);
319 
320   static native void deregisterSelectionKey(AbstractSelectableChannel chann, SelectionKey key);
321 
322   static native byte[] tipcGetNodeId(int peer) throws IOException;
323 
324   static native byte[] tipcGetLinkName(int peer, int bearerId) throws IOException;
325 
326   static native int sockAddrNativeDataOffset();
327 
328   static native int sockAddrNativeFamilyOffset();
329 
330   static native int sockTypeToNative(int type) throws IOException;
331 
332   static native int vsockGetLocalCID() throws IOException;
333 
334   static native int systemResolveCtlId(FileDescriptor fd, String ctlName) throws IOException;
335 
336   static void setLoaded(boolean successful) {
337     LOADED.compareAndSet(false, successful);
338   }
339 }