💾 Archived View for gemini.rmf-dev.com › repo › Vaati › rxproxy › files › 9121f7f2b2bfb023cef115b243… captured on 2023-09-08 at 16:20:32. Gemini links have been rewritten to link to archived content
⬅️ Previous capture (2023-03-20)
-=-=-=-=-=-=-
0 /* See LICENSE file for copyright and license details. */
1 module client;
2 import std.datetime.systime : SysTime, Clock;
3 import std.algorithm.mutation;
4 import std.socket;
5 import core.stdc.string;
6 import logger;
7 import std.format;
8 import core.time;
9 import proxy;
10
11 /// Connection state of the client
12 enum State { CONNECTED, GREETED, AUTHENTICATED, ESTABLISHED, FAILED }
13
14 /// Converting nano seconds to mseconds
15 const long NANOTOMS = 1_000_000_000;
16
17 /// Client of the proxy server
18 class Client {
19
20 private:
21 State state;
22 long connectionTime;
23 Proxy server;
24 ubyte[1024] confirmData;
25 int confirmDataLen;
26 string rDomain;
27 int rPort;
28 public:
29 /// Socket to the client
30 Socket socket;
31 /// Socket to the remote server
32 Socket remote;
33
34 /// Assign sockets to the server
35 this(Socket socket, Proxy proxy) {
36 this.server=proxy;
37 this.socket=socket;
38 this.remote=null;
39 connectionTime=Clock.currStdTime()/NANOTOMS;
40 state = State.CONNECTED;
41 }
42
43 ~this() {
44 if(socket !is null) {
45 socket.close();
46 socket.shutdown(SocketShutdown.BOTH);
47 }
48 if (remote !is null && remote.isAlive) {
49 try
50 {
51 Logger.error(format("Connection from %s:%d closed.", rDomain,rPort));
52 }
53 catch (SocketException)
54 {
55 Logger.error(format("Connection closed."));
56 }
57 remote.shutdown(SocketShutdown.BOTH);
58 remote.close();
59 } else {
60 Logger.error(format("Connection closed. No connection"));
61 }
62 }
63
64 /// Return true if the remote connection is established
65 bool isEstablished() {
66 return state == State.ESTABLISHED;
67 }
68
69 /// Confirm to the client that the remote connectio was established
70 void confirm() {
71 if(state == State.GREETED) {
72 sendRes(0x00,confirmData,confirmDataLen);
73 Logger.info(format("Connected to "~rDomain~":%d",rPort));
74 state = State.ESTABLISHED;
75 }
76 }
77
78 /// Return true if the client is connected to the remote address
79 bool isConnecting() {
80 if(state == State.GREETED) {
81 if(remote is null || !remote.isAlive) {
82 Logger.error("Failed to connect to that");
83 sendRes(0x05);
84 state = State.FAILED;
85 }
86 }
87 return state == State.GREETED;
88 }
89
90 /// Return true if the client should disconnect
91 bool shouldDisconnect() {
92 switch(state) {
93 case State.FAILED:
94 return true;
95 case State.ESTABLISHED:
96 if(server.getTimeout(true)<0) return false;
97 return connectionTime+server.getTimeout(true)<Clock.currStdTime()/NANOTOMS;
98 default:
99 if(server.getTimeout(false)<0) return false;
100 return connectionTime+server.getTimeout(false)<Clock.currStdTime()/NANOTOMS;
101 }
102 }
103
104 /// Send Auth data to the client
105 void sendAuth(ubyte method) {
106 auto data = new char[2];
107 data[0]=0x05;
108 data[1]=method;
109 socket.send(data[0..2]);
110 }
111
112 /// Send packet to the client
113 void sendRes(ubyte status, ubyte[] data=null, int len=0) {
114 auto res = new char[3+len];
115 res[0]=0x05;
116 res[1]=status;
117 res[2]=0x00;
118 if(len!=0)
119 memcpy(cast(void*)&res[3],cast(void*)data,len);
120 socket.send(res[0..3+len]);
121 }
122
123 /// Return true if the address is martian
124 bool isMartian(uint addr) {
125 if((3232235520<=addr&&addr<=3232301055)||
126 (167772160<=addr&&addr<=184549375)||
127 (0<=addr&&addr<=16777215)||
128 (2851995648<=addr&&addr<=2852061183)||
129 (3221225472<=addr&&addr<=3221225727)||
130 (3221225984<=addr&&addr<=3221226239)||
131 (3325256704<=addr&&addr<=3325256959)||
132 (3405803776<=addr&&addr<=3405804031)||
133 (3323068416<=addr&&addr<=3323199487)||
134 (2886729728<=addr&&addr<=2887778303)||
135 (3758096384<=addr&&addr<=4294967295)
136 ) return true;
137 return false;
138 }
139
140 /// Send data to the client
141 bool feed(ubyte[] buf, long len) {
142 if(state==State.CONNECTED) {
143 if(buf[0]!=0x05||len<buf[1]+1) return false;
144 ubyte auth=0xFF;
145 for(ubyte b=0; b<buf[1]; b++)
146 if(buf[b+2]==0) {
147 auth=0;
148 break;
149 }
150 if(auth==0xFF) {
151 sendAuth(0xFF);
152 return false;
153 }
154 switch(auth) {
155 case 0x00:
156 sendAuth(0);
157 state=State.AUTHENTICATED;
158 connectionTime=Clock.currStdTime()/NANOTOMS;
159 break;
160 case 0x02:
161 // TODO: Username/password Authentication
162 default:
163 sendAuth(0xFF);
164 return false;
165 }
166 } else if(state==State.AUTHENTICATED) {
167 if(buf[0]!=0x05||len<5) return false;
168 if(buf[1]==0x01) {
169 switch(buf[3]) {
170 case 0x01:
171 if(len<10) return false;
172 remote = new TcpSocket();
173 remote.blocking=false;
174 uint ip = *cast(uint*)reverse(cast(byte[])buf[4..8]);
175 ushort port = *cast(ushort*)reverse(cast(byte[])buf[8..10]);
176 InternetAddress addr = new InternetAddress(ip,port);
177 rPort=port;
178 if(server.isPortBlacklisted(rPort)) {
179 Logger.error(format("Port %d is blacklisted",rPort));
180 sendRes(0x08);
181 return false;
182 }
183 rDomain=addr.toAddrString();
184 if(server.isBlacklisted(addr.toAddrString())) {
185 Logger.error(format("%s is blacklisted",rDomain));
186 sendRes(0x08);
187 return false;
188 }
189 if(isMartian(addr.addr)) {
190 Logger.error(format("%s is martian",rDomain));
191 sendRes(0x08);
192 return false;
193 }
194 try {
195 remote.connect(addr);
196 state = State.GREETED;
197 confirmDataLen = 7;
198 memcpy(cast(void*)confirmData,cast(void*)buf[3..10],confirmDataLen);
199 } catch(SocketException) {
200 Logger.error(format("Failed to connect to %s:%d",rDomain,rPort));
201 }
202 break;
203 case 0x03:
204 ubyte dlen = buf[4];
205 if(len<dlen+7) return false;
206 string domain = cast(string)(buf[5..5+dlen]).dup;
207 if(server.isBlacklisted(domain)) {
208 Logger.error(format("%s is blacklisted",domain));
209 sendRes(0x08);
210 return false;
211 }
212 ushort port = *cast(ushort*)reverse(cast(byte[])buf[5+dlen..7+dlen]);
213 rPort=port;
214 if(server.isPortBlacklisted(rPort)) {
215 Logger.error(format("Port %d is blacklisted",rPort));
216 sendRes(0x08);
217 return false;
218 }
219 InternetAddress addr = new InternetAddress(domain,port);
220 if(isMartian(addr.addr)) {
221 Logger.error(format("%s is martian",domain));
222 sendRes(0x08);
223 return false;
224 }
225 rDomain=domain;
226 try {
227 remote = new TcpSocket();
228 remote.blocking=false;
229 remote.connect(addr);
230 state = State.GREETED;
231 confirmDataLen = dlen+4;
232 memcpy(cast(void*)confirmData,cast(void*)buf[3..7+dlen],dlen+4);
233 } catch(SocketException) {
234 Logger.error(format("Failed to connect to %s:%d",domain,port));
235 }
236 break;
237 default:
238 sendRes(0x08);
239 return false;
240 }
241
242 } else if(state==State.GREETED) {
243 if(remote.isAlive) {
244 state=State.ESTABLISHED;
245 } else {
246 Logger.error("Failed to connect to that");
247 sendRes(0x05);
248 return false;
249 }
250 } else {
251 sendRes(0x02);
252 return false;
253 }
254 } else if(state==State.ESTABLISHED) {
255 connectionTime=Clock.currStdTime()/NANOTOMS;
256 remote.send(buf[0..len]);
257 }
258 return true;
259 }
260
261 }
262