1 /*
2  * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package sun.nio.ch;
27 
28 import java.io.FileDescriptor;
29 import java.io.IOException;
30 import java.net.BindException;
31 import java.net.NetPermission;
32 import java.net.SocketAddress;
33 import java.net.UnixDomainSocketAddress;
34 import java.nio.channels.UnsupportedAddressTypeException;
35 import java.nio.file.FileSystems;
36 import java.nio.file.InvalidPathException;
37 import java.nio.file.Path;
38 import java.nio.file.spi.FileSystemProvider;
39 import java.security.NoSuchAlgorithmException;
40 import java.security.SecureRandom;
41 import java.util.Random;
42 import sun.nio.fs.AbstractFileSystemProvider;
43 
44 /*
45  * @hide
46  */
47 class UnixDomainSockets {
UnixDomainSockets()48     private UnixDomainSockets() { }
49 
50     static final UnixDomainSocketAddress UNNAMED = UnixDomainSocketAddress.of("");
51 
52     private static final boolean supported;
53 
54     private static final String tempDir = UnixDomainSocketsUtil.getTempDir();
55 
56     private static final NetPermission accessUnixDomainSocket =
57             new NetPermission("accessUnixDomainSocket");
58 
isSupported()59     static boolean isSupported() {
60         return supported;
61     }
62 
checkPermission()63     static void checkPermission() {
64         @SuppressWarnings("removal")
65         SecurityManager sm = System.getSecurityManager();
66         if (sm != null)
67             sm.checkPermission(accessUnixDomainSocket);
68     }
69 
getRevealedLocalAddress(SocketAddress sa)70     static UnixDomainSocketAddress getRevealedLocalAddress(SocketAddress sa) {
71         UnixDomainSocketAddress addr = (UnixDomainSocketAddress) sa;
72         try {
73             checkPermission();
74             // Security check passed
75         } catch (SecurityException e) {
76             // Return unnamed address only if security check fails
77             addr = UNNAMED;
78         }
79         return addr;
80     }
81 
localAddress(FileDescriptor fd)82     static UnixDomainSocketAddress localAddress(FileDescriptor fd) throws IOException {
83         String path = new String(localAddress0(fd), UnixDomainSocketsUtil.getCharset());
84         return UnixDomainSocketAddress.of(path);
85     }
86 
localAddress0(FileDescriptor fd)87     private static native byte[] localAddress0(FileDescriptor fd) throws IOException;
88 
89     @SuppressWarnings("removal")
getRevealedLocalAddressAsString(SocketAddress sa)90     static String getRevealedLocalAddressAsString(SocketAddress sa) {
91         return (System.getSecurityManager() != null) ? sa.toString() : "";
92     }
93 
checkAddress(SocketAddress sa)94     static UnixDomainSocketAddress checkAddress(SocketAddress sa) {
95         if (sa == null)
96             throw new NullPointerException();
97         if (!(sa instanceof UnixDomainSocketAddress))
98             throw new UnsupportedAddressTypeException();
99         return (UnixDomainSocketAddress) sa;
100     }
101 
getPathBytes(Path path)102     static byte[] getPathBytes(Path path) {
103         FileSystemProvider provider = FileSystems.getDefault().provider();
104         return ((AbstractFileSystemProvider) provider).getSunPathForSocketFile(path);
105     }
106 
socket()107     static FileDescriptor socket() throws IOException {
108         return IOUtil.newFD(socket0());
109     }
110 
bind(FileDescriptor fd, Path addr)111     static void bind(FileDescriptor fd, Path addr) throws IOException {
112         byte[] path = getPathBytes(addr);
113         if (path.length == 0) {
114             throw new BindException("Server socket cannot bind to unnamed address");
115         }
116         bind0(fd, path);
117     }
118 
getRandom()119     private static Random getRandom() {
120         try {
121             return SecureRandom.getInstance("NativePRNGNonBlocking");
122         } catch (NoSuchAlgorithmException e) {
123             return new SecureRandom(); // This should not fail
124         }
125     }
126 
127     private static final Random random = getRandom();
128 
129     /**
130      * Return a possible temporary name to bind to, which is different for each call
131      * Name is of the form <temp dir>/socket_<random>
132      */
generateTempName()133     static UnixDomainSocketAddress generateTempName() throws IOException {
134         String dir = UnixDomainSockets.tempDir;
135         if (dir == null)
136             throw new BindException("Could not locate temporary directory for sockets");
137         int rnd = random.nextInt(Integer.MAX_VALUE);
138         try {
139             Path path = Path.of(dir, "socket_" + rnd);
140             return UnixDomainSocketAddress.of(path);
141         } catch (InvalidPathException e) {
142             throw new BindException("Invalid temporary directory");
143         }
144     }
145 
connect(FileDescriptor fd, SocketAddress sa)146     static int connect(FileDescriptor fd, SocketAddress sa) throws IOException {
147         return UnixDomainSockets.connect(fd, ((UnixDomainSocketAddress) sa).getPath());
148     }
149 
connect(FileDescriptor fd, Path path)150     static int connect(FileDescriptor fd, Path path) throws IOException {
151         return connect0(fd, getPathBytes(path));
152     }
153 
accept(FileDescriptor fd, FileDescriptor newfd, String[] paths)154     static int accept(FileDescriptor fd, FileDescriptor newfd, String[] paths)
155         throws IOException
156     {
157         Object[] array  = new Object[1];
158         int n = accept0(fd, newfd, array);
159         if (n > 0) {
160             byte[] bytes = (byte[]) array[0];
161             paths[0] = new String(bytes, UnixDomainSocketsUtil.getCharset());
162         }
163         return n;
164     }
165 
init()166     private static native boolean init();
167 
socket0()168     private static native int socket0() throws IOException;
169 
bind0(FileDescriptor fd, byte[] path)170     private static native void bind0(FileDescriptor fd, byte[] path)
171         throws IOException;
172 
connect0(FileDescriptor fd, byte[] path)173     private static native int connect0(FileDescriptor fd, byte[] path)
174         throws IOException;
175 
accept0(FileDescriptor fd, FileDescriptor newfd, Object[] array)176     private static native int accept0(FileDescriptor fd, FileDescriptor newfd, Object[] array)
177         throws IOException;
178 
179     static {
180         // Load all required native libs
181         // Android-removed: no native libs.
182         // IOUtil.load();
183         supported = init();
184     }
185 }
186