1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.newsclub.net.unix;
19
20 import java.io.DataInputStream;
21 import java.io.DataOutputStream;
22 import java.io.FileDescriptor;
23 import java.io.IOException;
24 import java.net.Socket;
25 import java.net.SocketException;
26 import java.nio.ByteBuffer;
27 import java.nio.ByteOrder;
28 import java.nio.channels.SocketChannel;
29 import java.util.concurrent.atomic.AtomicBoolean;
30
31 import org.eclipse.jdt.annotation.NonNull;
32
33 import com.kohlschutter.annotations.compiletime.SuppressFBWarnings;
34
35
36
37
38
39
40 public final class AFUNIXSocket extends AFSocket<AFUNIXSocketAddress> implements
41 AFUNIXSocketExtensions {
42 private static final Constructor<AFUNIXSocketAddress> CONSTRUCTOR_STRICT =
43 new Constructor<AFUNIXSocketAddress>() {
44
45 @Override
46 public @NonNull AFSocket<AFUNIXSocketAddress> newInstance(FileDescriptor fdObj,
47 AFSocketFactory<AFUNIXSocketAddress> factory) throws SocketException {
48 return new AFUNIXSocket(new AFUNIXSocketImpl(fdObj), factory);
49 }
50 };
51
52 private AFUNIXSocket(AFSocketImpl<AFUNIXSocketAddress> impl,
53 AFSocketFactory<AFUNIXSocketAddress> factory) throws SocketException {
54 super(impl, factory);
55 }
56
57 AFUNIXSocket(FileDescriptor fd, AFSocketFactory<AFUNIXSocketAddress> factory)
58 throws SocketException {
59 this(new AFUNIXSocketImpl.Lenient(fd), factory);
60 }
61
62 @Override
63 protected AFUNIXSocketChannel newChannel() {
64 return new AFUNIXSocketChannel(this);
65 }
66
67
68
69
70
71
72
73
74
75
76
77
78 public static AFUNIXSocket newInstance() throws IOException {
79 return (AFUNIXSocket) AFSocket.newInstance(AFUNIXSocket::new, (AFUNIXSocketFactory) null);
80 }
81
82 static AFUNIXSocket newLenientInstance() throws IOException {
83 return newInstance();
84 }
85
86 static AFUNIXSocket newInstance(FileDescriptor fdObj, int localPort, int remotePort)
87 throws IOException {
88 return (AFUNIXSocket) AFSocket.newInstance(AFUNIXSocket::new, (AFUNIXSocketFactory) null, fdObj,
89 localPort, remotePort);
90 }
91
92 static AFUNIXSocket newInstance(AFUNIXSocketFactory factory) throws SocketException {
93 return (AFUNIXSocket) AFSocket.newInstance(AFUNIXSocket::new, factory);
94 }
95
96
97
98
99
100
101
102
103
104
105 public static AFUNIXSocket newStrictInstance() throws IOException {
106 return (AFUNIXSocket) AFSocket.newInstance(CONSTRUCTOR_STRICT, (AFUNIXSocketFactory) null);
107 }
108
109
110
111
112
113
114
115
116 public static AFUNIXSocket connectTo(AFUNIXSocketAddress addr) throws IOException {
117 return (AFUNIXSocket) AFSocket.connectTo(AFUNIXSocket::new, addr);
118 }
119
120 @Override
121 public AFUNIXSocketChannel getChannel() {
122 return (AFUNIXSocketChannel) super.getChannel();
123 }
124
125 @Override
126 public AFUNIXSocketCredentials getPeerCredentials() throws IOException {
127 if (isClosed() || !isConnected()) {
128 throw new SocketException("Not connected");
129 }
130 return ((AFUNIXSocketImpl) getAFImpl()).getPeerCredentials();
131 }
132
133 @Override
134 public FileDescriptor[] getReceivedFileDescriptors() throws IOException {
135 return ((AFUNIXSocketImpl) getAFImpl()).getReceivedFileDescriptors();
136 }
137
138 @Override
139 public void clearReceivedFileDescriptors() {
140 ((AFUNIXSocketImpl) getAFImpl()).clearReceivedFileDescriptors();
141 }
142
143 @Override
144 public void setOutboundFileDescriptors(FileDescriptor... fdescs) throws IOException {
145 if (fdescs != null && fdescs.length > 0 && !isConnected()) {
146 throw new SocketException("Not connected");
147 }
148 ((AFUNIXSocketImpl) getAFImpl()).setOutboundFileDescriptors(fdescs);
149 }
150
151 @Override
152 public boolean hasOutboundFileDescriptors() {
153 return ((AFUNIXSocketImpl) getAFImpl()).hasOutboundFileDescriptors();
154 }
155
156
157
158
159
160
161
162
163
164
165
166
167
168 @SuppressFBWarnings("HSM_HIDING_METHOD")
169 public static boolean isSupported() {
170 return AFSocket.isSupported() && AFSocket.supports(AFSocketCapability.CAPABILITY_UNIX_DOMAIN);
171 }
172
173
174
175
176
177
178
179
180 public static void main(String[] args) {
181
182
183 System.out.print(AFUNIXSocket.class.getName() + ".isSupported(): ");
184 System.out.flush();
185 System.out.println(AFUNIXSocket.isSupported());
186
187 for (AFSocketCapability cap : AFSocketCapability.values()) {
188 System.out.print(cap + ": ");
189 System.out.flush();
190 System.out.println(AFSocket.supports(cap));
191 }
192 System.out.println();
193 if (AFSocket.supports(AFSocketCapability.CAPABILITY_UNIX_DOMAIN)) {
194 System.out.println("Starting mini selftest...");
195 miniSelftest();
196 } else {
197 System.out.println(
198 "Skipping mini selftest; AFSocketCapability.CAPABILITY_UNIX_DOMAIN is missing");
199 }
200 }
201
202 private static void miniSelftest() {
203 AtomicBoolean success = new AtomicBoolean(true);
204 try {
205 AFUNIXSocketAddress addr = AFUNIXSocketAddress.ofNewTempFile();
206 System.out.println("Using temporary address: " + addr);
207 try (AFUNIXServerSocket server = addr.newBoundServerSocket()) {
208 Thread t = new Thread(() -> {
209 try {
210 try (AFUNIXSocket client = server.accept()) {
211 System.out.println("Server accepted client connection");
212 try (SocketChannel chann = client.getChannel()) {
213 ByteBuffer bb = ByteBuffer.allocate(64).order(ByteOrder.BIG_ENDIAN);
214
215 int numRead = 0;
216 while (bb.position() != 4 && numRead != -1) {
217 numRead = chann.read(bb);
218 }
219 if (bb.position() != 4) {
220 throw new IOException("Unexpected number of bytes read: " + bb.position());
221 }
222 bb.flip();
223 int v;
224 if ((v = bb.getInt()) != 0xABCDEF12) {
225 throw new IOException("Received unexpected data from client: 0x" + Integer
226 .toHexString(v));
227 }
228 bb.clear();
229 bb.putLong(0x00112233456789L);
230 bb.flip();
231 chann.write(bb);
232 }
233 } finally {
234 server.close();
235 }
236 } catch (Exception e) {
237 success.set(false);
238 e.printStackTrace();
239 }
240 });
241 t.start();
242
243 try (AFUNIXSocket socket = addr.newConnectedSocket();
244 DataInputStream in = new DataInputStream(socket.getInputStream());
245 DataOutputStream out = new DataOutputStream(socket.getOutputStream());) {
246 out.writeInt(0xABCDEF12);
247 out.flush();
248 long v = in.readLong();
249 if (v != 0x00112233456789L) {
250 throw new IOException("Received unexpected data from server: 0x" + Long.toHexString(v));
251 }
252 }
253 System.out.println("Data exchange succeeded");
254 }
255 } catch (Exception e) {
256 success.set(false);
257 e.printStackTrace();
258 return;
259 } finally {
260 System.out.println("mini selftest " + (success.get() ? "passed" : "failed"));
261 }
262 }
263 }