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