1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.newsclub.net.unix;
19
20 import java.io.File;
21 import java.io.IOException;
22 import java.net.Socket;
23 import java.net.SocketException;
24 import java.net.URLDecoder;
25 import java.util.Objects;
26
27 import javax.net.SocketFactory;
28
29 import com.kohlschutter.annotations.compiletime.SuppressFBWarnings;
30
31
32
33
34
35
36
37
38
39
40
41 public abstract class AFUNIXSocketFactory extends AFSocketFactory<AFUNIXSocketAddress> {
42
43
44
45 protected AFUNIXSocketFactory() {
46 super(AFUNIXSocketAddress.class);
47 }
48
49 @Override
50 public Socket createSocket() throws SocketException {
51 return AFUNIXSocket.newInstance(this);
52 }
53
54 @Override
55 protected AFUNIXSocket connectTo(AFUNIXSocketAddress addr) throws IOException {
56 return AFUNIXSocket.connectTo(addr);
57 }
58
59
60
61
62
63
64 private abstract static class DefaultSocketHostnameSocketFactory extends AFUNIXSocketFactory {
65 private static final String PROP_SOCKET_HOSTNAME = "org.newsclub.net.unix.socket.hostname";
66
67
68
69
70 public DefaultSocketHostnameSocketFactory() {
71 super();
72 }
73
74 @Override
75 public final boolean isHostnameSupported(String host) {
76 return getDefaultSocketHostname().equals(host);
77 }
78
79 private static String getDefaultSocketHostname() {
80 return System.getProperty(PROP_SOCKET_HOSTNAME, "localhost");
81 }
82 }
83
84
85
86
87
88
89
90
91
92
93
94 @SuppressFBWarnings("PATH_TRAVERSAL_IN")
95 public static final class FactoryArg extends DefaultSocketHostnameSocketFactory {
96 private final File socketFile;
97
98
99
100
101
102
103 public FactoryArg(String socketPath) {
104 super();
105 Objects.requireNonNull(socketPath, "Socket path was null");
106
107 this.socketFile = new File(socketPath);
108 }
109
110
111
112
113
114
115 public FactoryArg(File file) {
116 super();
117 Objects.requireNonNull(file, "File was null");
118
119 this.socketFile = file;
120 }
121
122 @Override
123 public AFUNIXSocketAddress addressFromHost(String host, int port) throws SocketException {
124 return AFUNIXSocketAddress.of(socketFile, port);
125 }
126 }
127
128
129
130
131
132
133
134
135
136
137
138
139 public static final class SystemProperty extends DefaultSocketHostnameSocketFactory {
140 private static final String PROP_SOCKET_DEFAULT = "org.newsclub.net.unix.socket.default";
141
142
143
144
145 public SystemProperty() {
146 super();
147 }
148
149 @Override
150 public AFUNIXSocketAddress addressFromHost(String host, int port) throws SocketException {
151 String path = System.getProperty(PROP_SOCKET_DEFAULT);
152 if (path == null || path.isEmpty()) {
153 throw new IllegalStateException("Property not configured: " + PROP_SOCKET_DEFAULT);
154 }
155 File socketFile = new File(path);
156
157 return AFUNIXSocketAddress.of(socketFile, port);
158 }
159 }
160
161
162
163
164
165
166
167
168
169
170
171
172
173 @SuppressFBWarnings("PATH_TRAVERSAL_IN")
174 public static final class URIScheme extends AFUNIXSocketFactory {
175 private static final String FILE_SCHEME_PREFIX = "file://";
176 private static final String FILE_SCHEME_PREFIX_ENCODED = "file%";
177 private static final String FILE_SCHEME_LOCALHOST = "localhost";
178
179
180
181
182 public URIScheme() {
183 super();
184 }
185
186 private static String stripBrackets(String host) {
187 if (host.startsWith("[")) {
188 if (host.endsWith("]")) {
189 host = host.substring(1, host.length() - 1);
190 } else {
191 host = host.substring(1);
192 }
193 }
194 return host;
195 }
196
197 @Override
198 public boolean isHostnameSupported(String host) {
199 host = stripBrackets(host);
200 return host.startsWith(FILE_SCHEME_PREFIX) || host.startsWith(FILE_SCHEME_PREFIX_ENCODED);
201 }
202
203 @Override
204 @SuppressWarnings("PMD.UseStandardCharsets")
205 public AFUNIXSocketAddress addressFromHost(String host, int port) throws SocketException {
206 host = stripBrackets(host);
207 if (host.startsWith(FILE_SCHEME_PREFIX_ENCODED)) {
208 try {
209 host = URLDecoder.decode(host, "UTF-8");
210 } catch (Exception e) {
211 throw (SocketException) new SocketException().initCause(e);
212 }
213 }
214 if (!host.startsWith(FILE_SCHEME_PREFIX)) {
215 throw new SocketException("Unsupported scheme");
216 }
217
218 String path = host.substring(FILE_SCHEME_PREFIX.length());
219 if (path.startsWith(FILE_SCHEME_LOCALHOST)) {
220 path = path.substring(FILE_SCHEME_LOCALHOST.length());
221 }
222 if (path.isEmpty()) {
223 throw new SocketException("Path is empty");
224 }
225 if (!path.startsWith("/")) {
226 throw new SocketException("Path must be absolute");
227 }
228
229 File socketFile = new File(path);
230 return AFUNIXSocketAddress.of(socketFile, port);
231 }
232 }
233 }