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