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.Closeable;
21  import java.io.FileDescriptor;
22  import java.io.IOException;
23  import java.net.SocketException;
24  import java.nio.ByteBuffer;
25  import java.util.ArrayList;
26  import java.util.Collections;
27  import java.util.HashMap;
28  import java.util.List;
29  import java.util.Map;
30  
31  final class AncillaryDataSupport implements Closeable {
32    private static final ByteBuffer EMPTY_BUFFER = ByteBuffer.allocate(0);
33    private static final FileDescriptor[] NO_FILE_DESCRIPTORS = new FileDescriptor[0];
34  
35    private static final int MIN_ANCBUF_LEN = NativeUnixSocket.isLoaded() ? NativeUnixSocket
36        .ancillaryBufMinLen() : 0;
37  
38    private final Map<FileDescriptor, Integer> openReceivedFileDescriptors = Collections
39        .synchronizedMap(new HashMap<>());
40  
41    private final List<FileDescriptor[]> receivedFileDescriptors = Collections.synchronizedList(
42        new ArrayList<>());
43  
44    // referenced from native code
45    private ByteBuffer ancillaryReceiveBuffer = EMPTY_BUFFER;
46  
47    // referenced from native code
48    int[] pendingFileDescriptors = null;
49  
50    private int[] tipcErrorInfo = null;
51  
52    private int[] tipcDestName = null;
53  
54    // referenced from native code
55    void setTipcErrorInfo(int errorCode, int dataLength) {
56      if (errorCode == 0 && dataLength == 0) {
57        tipcErrorInfo = null;
58      } else {
59        tipcErrorInfo = new int[] {errorCode, dataLength};
60      }
61    }
62  
63    int[] getTIPCErrorInfo() {
64      int[] info = tipcErrorInfo;
65      tipcErrorInfo = null;
66      return info;
67    }
68  
69    void setTipcDestName(int a, int b, int c) {
70      if (a == 0 && b == 0 && c == 0) {
71        this.tipcDestName = null;
72      } else {
73        this.tipcDestName = new int[] {a, b, c};
74      }
75    }
76  
77    int[] getTIPCDestName() {
78      int[] addr = tipcDestName;
79      tipcDestName = null;
80      return addr;
81    }
82  
83    int getAncillaryReceiveBufferSize() {
84      return ancillaryReceiveBuffer.capacity();
85    }
86  
87    void setAncillaryReceiveBufferSize(int size) {
88      if (size == ancillaryReceiveBuffer.capacity()) {
89        return;
90      } else if (size <= 0) {
91        this.ancillaryReceiveBuffer = EMPTY_BUFFER;
92      } else {
93        setAncillaryReceiveBufferSize0(Math.max(256, Math.min(MIN_ANCBUF_LEN, size)));
94      }
95    }
96  
97    void setAncillaryReceiveBufferSize0(int size) {
98      this.ancillaryReceiveBuffer = ByteBuffer.allocateDirect(size);
99    }
100 
101   public void ensureAncillaryReceiveBufferSize(int minSize) {
102     if (minSize <= 0) {
103       return;
104     }
105     if (ancillaryReceiveBuffer.capacity() < minSize) {
106       setAncillaryReceiveBufferSize(minSize);
107     }
108   }
109 
110   // called from native code
111   void receiveFileDescriptors(int[] fds) throws IOException {
112     if (fds == null || fds.length == 0) {
113       return;
114     }
115     final int fdsLength = fds.length;
116     FileDescriptor[] descriptors = new FileDescriptor[fdsLength];
117     for (int i = 0; i < fdsLength; i++) {
118       final FileDescriptor fdesc = new FileDescriptor();
119       NativeUnixSocket.initFD(fdesc, fds[i]);
120       descriptors[i] = fdesc;
121 
122       openReceivedFileDescriptors.put(fdesc, fds[i]);
123 
124       final Closeable cleanup = new Closeable() {
125 
126         @Override
127         public void close() throws IOException {
128           openReceivedFileDescriptors.remove(fdesc);
129         }
130       };
131 
132       try {
133         NativeUnixSocket.attachCloseable(fdesc, cleanup);
134       } catch (SocketException e) {
135         // ignore (cannot attach)
136       }
137     }
138 
139     this.receivedFileDescriptors.add(descriptors);
140   }
141 
142   void clearReceivedFileDescriptors() {
143     receivedFileDescriptors.clear();
144   }
145 
146   FileDescriptor[] getReceivedFileDescriptors() {
147     if (receivedFileDescriptors.isEmpty()) {
148       return NO_FILE_DESCRIPTORS;
149     }
150     List<FileDescriptor[]> copy = new ArrayList<>(receivedFileDescriptors);
151     if (copy.isEmpty()) {
152       return NO_FILE_DESCRIPTORS;
153     }
154     receivedFileDescriptors.removeAll(copy);
155     int count = 0;
156     for (FileDescriptor[] fds : copy) {
157       count += fds.length;
158     }
159     if (count == 0) {
160       return NO_FILE_DESCRIPTORS;
161     }
162     FileDescriptor[] oneArray = new FileDescriptor[count];
163     int offset = 0;
164     for (FileDescriptor[] fds : copy) {
165       System.arraycopy(fds, 0, oneArray, offset, fds.length);
166       offset += fds.length;
167     }
168     return oneArray;
169   }
170 
171   void setOutboundFileDescriptors(int[] fds) {
172     this.pendingFileDescriptors = (fds == null || fds.length == 0) ? null : fds;
173   }
174 
175   boolean hasOutboundFileDescriptors() {
176     return this.pendingFileDescriptors != null;
177   }
178 
179   void setOutboundFileDescriptors(FileDescriptor... fdescs) throws IOException {
180     final int[] fds;
181     if (fdescs == null || fdescs.length == 0) {
182       fds = null;
183     } else {
184       final int numFdescs = fdescs.length;
185       fds = new int[numFdescs];
186       for (int i = 0; i < numFdescs; i++) {
187         FileDescriptor fdesc = fdescs[i];
188         fds[i] = NativeUnixSocket.getFD(fdesc);
189       }
190     }
191     this.setOutboundFileDescriptors(fds);
192   }
193 
194   @Override
195   public void close() {
196     synchronized (openReceivedFileDescriptors) {
197       for (FileDescriptor desc : openReceivedFileDescriptors.keySet()) {
198         if (desc.valid()) {
199           try {
200             NativeUnixSocket.close(desc);
201           } catch (Exception e) {
202             // ignore
203           }
204         }
205       }
206     }
207   }
208 }