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