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.FileDescriptor;
22 import java.io.IOException;
23 import java.net.Socket;
24 import java.net.SocketAddress;
25 import java.net.SocketException;
26 import java.net.SocketImpl;
27 import java.util.concurrent.atomic.AtomicBoolean;
28
29 import org.eclipse.jdt.annotation.NonNull;
30 import org.eclipse.jdt.annotation.Nullable;
31
32 import com.kohlschutter.annotations.compiletime.SuppressFBWarnings;
33
34
35
36
37
38
39
40 @SuppressWarnings({"PMD.CouplingBetweenObjects", "PMD.CyclomaticComplexity"})
41 public abstract class AFSocket<A extends AFSocketAddress> extends Socket implements AFSomeSocket,
42 AFSocketExtensions {
43 static final String PROP_LIBRARY_DISABLE_CAPABILITY_PREFIX =
44 "org.newsclub.net.unix.library.disable.";
45
46 private static final byte[] ZERO_BYTES = new byte[0];
47
48 @SuppressWarnings("PMD.MutableStaticState")
49 static String loadedLibrary;
50
51 private static Integer capabilitiesValue = null;
52
53 private final AFSocketImpl<A> impl;
54
55 private final AFSocketAddressFromHostname<A> afh;
56 private final Closeables closeables = new Closeables();
57 private final AtomicBoolean created = new AtomicBoolean(false);
58
59 @SuppressWarnings("this-escape")
60 private final AFSocketChannel<A> channel = newChannel();
61
62 private @Nullable SocketAddressFilter connectFilter;
63
64
65
66
67
68
69
70
71 @SuppressFBWarnings("CT_CONSTRUCTOR_THROW")
72 protected AFSocket(final AFSocketImpl<A> impl, AFSocketAddressFromHostname<A> afh)
73 throws SocketException {
74 super(impl);
75 this.afh = afh;
76 this.impl = impl;
77 }
78
79
80
81
82
83
84 protected final Class<? extends AFSocketAddress> socketAddressClass() {
85 return getAFImpl(false).getAddressFamily().getSocketAddressClass();
86 }
87
88
89
90
91
92
93 protected abstract AFSocketChannel<A> newChannel();
94
95
96
97
98
99
100 @FunctionalInterface
101 public interface Constructor<A extends AFSocketAddress> {
102
103
104
105
106
107
108
109
110 @NonNull
111 AFSocket<A> newInstance(FileDescriptor fdObj, AFSocketFactory<A> factory)
112 throws SocketException;
113 }
114
115 static <A extends AFSocketAddress> AFSocket<A> newInstance(Constructor<A> constr,
116 AFSocketFactory<A> sf, FileDescriptor fdObj, int localPort, int remotePort)
117 throws IOException {
118 if (!fdObj.valid()) {
119 throw new SocketException("Invalid file descriptor");
120 }
121 int status = NativeUnixSocket.socketStatus(fdObj);
122 if (status == NativeUnixSocket.SOCKETSTATUS_INVALID) {
123 throw new SocketException("Not a valid socket");
124 }
125
126 AFSocket<A> socket = newInstance0(constr, fdObj, sf);
127 socket.getAFImpl().updatePorts(localPort, remotePort);
128
129 switch (status) {
130 case NativeUnixSocket.SOCKETSTATUS_CONNECTED:
131 socket.internalDummyConnect();
132 break;
133 case NativeUnixSocket.SOCKETSTATUS_BOUND:
134 socket.internalDummyBind();
135 break;
136 case NativeUnixSocket.SOCKETSTATUS_UNKNOWN:
137 break;
138 default:
139 throw new IllegalStateException("Invalid socketStatus response: " + status);
140 }
141 socket.getAFImpl().setSocketAddress(socket.getLocalSocketAddress());
142
143 return socket;
144 }
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160 protected static final <A extends AFSocketAddress> AFSocket<A> newInstance(Constructor<A> constr,
161 AFSocketFactory<A> factory) throws SocketException {
162 return newInstance0(constr, null, factory);
163 }
164
165 private static <A extends AFSocketAddress> @NonNull AFSocket<A> newInstance0(
166 Constructor<A> constr, FileDescriptor fdObj, AFSocketFactory<A> factory)
167 throws SocketException {
168 return constr.newInstance(fdObj, factory);
169 }
170
171
172
173
174
175
176
177
178
179
180 protected static final <A extends AFSocketAddress> @NonNull AFSocket<A> connectTo(
181 Constructor<A> constr, A addr) throws IOException {
182 AFSocket<A> socket = constr.newInstance(null, null);
183 socket.connect(addr);
184 return socket;
185 }
186
187
188
189
190
191
192
193
194
195
196 public static final <A extends AFSocketAddress> AFSocket<?> connectTo(@NonNull A addr)
197 throws IOException {
198 AFSocket<?> socket = addr.getAddressFamily().getSocketConstructor().newInstance(null, null);
199 socket.connect(addr);
200 return socket;
201 }
202
203
204
205
206
207
208 @Override
209 public final void bind(SocketAddress bindpoint) throws IOException {
210 if (bindpoint == null) {
211 throw new IllegalArgumentException();
212 }
213 if (isClosed()) {
214 throw new SocketException("Socket is closed");
215 }
216 if (isBound()) {
217 throw new SocketException("Already bound");
218 }
219 preprocessSocketAddress(bindpoint);
220 throw new SocketException("Use AF*ServerSocket#bind or #bindOn");
221 }
222
223 @Override
224 public final boolean isBound() {
225 return impl.getFD().valid() && (super.isBound() || impl.isBound());
226 }
227
228 @Override
229 public final boolean isConnected() {
230 return impl.getFD().valid() && (super.isConnected() || impl.isConnected());
231 }
232
233 @Override
234 public final void connect(SocketAddress endpoint) throws IOException {
235 connect(endpoint, 0);
236 }
237
238 @Override
239 public final void connect(SocketAddress endpoint, int timeout) throws IOException {
240 connect0(endpoint, timeout);
241 }
242
243 private AFSocketAddress preprocessSocketAddress(SocketAddress endpoint) throws SocketException {
244 if (endpoint == null) {
245 throw new IllegalArgumentException("endpoint is null");
246 } else if (endpoint instanceof SentinelSocketAddress) {
247 return (AFSocketAddress) endpoint;
248 } else {
249 return AFSocketAddress.preprocessSocketAddress(socketAddressClass(), endpoint, afh);
250 }
251 }
252
253 final boolean connect0(SocketAddress endpoint, int timeout) throws IOException {
254 if (timeout < 0) {
255 throw new IllegalArgumentException("connect: timeout can't be negative");
256 }
257 if (isClosed()) {
258 throw new SocketException("Socket is closed");
259 }
260
261 if (connectFilter != null) {
262 endpoint = connectFilter.apply(endpoint);
263 }
264
265 AFSocketAddress address = preprocessSocketAddress(endpoint);
266
267 if (!isBound()) {
268 internalDummyBind();
269 }
270
271 boolean success = getAFImpl().connect0(address, timeout);
272 if (success) {
273 int port = address.getPort();
274 if (port > 0) {
275 getAFImpl().updatePorts(getLocalPort(), port);
276 }
277 }
278 internalDummyConnect();
279 return success;
280 }
281
282 final void internalDummyConnect() throws IOException {
283 if (!isConnected()) {
284 super.connect(AFSocketAddress.INTERNAL_DUMMY_CONNECT, 0);
285 }
286 }
287
288 final void internalDummyBind() throws IOException {
289 if (!isBound()) {
290 super.bind(AFSocketAddress.INTERNAL_DUMMY_BIND);
291 }
292 }
293
294 @Override
295 public final String toString() {
296 return getClass().getName() + "@" + Integer.toHexString(hashCode()) + toStringSuffix();
297 }
298
299 final String toStringSuffix() {
300 if (impl.getFD().valid()) {
301 return "[local=" + getLocalSocketAddress() + ";remote=" + getRemoteSocketAddress() + "]";
302 } else {
303 return "[invalid]";
304 }
305 }
306
307
308
309
310
311
312
313
314
315 public static boolean isSupported() {
316 return NativeUnixSocket.isLoaded();
317 }
318
319
320
321
322
323
324
325
326 public static void ensureSupported() throws UnsupportedOperationException {
327 NativeUnixSocket.ensureSupported();
328 }
329
330
331
332
333
334
335
336
337
338 public static final String getVersion() {
339 String v = BuildProperties.getBuildProperties().get("git.build.version");
340 if (v != null && !v.startsWith("$")) {
341 return v;
342 }
343
344 try {
345 return NativeLibraryLoader.getJunixsocketVersion();
346 } catch (IOException e) {
347 return null;
348 }
349 }
350
351
352
353
354
355
356
357
358
359 public static final String getLoadedLibrary() {
360 return loadedLibrary;
361 }
362
363 @Override
364 public final boolean isClosed() {
365 return super.isClosed() || (isConnected() && !impl.getFD().valid()) || impl.isClosed();
366 }
367
368 @Override
369 public final int getAncillaryReceiveBufferSize() {
370 return impl.getAncillaryReceiveBufferSize();
371 }
372
373 @Override
374 public final void setAncillaryReceiveBufferSize(int size) {
375 impl.setAncillaryReceiveBufferSize(size);
376 }
377
378 @Override
379 public final void ensureAncillaryReceiveBufferSize(int minSize) {
380 impl.ensureAncillaryReceiveBufferSize(minSize);
381 }
382
383 private static boolean isCapDisabled(AFSocketCapability cap) {
384 return Boolean.parseBoolean(System.getProperty(PROP_LIBRARY_DISABLE_CAPABILITY_PREFIX + cap
385 .name(), "false"));
386 }
387
388 private static int initCapabilities() {
389 if (!isSupported()) {
390 return 0;
391 } else {
392 int v = NativeUnixSocket.capabilities();
393
394 if (System.getProperty("osv.version") != null) {
395
396 v &= ~(AFSocketCapability.CAPABILITY_FD_AS_REDIRECT.getBitmask());
397 }
398
399 for (AFSocketCapability cap : AFSocketCapability.values()) {
400 if (isCapDisabled(cap)) {
401 v &= ~(cap.getBitmask());
402 }
403 }
404
405 return v;
406 }
407 }
408
409 private static synchronized int capabilities() {
410 if (capabilitiesValue == null) {
411 capabilitiesValue = initCapabilities();
412 }
413 return capabilitiesValue;
414 }
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429 @Deprecated
430 public static final boolean supports(AFUNIXSocketCapability capability) {
431 return (capabilities() & capability.getBitmask()) != 0;
432 }
433
434
435
436
437
438
439
440
441
442
443
444 public static final boolean supports(AFSocketCapability capability) {
445 return (capabilities() & capability.getBitmask()) != 0;
446 }
447
448
449
450
451
452
453
454
455
456
457 public static final void ensureUnsafeSupported() throws IOException {
458 if (!AFSocket.supports(AFSocketCapability.CAPABILITY_UNSAFE)) {
459 throw new IOException("Unsafe operations are not supported in this environment");
460 }
461 }
462
463 @Override
464 public final synchronized void close() throws IOException {
465 IOException superException = null;
466 try {
467 super.close();
468 } catch (IOException e) {
469 superException = e;
470 }
471 closeables.close(superException);
472 }
473
474
475
476
477
478
479 public final void addCloseable(Closeable closeable) {
480 closeables.add(closeable);
481 }
482
483
484
485
486
487
488 public final void removeCloseable(Closeable closeable) {
489 closeables.remove(closeable);
490 }
491
492 final AFSocketImpl<A> getAFImpl() {
493 return getAFImpl(true);
494 }
495
496 final AFSocketImpl<A> getAFImpl(boolean createSocket) {
497 if (createSocket && created.compareAndSet(false, true)) {
498 try {
499 getSoTimeout();
500 } catch (SocketException e) {
501
502 }
503 }
504 return impl;
505 }
506
507 @SuppressFBWarnings("EI_EXPOSE_REP")
508 @Override
509 public AFSocketChannel<A> getChannel() {
510 return channel;
511 }
512
513 @SuppressWarnings("null")
514 @Override
515 public final synchronized A getRemoteSocketAddress() {
516 if (!isConnected()) {
517 return null;
518 }
519 return impl.getRemoteSocketAddress();
520 }
521
522 @SuppressWarnings("null")
523 @Override
524 public final A getLocalSocketAddress() {
525 if (isClosed()) {
526 return null;
527 }
528 return impl.getLocalSocketAddress();
529 }
530
531 @Override
532 public final FileDescriptor getFileDescriptor() throws IOException {
533 return impl.getFileDescriptor();
534 }
535
536 @Override
537 public final AFInputStream getInputStream() throws IOException {
538 return getAFImpl().getInputStream();
539 }
540
541 @Override
542 public final AFOutputStream getOutputStream() throws IOException {
543 return getAFImpl().getOutputStream();
544 }
545
546
547
548
549
550
551
552
553 protected final AFSocketImplExtensions<A> getImplExtensions() {
554 return getAFImpl(false).getImplExtensions();
555 }
556
557
558
559
560
561
562
563
564 public final AFSocket<A> forceConnectAddress(SocketAddress endpoint) {
565 return connectHook((SocketAddress orig) -> {
566 return orig == null ? null : endpoint;
567 });
568 }
569
570
571
572
573
574
575
576
577
578
579 public final AFSocket<A> connectHook(SocketAddressFilter hook) {
580 this.connectFilter = hook;
581 return this;
582 }
583
584
585
586
587
588
589
590
591
592
593
594 public boolean checkConnectionClosed() throws IOException {
595 if (!isConnected()) {
596 return true;
597 }
598 try {
599 if (!AFSocket.supports(AFSocketCapability.CAPABILITY_ZERO_LENGTH_SEND)) {
600 return false;
601 }
602 getOutputStream().write(ZERO_BYTES);
603 return false;
604 } catch (SocketClosedException e) {
605 return true;
606 } catch (IOException e) {
607 if (!isConnected()) {
608 return true;
609 } else {
610 throw e;
611 }
612 }
613 }
614
615
616
617
618
619
620 public static boolean isRunningOnAndroid() {
621 return NativeLibraryLoader.isAndroid();
622 }
623
624 @Override
625 public void setShutdownOnClose(boolean enabled) {
626 getAFImpl().getCore().setShutdownOnClose(enabled);
627 }
628 }