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.FileNotFoundException;
22 import java.io.IOException;
23 import java.net.InetAddress;
24 import java.net.InetSocketAddress;
25 import java.net.SocketAddress;
26 import java.net.SocketException;
27 import java.net.URI;
28 import java.net.URISyntaxException;
29 import java.nio.ByteBuffer;
30 import java.nio.charset.Charset;
31 import java.nio.charset.StandardCharsets;
32 import java.nio.file.Path;
33 import java.util.Arrays;
34 import java.util.HashSet;
35 import java.util.Locale;
36 import java.util.Objects;
37 import java.util.Set;
38
39 import org.eclipse.jdt.annotation.NonNull;
40
41
42
43
44
45
46
47
48
49 @SuppressWarnings("PMD.ShortMethodName")
50 public final class AFUNIXSocketAddress extends AFSocketAddress {
51 private static final long serialVersionUID = 1L;
52
53 private static final Charset ADDRESS_CHARSET = Charset.defaultCharset();
54
55 @SuppressWarnings("null")
56 static final AFAddressFamily<@NonNull AFUNIXSocketAddress> AF_UNIX = AFAddressFamily
57 .registerAddressFamily("un",
58 AFUNIXSocketAddress.class, new AFSocketAddressConfig<AFUNIXSocketAddress>() {
59
60 private final AFSocketAddressConstructor<AFUNIXSocketAddress> addrConstr =
61 isUseDeserializationForInit() ? AFUNIXSocketAddress::newAFSocketAddress
62 : AFUNIXSocketAddress::new;
63
64 @Override
65 public AFUNIXSocketAddress parseURI(URI u, int port) throws SocketException {
66 return AFUNIXSocketAddress.of(u, port);
67 }
68
69 @Override
70 protected AFSocketAddressConstructor<AFUNIXSocketAddress> addressConstructor() {
71 return addrConstr;
72 }
73
74 @Override
75 protected String selectorProviderClassname() {
76 return AFUNIXSelectorProvider.class.getName();
77 }
78
79 @Override
80 protected Set<String> uriSchemes() {
81 return new HashSet<>(Arrays.asList("unix", "http+unix", "https+unix"));
82 }
83 });
84
85 private AFUNIXSocketAddress(int port, final byte[] socketAddress, ByteBuffer nativeAddress)
86 throws SocketException {
87 super(port, socketAddress, nativeAddress, AF_UNIX);
88 }
89
90
91
92
93
94
95
96
97
98
99 @Deprecated
100 public AFUNIXSocketAddress(File socketFile) throws SocketException {
101 this(socketFile, 0);
102 }
103
104
105
106
107
108
109
110
111
112
113
114 @Deprecated
115 public AFUNIXSocketAddress(File socketFile, int port) throws SocketException {
116 this(port, of(socketFile, port).getPathAsBytes(), of(socketFile, port)
117 .getNativeAddressDirectBuffer());
118 }
119
120 static AFUNIXSocketAddress newAFSocketAddress(int port, final byte[] socketAddress,
121 ByteBuffer nativeAddress) throws SocketException {
122 return newDeserializedAFSocketAddress(port, socketAddress, nativeAddress, AF_UNIX,
123 AFUNIXSocketAddress::new);
124 }
125
126
127
128
129
130
131
132
133
134 public static AFUNIXSocketAddress of(final File socketFile) throws SocketException {
135 return of(socketFile, 0);
136 }
137
138
139
140
141
142
143
144
145
146
147 public static AFUNIXSocketAddress of(final File socketFile, int port) throws SocketException {
148 return of(socketFile.getPath().getBytes(ADDRESS_CHARSET), port);
149 }
150
151
152
153
154
155
156
157
158
159
160
161
162
163 public static AFUNIXSocketAddress of(final byte[] socketAddress) throws SocketException {
164 return of(socketAddress, 0);
165 }
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180 public static AFUNIXSocketAddress of(final byte[] socketAddress, int port)
181 throws SocketException {
182 return AFSocketAddress.resolveAddress(socketAddress, port, AF_UNIX);
183 }
184
185
186
187
188
189
190
191
192
193 public static AFUNIXSocketAddress of(Path socketPath) throws SocketException {
194 return of(socketPath, 0);
195 }
196
197
198
199
200
201
202
203
204
205
206 public static AFUNIXSocketAddress of(Path socketPath, int port) throws SocketException {
207 if (!PathUtil.isPathInDefaultFileSystem(socketPath)) {
208 throw new SocketException("Path is not in the default file system");
209 }
210
211 return of(socketPath.toString().getBytes(ADDRESS_CHARSET), port);
212 }
213
214
215
216
217
218
219
220
221 public static AFUNIXSocketAddress of(URI u) throws SocketException {
222 return of(u, -1);
223 }
224
225
226
227
228
229
230
231
232
233 public static AFUNIXSocketAddress of(URI u, int overridePort) throws SocketException {
234 switch (u.getScheme()) {
235 case "file":
236 case "unix":
237 String path = u.getPath();
238 if (path == null || path.isEmpty()) {
239 String auth = u.getAuthority();
240 if (auth != null && !auth.isEmpty() && u.getRawSchemeSpecificPart().indexOf('@') == -1) {
241 path = auth;
242 } else {
243 throw new SocketException("Cannot find UNIX socket path component from URI: " + u);
244 }
245 }
246 return of(new File(path), overridePort != -1 ? overridePort : u.getPort());
247 case "http+unix":
248 case "https+unix":
249 HostAndPort hp = HostAndPort.parseFrom(u);
250 return of(new File(hp.getHostname()), overridePort != -1 ? overridePort : hp.getPort());
251 default:
252 throw new SocketException("Invalid URI");
253 }
254 }
255
256
257
258
259
260
261
262
263 public static AFUNIXSocketAddress ofNewTempFile() throws IOException {
264 return ofNewTempPath(0);
265 }
266
267
268
269
270
271
272
273
274
275 public static AFUNIXSocketAddress ofNewTempPath(int port) throws IOException {
276 return of(newTempPath(true), port);
277 }
278
279
280
281
282
283
284
285
286
287
288
289 public static AFUNIXSocketAddress of(SocketAddress address) throws IOException {
290 AFUNIXSocketAddress addr = unwrap(Objects.requireNonNull(address));
291 if (addr == null) {
292 throw new SocketException("Could not convert SocketAddress to AFUNIXSocketAddress");
293 }
294 return addr;
295 }
296
297 static File newTempPath(boolean deleteOnExit) throws IOException {
298 File f = File.createTempFile("jux", ".sock");
299 if (deleteOnExit) {
300 f.deleteOnExit();
301 }
302 if (!f.delete() && f.exists()) {
303 throw new IOException("Could not delete temporary file that we just created: " + f);
304 }
305 return f;
306 }
307
308
309
310
311
312
313
314
315
316
317
318 public static AFUNIXSocketAddress unwrap(InetAddress address, int port) throws SocketException {
319 return AFSocketAddress.unwrap(address, port, AF_UNIX);
320 }
321
322
323
324
325
326
327
328
329
330 public static AFUNIXSocketAddress unwrap(SocketAddress address) throws SocketException {
331
332 Objects.requireNonNull(address);
333 AFSupplier<AFUNIXSocketAddress> supplier = supportedAddressSupplier(address);
334 if (supplier == null) {
335 throw new SocketException("Unsupported address");
336 }
337 return supplier.get();
338 }
339
340
341
342
343
344
345
346
347
348
349
350
351 public static AFUNIXSocketAddress unwrap(String hostname, int port) throws SocketException {
352 return AFSocketAddress.unwrap(hostname, port, AF_UNIX);
353 }
354
355
356
357
358
359
360
361
362
363
364
365
366 public static AFUNIXSocketAddress inAbstractNamespace(String name) throws SocketException {
367 return inAbstractNamespace(name, 0);
368 }
369
370
371
372
373
374
375
376
377
378
379
380
381
382 public static AFUNIXSocketAddress inAbstractNamespace(String name, int port)
383 throws SocketException {
384 byte[] bytes = name.getBytes(ADDRESS_CHARSET);
385 byte[] addr = new byte[bytes.length + 1];
386 System.arraycopy(bytes, 0, addr, 1, bytes.length);
387 return AFUNIXSocketAddress.of(addr, port);
388 }
389
390 private static String prettyPrint(byte[] data) {
391 final int dataLength = data.length;
392 if (dataLength == 0) {
393 return "";
394 }
395 StringBuilder sb = new StringBuilder(dataLength + 16);
396 for (int i = 0; i < dataLength; i++) {
397 byte c = data[i];
398 if (c >= 32 && c < 127) {
399 sb.append((char) c);
400 } else {
401 sb.append("\\x");
402 sb.append(String.format(Locale.ENGLISH, "%02x", c));
403 }
404 }
405 return sb.toString();
406 }
407
408 @Override
409 public String toString() {
410 int port = getPort();
411 return getClass().getName() + "[" + (port == 0 ? "" : "port=" + port + ";") + "path="
412 + prettyPrint(getBytes()) + "]";
413 }
414
415
416
417
418
419
420
421
422
423
424
425 public String getPath() {
426 byte[] bytes = getBytes();
427 if (bytes.length == 0) {
428 return "";
429 } else if (bytes[0] != 0) {
430 return new String(bytes, ADDRESS_CHARSET);
431 }
432
433 byte[] by = bytes.clone();
434 for (int i = 0; i < by.length; i++) {
435 byte b = by[i];
436 if (b == 0) {
437 by[i] = '@';
438 } else if (b >= 32 && b < 127) {
439
440 } else {
441 by[i] = '.';
442 }
443 }
444 return new String(by, StandardCharsets.US_ASCII);
445 }
446
447
448
449
450
451
452
453
454
455 public static Charset addressCharset() {
456 return ADDRESS_CHARSET;
457 }
458
459
460
461
462
463
464
465 public byte[] getPathAsBytes() {
466 return getBytes().clone();
467 }
468
469
470
471
472
473
474
475 public boolean isInAbstractNamespace() {
476 byte[] bytes = getBytes();
477 return bytes.length > 0 && bytes[0] == 0;
478 }
479
480 @Override
481 public boolean hasFilename() {
482 byte[] bytes = getBytes();
483 return bytes.length > 0 && bytes[0] != 0;
484 }
485
486 @Override
487 public File getFile() throws FileNotFoundException {
488 if (isInAbstractNamespace()) {
489 throw new FileNotFoundException("Socket is in abstract namespace");
490 }
491 byte[] bytes = getBytes();
492
493 if (bytes.length == 0) {
494 throw new FileNotFoundException("No name");
495 }
496 return new File(new String(bytes, ADDRESS_CHARSET));
497 }
498
499
500
501
502
503
504
505
506
507 public static boolean isSupportedAddress(InetAddress addr) {
508 return AFInetAddress.isSupportedAddress(addr, AF_UNIX);
509 }
510
511
512
513
514
515
516
517
518 public static boolean isSupportedAddress(SocketAddress addr) {
519 return supportedAddressSupplier(addr) != null;
520 }
521
522
523
524
525
526
527
528
529 static AFSupplier<AFUNIXSocketAddress> supportedAddressSupplier(SocketAddress addr) {
530 if (addr == null) {
531 return null;
532 } else if (addr instanceof AFUNIXSocketAddress) {
533 return () -> ((AFUNIXSocketAddress) addr);
534 } else {
535 return SocketAddressUtil.supplyAFUNIXSocketAddress(addr);
536 }
537 }
538
539
540
541
542
543
544 @SuppressWarnings("null")
545 public static AFAddressFamily<AFUNIXSocketAddress> addressFamily() {
546 return AFUNIXSelectorProvider.getInstance().addressFamily();
547 }
548
549 @Override
550 public URI toURI(String scheme, URI template) throws IOException {
551 switch (scheme) {
552 case "unix":
553 case "file":
554 try {
555 if (getPort() > 0 && !"file".equals(scheme)) {
556 return new URI(scheme, null, "localhost", getPort(), getPath(), null, (String) null);
557 } else {
558 return new URI(scheme, null, null, -1, getPath(), null, null);
559 }
560 } catch (URISyntaxException e) {
561 throw new IOException(e);
562 }
563 case "http+unix":
564 case "https+unix":
565 HostAndPort hp = new HostAndPort(getPath(), getPort());
566 return hp.toURI(scheme, template);
567 default:
568 return super.toURI(scheme, template);
569 }
570 }
571
572 @Override
573 public AFUNIXSocket newConnectedSocket() throws IOException {
574 return (AFUNIXSocket) super.newConnectedSocket();
575 }
576
577 @Override
578 public AFUNIXServerSocket newBoundServerSocket() throws IOException {
579 return (AFUNIXServerSocket) super.newBoundServerSocket();
580 }
581
582 @Override
583 public AFUNIXServerSocket newForceBoundServerSocket() throws IOException {
584 return (AFUNIXServerSocket) super.newForceBoundServerSocket();
585 }
586 }