1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.newsclub.net.unix.vsock;
19
20 import java.io.IOException;
21 import java.io.InputStream;
22 import java.io.OutputStream;
23 import java.net.SocketException;
24 import java.nio.charset.StandardCharsets;
25 import java.util.regex.Matcher;
26 import java.util.regex.Pattern;
27
28 import org.newsclub.net.unix.AFSocket;
29 import org.newsclub.net.unix.AFSocketAddress;
30 import org.newsclub.net.unix.AFSocketConnector;
31 import org.newsclub.net.unix.AFUNIXSocket;
32 import org.newsclub.net.unix.AFUNIXSocketAddress;
33 import org.newsclub.net.unix.AFVSOCKSocketAddress;
34 import org.newsclub.net.unix.AddressUnavailableSocketException;
35
36
37
38
39
40
41
42
43
44 public final class AFVSOCKProxySocketConnector implements
45 AFSocketConnector<AFVSOCKSocketAddress, AFSocketAddress> {
46 private static final AFSocketConnector<AFVSOCKSocketAddress, AFSocketAddress> DIRECT_CONNECTOR =
47 new AFSocketConnector<AFVSOCKSocketAddress, AFSocketAddress>() {
48
49 @Override
50 public AFSocket<? extends AFSocketAddress> connect(AFVSOCKSocketAddress addr)
51 throws IOException {
52 return addr.newConnectedSocket();
53 }
54 };
55
56 private static final Pattern PAT_OK = Pattern.compile("OK ([0-9]+)");
57 private static final byte[] OK = {'O', 'K', ' '};
58 private final AFUNIXSocketAddress connectorAddress;
59 private final int allowedCID;
60
61 private AFVSOCKProxySocketConnector(AFUNIXSocketAddress connectorAddress, int allowedCID) {
62 this.connectorAddress = connectorAddress;
63 this.allowedCID = allowedCID;
64 }
65
66
67
68
69
70
71
72
73
74
75
76 public static AFSocketConnector<AFVSOCKSocketAddress, AFSocketAddress> openFirecrackerStyleConnector(
77 AFUNIXSocketAddress connectorAddress, int allowedCID) {
78 return new AFVSOCKProxySocketConnector(connectorAddress, allowedCID);
79 }
80
81
82
83
84
85
86 public static AFSocketConnector<AFVSOCKSocketAddress, AFSocketAddress> openDirectConnector() {
87 return DIRECT_CONNECTOR;
88 }
89
90
91
92
93
94
95
96
97
98 @Override
99 @SuppressWarnings("Finally" )
100 public AFSocket<?> connect(AFVSOCKSocketAddress vsockAddress) throws IOException {
101 int cid = vsockAddress.getVSOCKCID();
102 if (cid != allowedCID && cid != AFVSOCKSocketAddress.VMADDR_CID_ANY
103 && allowedCID != AFVSOCKSocketAddress.VMADDR_CID_ANY) {
104 throw new AddressUnavailableSocketException("Connector does not cover CID " + cid);
105 }
106
107 @SuppressWarnings("resource")
108 AFUNIXSocket sock = connectorAddress.newConnectedSocket();
109 InputStream in = sock.getInputStream();
110 OutputStream out = sock.getOutputStream();
111
112 boolean success = false;
113
114 try {
115 String connectLine = "CONNECT " + vsockAddress.getVSOCKPort() + "\n";
116 out.write(connectLine.getBytes(StandardCharsets.ISO_8859_1));
117
118 byte[] buf = new byte[16];
119 int b;
120 int i = 0;
121 while ((b = in.read()) != -1 && b != '\n' && i < buf.length) {
122 buf[i] = (byte) b;
123 if (i < 3) {
124 if (OK[i] != b) {
125 break;
126 }
127 }
128 i++;
129 }
130 if (b == '\n' && i > 3) {
131 Matcher m = PAT_OK.matcher(new String(buf, 0, i, StandardCharsets.ISO_8859_1));
132 if (m.matches()) {
133 Integer.parseInt(m.group(1));
134 success = true;
135 }
136 }
137 } finally {
138 if (!success) {
139 sock.close();
140 throw new SocketException("Unexpected response from proxy socket");
141 }
142 }
143
144 return sock;
145 }
146 }