View Javadoc
1   /*
2    * junixsocket
3    *
4    * Copyright 2009-2026 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.AtomicBoolean;
26  import java.util.concurrent.atomic.AtomicInteger;
27  import java.util.concurrent.atomic.AtomicLong;
28  
29  import org.newsclub.net.unix.pool.ObjectPool.Lease;
30  
31  /**
32   * A shared core that is common for all AF* sockets (datagrams, streams).
33   *
34   * @author Christian Kohlschütter
35   */
36  class AFSocketCore extends AFCore {
37    private final AtomicInteger pendingAccepts = new AtomicInteger(0);
38    private static final int SHUT_RD_WR = 2;
39  
40    /**
41     * We keep track of the server's inode to detect when another server connects to our address.
42     */
43    final AtomicLong inode = new AtomicLong(-1);
44  
45    AFSocketAddress socketAddress;
46  
47    private final AFAddressFamily<?> af;
48    private final AtomicBoolean shutdownOnClose = new AtomicBoolean(true);
49  
50    protected AFSocketCore(Object observed, FileDescriptor fd,
51        AncillaryDataSupport ancillaryDataSupport, AFAddressFamily<?> af, boolean datagramMode) {
52      super(observed, fd, ancillaryDataSupport, datagramMode);
53      this.af = af;
54    }
55  
56    protected AFAddressFamily<?> addressFamily() {
57      return af;
58    }
59  
60    @Override
61    @SuppressWarnings("UnsafeFinalization" /* errorprone */)
62    protected void doClose() throws IOException {
63      if (isShutdownOnClose()) {
64        NativeUnixSocket.shutdown(fd, SHUT_RD_WR);
65        unblockAccepts();
66      }
67  
68      super.doClose();
69    }
70  
71    protected void unblockAccepts() {
72      // see AFSocketImpl
73    }
74  
75    AFSocketAddress receive(ByteBuffer dst, AFSupplier<Integer> socketTimeout) throws IOException {
76      try (Lease<ByteBuffer> socketAddressBufferLease = AFSocketAddress.SOCKETADDRESS_BUFFER_TL
77          .take()) {
78        ByteBuffer socketAddressBuffer = socketAddressBufferLease.get();
79  
80        int read = read(dst, socketTimeout, socketAddressBuffer, 0);
81        if (read > 0) {
82          return AFSocketAddress.ofInternal(socketAddressBuffer, af);
83        } else {
84          return null;
85        }
86      }
87    }
88  
89    boolean isConnected(boolean boundOk) {
90      try {
91        if (fd.valid()) {
92          switch (NativeUnixSocket.socketStatus(fd)) {
93            case NativeUnixSocket.SOCKETSTATUS_CONNECTED:
94              return true;
95            case NativeUnixSocket.SOCKETSTATUS_BOUND:
96              if (boundOk) {
97                return true;
98              }
99              break;
100           default:
101         }
102       }
103     } catch (IOException e) {
104       throw new IllegalStateException(e);
105     }
106     return false;
107   }
108 
109   @SuppressWarnings({"unchecked"})
110   <T> T getOption(AFSocketOption<T> name) throws IOException {
111     Class<T> type = name.type();
112     if (Boolean.class.isAssignableFrom(type)) {
113       return (T) (Object) (NativeUnixSocket.getSocketOption(fd, name.level(), name.optionName(),
114           Integer.class) != 0);
115     } else if (NamedInteger.HasOfValue.class.isAssignableFrom(type)) {
116       @SuppressWarnings("all") // "null" creates another warning
117       int v = NativeUnixSocket.getSocketOption(fd, name.level(), name.optionName(), Integer.class);
118       try {
119         return (T) type.getMethod("ofValue", int.class).invoke(null, v);
120       } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException
121           | NoSuchMethodException | SecurityException e) {
122         throw new IOException("Value casting problem", e);
123       }
124     } else {
125       return NativeUnixSocket.getSocketOption(fd, name.level(), name.optionName(), type);
126     }
127   }
128 
129   <T> void setOption(AFSocketOption<T> name, T value) throws IOException {
130     final Object val;
131     if (value instanceof Boolean) {
132       val = (((Boolean) value) ? 1 : 0);
133     } else if (value instanceof NamedInteger) {
134       val = ((NamedInteger) value).value();
135     } else {
136       val = value;
137     }
138     int level = name.level();
139     int optionName = name.optionName();
140     NativeUnixSocket.setSocketOption(fd, level, optionName, val);
141     if (level == 271 && optionName == 135) {
142       // AFTIPCSocketOptions.TIPC_GROUP_JOIN
143       // unclear why, but sleeping for at least 1ms prevents issues with GROUP_JOIN
144       try {
145         Thread.sleep(1);
146       } catch (InterruptedException e) {
147         // ignore
148       }
149     }
150   }
151 
152   protected void incPendingAccepts() throws SocketException {
153     if (pendingAccepts.incrementAndGet() >= Integer.MAX_VALUE) {
154       pendingAccepts.decrementAndGet();
155       throw new SocketException("Too many pending accepts");
156     }
157   }
158 
159   protected void decPendingAccepts() {
160     pendingAccepts.decrementAndGet();
161   }
162 
163   protected boolean hasPendingAccepts() {
164     return pendingAccepts.get() > 0;
165   }
166 
167   boolean isShutdownOnClose() {
168     return shutdownOnClose.get();
169   }
170 
171   void setShutdownOnClose(boolean enabled) {
172     this.shutdownOnClose.set(enabled);
173   }
174 }