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