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.Closeable;
21 import java.io.File;
22 import java.io.FileDescriptor;
23 import java.io.IOException;
24 import java.net.InetAddress;
25 import java.net.ServerSocket;
26 import java.net.SocketAddress;
27 import java.net.SocketException;
28 import java.net.SocketOption;
29 import java.net.SocketOptions;
30 import java.nio.channels.IllegalBlockingModeException;
31 import java.util.Arrays;
32 import java.util.Objects;
33 import java.util.Set;
34 import java.util.concurrent.atomic.AtomicBoolean;
35
36 import org.eclipse.jdt.annotation.NonNull;
37 import org.eclipse.jdt.annotation.Nullable;
38
39 import com.kohlschutter.annotations.compiletime.SuppressFBWarnings;
40
41
42
43
44
45
46
47 @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.CouplingBetweenObjects"})
48 @SuppressFBWarnings("UNENCRYPTED_SERVER_SOCKET")
49 public abstract class AFServerSocket<A extends AFSocketAddress> extends ServerSocket implements
50 AFSomeSocketThing {
51 private final AFSocketImpl<A> implementation;
52 private @Nullable A boundEndpoint;
53 private final Closeables closeables = new Closeables();
54 private final AtomicBoolean created = new AtomicBoolean(false);
55 private final AtomicBoolean deleteOnClose = new AtomicBoolean(true);
56
57 @SuppressWarnings("this-escape")
58 private final AFServerSocketChannel<A> channel = newChannel();
59 private @Nullable SocketAddressFilter bindFilter;
60
61 private final AtomicBoolean closed = new AtomicBoolean(false);
62
63
64
65
66
67
68 @FunctionalInterface
69 public interface Constructor<A extends AFSocketAddress> {
70
71
72
73
74
75
76
77 @NonNull
78 AFServerSocket<A> newInstance(FileDescriptor fd) throws IOException;
79 }
80
81
82
83
84
85
86 @SuppressFBWarnings("CT_CONSTRUCTOR_THROW")
87 protected AFServerSocket() throws IOException {
88 this(null);
89 }
90
91
92
93
94
95
96
97 @SuppressWarnings({"this-escape", "PMD.ConstructorCallsOverridableMethod"})
98 @SuppressFBWarnings("CT_CONSTRUCTOR_THROW")
99 protected AFServerSocket(FileDescriptor fdObj) throws IOException {
100 super();
101
102 this.implementation = newImpl(fdObj);
103 NativeUnixSocket.initServerImpl(this, implementation);
104
105 getAFImpl().setOption(SocketOptions.SO_REUSEADDR, true);
106 }
107
108
109
110
111
112
113 protected abstract AFServerSocketChannel<A> newChannel();
114
115
116
117
118
119
120
121
122 protected abstract AFSocketImpl<A> newImpl(FileDescriptor fdObj) throws IOException;
123
124
125
126
127
128
129
130
131
132 protected static <A extends AFSocketAddress> AFServerSocket<A> newInstance(
133 Constructor<A> instanceSupplier) throws IOException {
134 return instanceSupplier.newInstance(null);
135 }
136
137
138
139
140
141
142
143
144
145
146
147
148 protected static <A extends AFSocketAddress> AFServerSocket<A> newInstance(
149 Constructor<A> instanceSupplier, FileDescriptor fdObj, int localPort, int remotePort)
150 throws IOException {
151 if (fdObj == null) {
152 return instanceSupplier.newInstance(null);
153 }
154
155 int status = NativeUnixSocket.socketStatus(fdObj);
156 if (!fdObj.valid() || status == NativeUnixSocket.SOCKETSTATUS_INVALID) {
157 throw new SocketException("Not a valid socket");
158 }
159 AFServerSocket<A> socket = instanceSupplier.newInstance(fdObj);
160 socket.getAFImpl().updatePorts(localPort, remotePort);
161
162 switch (status) {
163 case NativeUnixSocket.SOCKETSTATUS_CONNECTED:
164 throw new SocketException("Not a ServerSocket");
165 case NativeUnixSocket.SOCKETSTATUS_BOUND:
166 socket.bind(AFSocketAddress.INTERNAL_DUMMY_BIND);
167
168 socket.setBoundEndpoint(AFSocketAddress.getSocketAddress(fdObj, false, localPort, socket
169 .addressFamily()));
170 break;
171 case NativeUnixSocket.SOCKETSTATUS_UNKNOWN:
172 break;
173 default:
174 throw new IllegalStateException("Invalid socketStatus response: " + status);
175 }
176
177 socket.getAFImpl().setSocketAddress(socket.getLocalSocketAddress());
178 return socket;
179 }
180
181
182
183
184
185
186
187
188
189
190 protected static <A extends AFSocketAddress> AFServerSocket<A> bindOn(
191 Constructor<A> instanceSupplier, final AFSocketAddress addr) throws IOException {
192 AFServerSocket<A> socket = instanceSupplier.newInstance(null);
193 socket.bind(addr);
194 return socket;
195 }
196
197
198
199
200
201
202
203
204
205
206
207
208 protected static <A extends AFSocketAddress> AFServerSocket<A> bindOn(
209 Constructor<A> instanceSupplier, final A addr, boolean deleteOnClose) throws IOException {
210 AFServerSocket<A> socket = instanceSupplier.newInstance(null);
211 socket.bind(addr);
212 socket.setDeleteOnClose(deleteOnClose);
213 return socket;
214 }
215
216
217
218
219
220
221
222
223
224
225
226 protected static <A extends AFSocketAddress> AFServerSocket<A> forceBindOn(
227 Constructor<A> instanceSupplier, final A forceAddr) throws IOException {
228 AFServerSocket<A> socket = instanceSupplier.newInstance(null);
229 return socket.forceBindAddress(forceAddr);
230 }
231
232
233
234
235
236
237
238
239
240 public final AFServerSocket<A> forceBindAddress(SocketAddress endpoint) {
241 return bindHook((SocketAddress orig) -> {
242 return orig == null ? null : endpoint;
243 });
244 }
245
246 @Override
247 public final void bind(SocketAddress endpoint) throws IOException {
248 bind(endpoint, 50);
249 }
250
251 @SuppressWarnings("unchecked")
252 @Override
253 public final void bind(SocketAddress endpoint, int backlog) throws IOException {
254 if (isClosed()) {
255 throw new SocketException("Socket is closed");
256 }
257
258 boolean bindErrorOk;
259 if (bindFilter != null) {
260 endpoint = bindFilter.apply(endpoint);
261 bindErrorOk = endpoint != null && isBound();
262 } else {
263 bindErrorOk = false;
264 }
265
266 endpoint = AFSocketAddress.mapOrFail(endpoint);
267
268 A endpointCast;
269 try {
270 endpointCast = (A) endpoint;
271 } catch (ClassCastException e) {
272 throw new IllegalArgumentException("Can only bind to specific endpoints", e);
273 }
274
275 try {
276 getAFImpl().bind(endpoint, getReuseAddress() ? NativeUnixSocket.BIND_OPT_REUSE : 0);
277 } catch (SocketException e) {
278 if (bindErrorOk) {
279
280 return;
281 } else {
282 throw e;
283 }
284 }
285 setBoundEndpoint(getAFImpl().getLocalSocketAddress());
286 if (boundEndpoint0() == null) {
287 setBoundEndpoint(endpointCast);
288 }
289
290 if (endpoint == AFSocketAddress.INTERNAL_DUMMY_BIND) {
291 return;
292 }
293
294 implementation.listen(backlog);
295 }
296
297 @Override
298 public final boolean isBound() {
299 return boundEndpoint0() != null && implementation.getFD().valid();
300 }
301
302 @Override
303 public final boolean isClosed() {
304 return super.isClosed() || (isBound() && !implementation.getFD().valid()) || implementation
305 .isClosed();
306 }
307
308 @Override
309 public AFSocket<A> accept() throws IOException {
310 return accept1(true);
311 }
312
313 AFSocket<A> accept1(boolean throwOnFail) throws IOException {
314 AFSocket<A> as = newSocketInstance();
315
316 boolean success = implementation.accept0(as.getAFImpl(false));
317 if (isClosed()) {
318
319 throw new BrokenPipeSocketException("Socket is closed");
320 }
321
322 if (!success) {
323 if (throwOnFail) {
324 if (getChannel().isBlocking()) {
325
326 return null;
327 } else {
328
329 throw new IllegalBlockingModeException();
330 }
331 } else {
332 return null;
333 }
334 }
335
336 as.getAFImpl(true);
337 as.connect(AFSocketAddress.INTERNAL_DUMMY_CONNECT);
338 as.getAFImpl().updatePorts(getAFImpl().getLocalPort1(), getAFImpl().getRemotePort());
339
340 return as;
341 }
342
343
344
345
346
347
348
349 protected abstract AFSocket<A> newSocketInstance() throws IOException;
350
351 @Override
352 public String toString() {
353 return getClass().getSimpleName() + "[" + (isBound() ? boundEndpoint0() : "unbound") + "]";
354 }
355
356 @Override
357 public void close() throws IOException {
358 if (!closed.compareAndSet(false, true)) {
359 return;
360 }
361 if (isClosed()) {
362 return;
363 }
364
365 boolean localSocketAddressValid = isLocalSocketAddressValid();
366
367 AFSocketAddress endpoint = boundEndpoint;
368
369 IOException superException = null;
370 try {
371 super.close();
372 } catch (IOException e) {
373 superException = e;
374 }
375 if (implementation != null) {
376 try {
377 implementation.close();
378 } catch (IOException e) {
379 if (superException == null) {
380 superException = e;
381 } else {
382 superException.addSuppressed(e);
383 }
384 }
385 }
386
387 IOException ex = null;
388 try {
389 closeables.close(superException);
390 } finally {
391 if (endpoint != null && endpoint.hasFilename() && localSocketAddressValid
392 && isDeleteOnClose()) {
393 File f = endpoint.getFile();
394 if (!f.delete() && f.exists()) {
395 ex = new IOException("Could not delete socket file after close: " + f);
396 }
397 }
398 }
399 if (ex != null) {
400 throw ex;
401 }
402 }
403
404
405
406
407
408
409 public final void addCloseable(Closeable closeable) {
410 closeables.add(closeable);
411 }
412
413
414
415
416
417
418 public final void removeCloseable(Closeable closeable) {
419 closeables.remove(closeable);
420 }
421
422
423
424
425
426
427 public static boolean isSupported() {
428 return NativeUnixSocket.isLoaded();
429 }
430
431 @Override
432 public final @Nullable A getLocalSocketAddress() {
433 @Nullable
434 A ep = boundEndpoint0();
435 if (ep == null) {
436 ep = getAFImpl().getLocalSocketAddress();
437 setBoundEndpoint(ep);
438 }
439 return ep;
440 }
441
442 private synchronized @Nullable A boundEndpoint0() {
443 return boundEndpoint;
444 }
445
446
447
448
449
450
451
452
453
454 public boolean isLocalSocketAddressValid() {
455 if (isClosed()) {
456 return false;
457 }
458 @Nullable
459 A addr = getLocalSocketAddress();
460 if (addr == null) {
461 return false;
462 }
463 byte[] addrBytes = addr.getBytes();
464 if (addrBytes == null) {
465 return false;
466 }
467 byte[] sab = getAFImpl().getLocalSocketAddressBytes();
468 if (sab == null) {
469 return false;
470 }
471 return Arrays.equals(sab, addrBytes);
472 }
473
474 final synchronized void setBoundEndpoint(@Nullable A addr) {
475 this.boundEndpoint = addr;
476 int port;
477 if (addr == null) {
478 port = -1;
479 } else {
480 port = addr.getPort();
481 }
482 getAFImpl().updatePorts(port, -1);
483 }
484
485 @Override
486 public final int getLocalPort() {
487 if (boundEndpoint0() == null) {
488 setBoundEndpoint(getAFImpl().getLocalSocketAddress());
489 }
490 if (boundEndpoint0() == null) {
491 return -1;
492 } else {
493 return getAFImpl().getLocalPort1();
494 }
495 }
496
497
498
499
500
501
502
503
504
505 public final boolean isDeleteOnClose() {
506 return deleteOnClose.get();
507 }
508
509
510
511
512
513
514
515
516
517
518 public final void setDeleteOnClose(boolean b) {
519 deleteOnClose.set(b);
520 }
521
522 final AFSocketImpl<A> getAFImpl() {
523 if (created.compareAndSet(false, true)) {
524 try {
525 getAFImpl().create(true);
526 getSoTimeout();
527 } catch (IOException e) {
528
529 }
530 }
531 return implementation;
532 }
533
534 @SuppressFBWarnings("EI_EXPOSE_REP")
535 @Override
536 public AFServerSocketChannel<A> getChannel() {
537 return channel;
538 }
539
540 @Override
541 public final FileDescriptor getFileDescriptor() throws IOException {
542 return implementation.getFileDescriptor();
543 }
544
545
546
547
548
549
550 protected final AFAddressFamily<A> addressFamily() {
551 return getAFImpl().getAddressFamily();
552 }
553
554
555
556
557
558
559
560
561
562
563 public final AFServerSocket<A> bindHook(SocketAddressFilter hook) {
564 this.bindFilter = hook;
565 return this;
566 }
567
568 @Override
569 public InetAddress getInetAddress() {
570 if (!isBound()) {
571 return null;
572 } else {
573 return getAFImpl().getInetAddress();
574 }
575 }
576
577 @Override
578 public synchronized void setReceiveBufferSize(int size) throws SocketException {
579 if (size <= 0) {
580 throw new IllegalArgumentException("receive buffer size must be a positive number");
581 }
582 if (isClosed()) {
583 throw new SocketException("Socket is closed");
584 }
585 getAFImpl().setOption(SocketOptions.SO_RCVBUF, size);
586 }
587
588 @Override
589 public synchronized int getReceiveBufferSize() throws SocketException {
590 if (isClosed()) {
591 throw new SocketException("Socket is closed");
592 }
593 int result = 0;
594 Object o = getAFImpl().getOption(SocketOptions.SO_RCVBUF);
595 if (o instanceof Number) {
596 result = ((Number) o).intValue();
597 }
598 return result;
599 }
600
601 @Override
602 @SuppressWarnings("UnsynchronizedOverridesSynchronized" )
603 public void setSoTimeout(int timeout) throws SocketException {
604 if (isClosed()) {
605 throw new SocketException("Socket is closed");
606 }
607 if (timeout < 0) {
608 throw new IllegalArgumentException("timeout < 0");
609 }
610 getAFImpl().setOption(SocketOptions.SO_TIMEOUT, timeout);
611 }
612
613 @Override
614 @SuppressWarnings("UnsynchronizedOverridesSynchronized" )
615 public int getSoTimeout() throws IOException {
616 if (isClosed()) {
617 throw new SocketException("Socket is closed");
618 }
619 Object o = getAFImpl().getOption(SocketOptions.SO_TIMEOUT);
620
621 if (o instanceof Number) {
622 return ((Number) o).intValue();
623 } else {
624 return 0;
625 }
626 }
627
628 @Override
629 public void setReuseAddress(boolean on) throws SocketException {
630 if (isClosed()) {
631 throw new SocketException("Socket is closed");
632 }
633 getAFImpl().setOption(SocketOptions.SO_REUSEADDR, on);
634 }
635
636 @Override
637 public boolean getReuseAddress() throws SocketException {
638 if (isClosed()) {
639 throw new SocketException("Socket is closed");
640 }
641 return ((Boolean) (getAFImpl().getOption(SocketOptions.SO_REUSEADDR)));
642 }
643
644 @Override
645 public void setPerformancePreferences(int connectionTime, int latency, int bandwidth) {
646 }
647
648 @SuppressWarnings({"all", "MissingOverride" })
649 public <T> T getOption(SocketOption<T> name) throws IOException {
650 Objects.requireNonNull(name);
651 if (isClosed()) {
652 throw new SocketException("Socket is closed");
653 }
654 return getAFImpl().getOption(name);
655 }
656
657 @SuppressWarnings({"all", "MissingOverride" })
658 public <T> ServerSocket setOption(SocketOption<T> name, T value) throws IOException {
659 Objects.requireNonNull(name);
660 if (isClosed()) {
661 throw new SocketException("Socket is closed");
662 }
663 getAFImpl().setOption(name, value);
664 return this;
665 }
666
667 @SuppressWarnings("all")
668 public Set<SocketOption<?>> supportedOptions() {
669 return getAFImpl().supportedOptions();
670 }
671
672 @Override
673 public void setShutdownOnClose(boolean enabled) {
674 getAFImpl().getCore().setShutdownOnClose(enabled);
675 }
676
677
678
679
680 }