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 static java.util.Objects.requireNonNull;
21  
22  import java.io.FileDescriptor;
23  import java.io.IOException;
24  import java.net.DatagramSocket;
25  import java.net.InetAddress;
26  import java.net.NetworkInterface;
27  import java.net.ProtocolFamily;
28  import java.net.Socket;
29  import java.net.SocketAddress;
30  import java.net.SocketOption;
31  import java.net.StandardProtocolFamily;
32  import java.nio.ByteBuffer;
33  import java.nio.channels.DatagramChannel;
34  import java.nio.channels.MembershipKey;
35  import java.nio.channels.spi.SelectorProvider;
36  import java.util.Set;
37  
38  import org.eclipse.jdt.annotation.Nullable;
39  
40  import com.kohlschutter.annotations.compiletime.SuppressFBWarnings;
41  
42  /**
43   * A {@link DatagramChannel} implementation that works with junixsocket.
44   *
45   * @author Christian Kohlschütter
46   * @param <A> The supported address type.
47   */
48  public abstract class AFDatagramChannel<A extends AFSocketAddress> extends DatagramChannel
49      implements AFSomeSocket, AFSocketExtensions, AFSomeSocketChannel {
50    private final AFDatagramSocket<A> afSocket;
51  
52    /**
53     * Creates a new {@link AFDatagramChannel} instance.
54     *
55     * @param selectorProvider The corresponding {@link SelectorProvider}.
56     * @param socket The corresponding {@link Socket}.
57     */
58    protected AFDatagramChannel(AFSelectorProvider<A> selectorProvider, AFDatagramSocket<A> socket) {
59      super(selectorProvider);
60      this.afSocket = socket;
61    }
62  
63    /**
64     * Returns the corresponding {@link Socket}.
65     *
66     * @return The socket.
67     */
68    protected final AFDatagramSocket<A> getAFSocket() {
69      return afSocket;
70    }
71  
72    // CPD-OFF
73  
74    @Override
75    public final MembershipKey join(InetAddress group, NetworkInterface interf) throws IOException {
76      throw new UnsupportedOperationException();
77    }
78  
79    @Override
80    public final MembershipKey join(InetAddress group, NetworkInterface interf, InetAddress source)
81        throws IOException {
82      throw new UnsupportedOperationException();
83    }
84  
85    @Override
86    public final AFDatagramChannel<A> bind(SocketAddress local) throws IOException {
87      afSocket.bind(local);
88      return this;
89    }
90  
91    @SuppressFBWarnings("EI_EXPOSE_REP")
92    @Override
93    public final AFDatagramSocket<A> socket() {
94      return afSocket;
95    }
96  
97    /**
98     * Returns the binding state of the socket.
99     *
100    * @return true if the socket successfully bound to an address
101    */
102   public final boolean isBound() {
103     return afSocket.isBound();
104   }
105 
106   @Override
107   public final boolean isConnected() {
108     return afSocket.isConnected();
109   }
110 
111   @Override
112   public final AFDatagramChannel<A> connect(SocketAddress remote) throws IOException {
113     afSocket.connect(remote);
114     return this;
115   }
116 
117   @Override
118   public final AFDatagramChannel<A> disconnect() throws IOException {
119     afSocket.disconnect();
120     return this;
121   }
122 
123   @Override
124   public final @Nullable A getRemoteAddress() throws IOException {
125     return getRemoteSocketAddress();
126   }
127 
128   @Override
129   public final @Nullable A getRemoteSocketAddress() {
130     return afSocket.getRemoteSocketAddress();
131   }
132 
133   @Override
134   public final @Nullable A getLocalAddress() throws IOException {
135     return getLocalSocketAddress();
136   }
137 
138   @Override
139   public final @Nullable A getLocalSocketAddress() {
140     return afSocket.getLocalSocketAddress();
141   }
142 
143   @Override
144   public final A receive(ByteBuffer dst) throws IOException {
145     boolean complete = false;
146     Exception exception = null;
147     try {
148       begin();
149       A ret = afSocket.getAFImpl().receive(dst);
150       complete = true;
151       return ret;
152     } catch (IOException e) {
153       throw InterruptibleChannelUtil.ioExceptionOrThrowRuntimeException( // NOPMD.PreserveStackTrace
154           (exception = InterruptibleChannelUtil.handleException(this, e)));
155     } finally {
156       InterruptibleChannelUtil.endInterruptable(this, this::end, complete, exception);
157     }
158   }
159 
160   @Override
161   public final int send(ByteBuffer src, SocketAddress target) throws IOException {
162     boolean complete = false;
163     Exception exception = null;
164     try {
165       begin();
166       int ret = afSocket.getAFImpl().send(src, target);
167       complete = true;
168       return ret;
169     } catch (IOException e) {
170       throw InterruptibleChannelUtil.ioExceptionOrThrowRuntimeException( // NOPMD.PreserveStackTrace
171           (exception = InterruptibleChannelUtil.handleException(this, e)));
172     } finally {
173       InterruptibleChannelUtil.endInterruptable(this, this::end, complete, exception);
174     }
175   }
176 
177   @Override
178   public final int read(ByteBuffer dst) throws IOException {
179     boolean complete = false;
180     Exception exception = null;
181     try {
182       begin();
183       int ret = afSocket.getAFImpl().read(dst, null);
184       complete = true;
185       return ret;
186     } catch (IOException e) {
187       throw InterruptibleChannelUtil.ioExceptionOrThrowRuntimeException( // NOPMD.PreserveStackTrace
188           (exception = InterruptibleChannelUtil.handleException(this, e)));
189     } finally {
190       InterruptibleChannelUtil.endInterruptable(this, this::end, complete, exception);
191     }
192   }
193 
194   @Override
195   public final long read(ByteBuffer[] dsts, int offset, int length) throws IOException {
196     if (length == 0) {
197       return 0;
198     }
199     // FIXME support more than one buffer for scatter-gather access
200     return read(dsts[offset]);
201   }
202 
203   @Override
204   public final int write(ByteBuffer src) throws IOException {
205     boolean complete = false;
206     Exception exception = null;
207     try {
208       begin();
209       int ret = afSocket.getAFImpl().write(src);
210       complete = true;
211       return ret;
212     } catch (IOException e) {
213       throw InterruptibleChannelUtil.ioExceptionOrThrowRuntimeException( // NOPMD.PreserveStackTrace
214           (exception = InterruptibleChannelUtil.handleException(this, e)));
215     } finally {
216       InterruptibleChannelUtil.endInterruptable(this, this::end, complete, exception);
217     }
218   }
219 
220   @Override
221   public final long write(ByteBuffer[] srcs, int offset, int length) throws IOException {
222     if (length == 0) {
223       return 0;
224     }
225     // FIXME support more than one buffer for scatter-gather access
226     return write(srcs[offset]);
227   }
228 
229   @Override
230   protected final void implCloseSelectableChannel() throws IOException {
231     getAFSocket().close();
232   }
233 
234   @Override
235   protected final void implConfigureBlocking(boolean block) throws IOException {
236     getAFCore().implConfigureBlocking(block);
237   }
238 
239   @Override
240   public final int getAncillaryReceiveBufferSize() {
241     return afSocket.getAncillaryReceiveBufferSize();
242   }
243 
244   @Override
245   public final void setAncillaryReceiveBufferSize(int size) {
246     afSocket.setAncillaryReceiveBufferSize(size);
247   }
248 
249   @Override
250   public final void ensureAncillaryReceiveBufferSize(int minSize) {
251     afSocket.ensureAncillaryReceiveBufferSize(minSize);
252   }
253 
254   @Override
255   public final <T> AFDatagramChannel<A> setOption(SocketOption<T> name, T value)
256       throws IOException {
257     if (name instanceof AFSocketOption<?>) {
258       getAFCore().setOption((AFSocketOption<T>) name, value);
259       return this;
260     }
261     Integer optionId = SocketOptionsMapper.resolve(name);
262     if (optionId == null) {
263       throw new UnsupportedOperationException("unsupported option");
264     } else {
265       afSocket.getAFImpl().setOption(optionId, value);
266     }
267     return this;
268   }
269 
270   @SuppressWarnings("unchecked")
271   @Override
272   public final <T> T getOption(SocketOption<T> name) throws IOException {
273     if (name instanceof AFSocketOption<?>) {
274       return getAFCore().getOption((AFSocketOption<T>) name);
275     }
276     Integer optionId = SocketOptionsMapper.resolve(name);
277     if (optionId == null) {
278       throw new UnsupportedOperationException("unsupported option");
279     } else {
280       return (T) afSocket.getAFImpl().getOption(optionId);
281     }
282   }
283 
284   @Override
285   public final Set<SocketOption<?>> supportedOptions() {
286     return SocketOptionsMapper.SUPPORTED_SOCKET_OPTIONS;
287   }
288 
289   final AFSocketCore getAFCore() {
290     return afSocket.getAFImpl().getCore();
291   }
292 
293   @Override
294   public final FileDescriptor getFileDescriptor() throws IOException {
295     return afSocket.getFileDescriptor();
296   }
297 
298   /**
299    * Checks if this {@link DatagramSocket}'s bound filename should be removed upon {@link #close()}.
300    *
301    * Deletion is not guaranteed, especially when not supported (e.g., addresses in the abstract
302    * namespace).
303    *
304    * @return {@code true} if an attempt is made to delete the socket file upon {@link #close()}.
305    */
306   public final boolean isDeleteOnClose() {
307     return afSocket.isDeleteOnClose();
308   }
309 
310   /**
311    * Enables/disables deleting this {@link DatagramSocket}'s bound filename upon {@link #close()}.
312    *
313    * Deletion is not guaranteed, especially when not supported (e.g., addresses in the abstract
314    * namespace).
315    *
316    * @param b Enabled if {@code true}.
317    */
318   public final void setDeleteOnClose(boolean b) {
319     afSocket.setDeleteOnClose(b);
320   }
321 
322   @Override
323   public void setShutdownOnClose(boolean enabled) {
324     getAFCore().setShutdownOnClose(enabled);
325   }
326 
327   /**
328    * Opens a datagram channel. The {@code family} parameter specifies the {@link ProtocolFamily
329    * protocol family} of the channel's socket.
330    * <p>
331    * If the {@link ProtocolFamily} is of an {@link AFProtocolFamily}, or {@code UNIX}, the
332    * corresponding junixsocket implementation is used. In all other cases, the call is delegated to
333    * {@link DatagramChannel#open()}.
334    *
335    * @param family The protocol family.
336    * @return The new {@link DatagramChannel}.
337    * @throws IOException on error.
338    */
339   @SuppressFBWarnings("HSM_HIDING_METHOD")
340   public static DatagramChannel open(ProtocolFamily family) throws IOException {
341     requireNonNull(family);
342 
343     if (family instanceof AFProtocolFamily) {
344       return ((AFProtocolFamily) family).openDatagramChannel();
345     } else if ("UNIX".equals(family.name())) {
346       return AFUNIXDatagramChannel.open();
347     } else if (family instanceof StandardProtocolFamily) {
348       return DatagramChannel.open();
349     } else {
350       throw new UnsupportedOperationException("Protocol family not supported");
351     }
352   }
353 }