View Javadoc
1   /*
2    * junixsocket
3    *
4    * Copyright 2009-2024 Christian Kohlschütter
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.newsclub.net.unix;
19  
20  import java.io.FileDescriptor;
21  import java.io.IOException;
22  import java.lang.reflect.InvocationTargetException;
23  import java.net.SocketException;
24  import java.nio.ByteBuffer;
25  import java.util.concurrent.atomic.AtomicInteger;
26  import java.util.concurrent.atomic.AtomicLong;
27  
28  /**
29   * A shared core that is common for all AF* sockets (datagrams, streams).
30   *
31   * @author Christian Kohlschütter
32   */
33  class AFSocketCore extends AFCore {
34    private final AtomicInteger pendingAccepts = new AtomicInteger(0);
35    private static final int SHUT_RD_WR = 2;
36  
37    /**
38     * We keep track of the server's inode to detect when another server connects to our address.
39     */
40    final AtomicLong inode = new AtomicLong(-1);
41  
42    AFSocketAddress socketAddress;
43  
44    private final AFAddressFamily<?> af;
45    private boolean shutdownOnClose = true;
46  
47    protected AFSocketCore(Object observed, FileDescriptor fd,
48        AncillaryDataSupport ancillaryDataSupport, AFAddressFamily<?> af, boolean datagramMode) {
49      super(observed, fd, ancillaryDataSupport, datagramMode);
50      this.af = af;
51    }
52  
53    protected AFAddressFamily<?> addressFamily() {
54      return af;
55    }
56  
57    @Override
58    @SuppressWarnings("UnsafeFinalization" /* errorprone */)
59    protected void doClose() throws IOException {
60      if (isShutdownOnClose()) {
61        NativeUnixSocket.shutdown(fd, SHUT_RD_WR);
62        unblockAccepts();
63      }
64  
65      super.doClose();
66    }
67  
68    protected void unblockAccepts() {
69    }
70  
71    AFSocketAddress receive(ByteBuffer dst) throws IOException {
72      ByteBuffer socketAddressBuffer = AFSocketAddress.SOCKETADDRESS_BUFFER_TL.get();
73      int read = read(dst, socketAddressBuffer, 0);
74      if (read > 0) {
75        return AFSocketAddress.ofInternal(socketAddressBuffer, af);
76      } else {
77        return null;
78      }
79    }
80  
81    boolean isConnected(boolean boundOk) {
82      try {
83        if (fd.valid()) {
84          switch (NativeUnixSocket.socketStatus(fd)) {
85            case NativeUnixSocket.SOCKETSTATUS_CONNECTED:
86              return true;
87            case NativeUnixSocket.SOCKETSTATUS_BOUND:
88              if (boundOk) {
89                return true;
90              }
91              break;
92            default:
93          }
94        }
95      } catch (IOException e) {
96        throw new IllegalStateException(e);
97      }
98      return false;
99    }
100 
101   @SuppressWarnings({"unchecked"})
102   <T> T getOption(AFSocketOption<T> name) throws IOException {
103     Class<T> type = name.type();
104     if (Boolean.class.isAssignableFrom(type)) {
105       return (T) (Object) (NativeUnixSocket.getSocketOption(fd, name.level(), name.optionName(),
106           Integer.class) != 0);
107     } else if (NamedInteger.HasOfValue.class.isAssignableFrom(type)) {
108       @SuppressWarnings("all") // "null" creates another warning
109       int v = NativeUnixSocket.getSocketOption(fd, name.level(), name.optionName(), Integer.class);
110       try {
111         return (T) type.getMethod("ofValue", int.class).invoke(null, v);
112       } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException
113           | NoSuchMethodException | SecurityException e) {
114         throw new IOException("Value casting problem", e);
115       }
116     } else {
117       return NativeUnixSocket.getSocketOption(fd, name.level(), name.optionName(), type);
118     }
119   }
120 
121   <T> void setOption(AFSocketOption<T> name, T value) throws IOException {
122     final Object val;
123     if (value instanceof Boolean) {
124       val = (((Boolean) value) ? 1 : 0);
125     } else if (value instanceof NamedInteger) {
126       val = ((NamedInteger) value).value();
127     } else {
128       val = value;
129     }
130     int level = name.level();
131     int optionName = name.optionName();
132     NativeUnixSocket.setSocketOption(fd, level, optionName, val);
133     if (level == 271 && optionName == 135) {
134       // AFTIPCSocketOptions.TIPC_GROUP_JOIN
135       // unclear why, but sleeping for at least 1ms prevents issues with GROUP_JOIN
136       try {
137         Thread.sleep(1);
138       } catch (InterruptedException e) {
139         // ignore
140       }
141     }
142   }
143 
144   protected void incPendingAccepts() throws SocketException {
145     if (pendingAccepts.incrementAndGet() >= Integer.MAX_VALUE) {
146       throw new SocketException("Too many pending accepts");
147     }
148   }
149 
150   protected void decPendingAccepts() throws SocketException {
151     pendingAccepts.decrementAndGet();
152   }
153 
154   protected boolean hasPendingAccepts() {
155     return pendingAccepts.get() > 0;
156   }
157 
158   boolean isShutdownOnClose() {
159     return shutdownOnClose;
160   }
161 
162   void setShutdownOnClose(boolean enabled) {
163     this.shutdownOnClose = enabled;
164   }
165 }