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 }