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