1 #include "Proxy.h"
2
3 // TODO: Mayaqua should not depend on Cedar.
4 #include "Cedar/WinUi.h"
5
6 #include "DNS.h"
7 #include "Memory.h"
8 #include "Str.h"
9
Internal_ProxyTcpConnect(PROXY_PARAM_IN * param,volatile bool * cancel_flag,IP * resolved_ip)10 SOCK *Internal_ProxyTcpConnect(PROXY_PARAM_IN *param, volatile bool *cancel_flag, IP *resolved_ip)
11 {
12 #ifdef OS_WIN32
13 if (param->Hwnd != NULL)
14 {
15 return WinConnectEx3((HWND)param->Hwnd, param->Hostname, param->Port, param->Timeout, 0, NULL, NULL, NULL, NULL, false);
16 }
17 #endif
18
19 return ConnectEx4(param->Hostname, param->Port, param->Timeout, (bool *)cancel_flag, NULL, NULL, false, true, resolved_ip);
20 }
21
22 // Connect to an HTTP proxy
ProxyHttpConnect(PROXY_PARAM_OUT * out,PROXY_PARAM_IN * in,volatile bool * cancel_flag)23 UINT ProxyHttpConnect(PROXY_PARAM_OUT *out, PROXY_PARAM_IN *in, volatile bool *cancel_flag)
24 {
25 bool dummy_cancel_flag = false, use_auth = false;
26 char target_hostname[MAX_HOST_NAME_LEN + 1];
27 char target_hostname_port[MAX_SIZE];
28 HTTP_HEADER *h;
29 UINT i, ret;
30 SOCK *s;
31 // Validate arguments
32 if (out == NULL || in == NULL || in->Port == 0 || in->TargetPort == 0 || IsEmptyStr(in->Hostname) || IsEmptyStr(in->TargetHostname))
33 {
34 return PROXY_ERROR_PARAMETER;
35 }
36
37 if (cancel_flag == NULL)
38 {
39 cancel_flag = &dummy_cancel_flag;
40 }
41 else if (*cancel_flag)
42 {
43 return PROXY_ERROR_CANCELED;
44 }
45
46 Zero(out, sizeof(PROXY_PARAM_OUT));
47
48 // Open TCP connection to the proxy server
49 s = Internal_ProxyTcpConnect(in, cancel_flag, &out->ResolvedIp);
50 if (s == NULL)
51 {
52 return PROXY_ERROR_CONNECTION;
53 }
54
55 SetTimeout(s, MIN(PROXY_CONNECTION_TIMEOUT, (in->Timeout == 0 ? INFINITE : in->Timeout)));
56
57 if ((IsEmptyStr(in->Username) || IsEmptyStr(in->Password)) == false)
58 {
59 use_auth = true;
60 }
61
62 Zero(target_hostname, sizeof(target_hostname));
63 StrCpy(target_hostname, sizeof(target_hostname), in->TargetHostname);
64
65 for (i = 0; i < StrLen(target_hostname); ++i)
66 {
67 if (target_hostname[i] == '/')
68 {
69 target_hostname[i] = 0;
70 }
71 }
72
73 // Generate HTTP header
74 if (IsStrIPv6Address(target_hostname))
75 {
76 IP ip;
77 char iptmp[MAX_PATH];
78
79 StrToIP(&ip, target_hostname);
80 IPToStr(iptmp, sizeof(iptmp), &ip);
81
82 Format(target_hostname_port, sizeof(target_hostname_port), "[%s]:%hu", iptmp, in->TargetPort);
83 }
84 else
85 {
86 Format(target_hostname_port, sizeof(target_hostname_port), "%s:%hu", target_hostname, in->TargetPort);
87 }
88
89 h = NewHttpHeader("CONNECT", target_hostname_port, "HTTP/1.0");
90
91 if (IsEmptyStr(in->HttpCustomHeader) == false)
92 {
93 TOKEN_LIST *tokens = ParseToken(in->HttpCustomHeader, "\r\n");
94 if (tokens != NULL)
95 {
96 for (i = 0; i < tokens->NumTokens; i++)
97 {
98 AddHttpValueStr(h, tokens->Token[i]);
99 }
100
101 FreeToken(tokens);
102 }
103 }
104
105 if (GetHttpValue(h, "User-Agent") == NULL)
106 {
107 AddHttpValue(h, NewHttpValue("User-Agent", IsEmptyStr(in->HttpUserAgent) ? DEFAULT_USER_AGENT : in->HttpUserAgent));
108 }
109
110 if (GetHttpValue(h, "Host") == NULL)
111 {
112 AddHttpValue(h, NewHttpValue("Host", target_hostname));
113 }
114
115 if (GetHttpValue(h, "Content-Length") == NULL)
116 {
117 AddHttpValue(h, NewHttpValue("Content-Length", "0"));
118 }
119
120 if (GetHttpValue(h, "Proxy-Connection") == NULL)
121 {
122 AddHttpValue(h, NewHttpValue("Proxy-Connection", "Keep-Alive"));
123 }
124
125 if (GetHttpValue(h, "Pragma") == NULL)
126 {
127 AddHttpValue(h, NewHttpValue("Pragma", "no-cache"));
128 }
129
130 if (use_auth && GetHttpValue(h, "Proxy-Authorization") == NULL)
131 {
132 char auth_str[MAX_SIZE * 2], auth_b64_str[MAX_SIZE * 2];
133
134 // Generate the authentication string
135 Format(auth_str, sizeof(auth_str), "%s:%s", in->Username, in->Password);
136
137 // Base64 encode
138 Zero(auth_b64_str, sizeof(auth_b64_str));
139 Encode64(auth_b64_str, auth_str);
140
141 // Generate final string
142 Format(auth_str, sizeof(auth_str), "Basic %s", auth_b64_str);
143
144 AddHttpValue(h, NewHttpValue("Proxy-Authorization", auth_str));
145 }
146
147 // Transmission
148 ret = SendHttpHeader(s, h);
149
150 FreeHttpHeader(h);
151
152 if (ret == false)
153 {
154 ret = PROXY_ERROR_DISCONNECTED;
155 goto FAILURE;
156 }
157
158 if (*cancel_flag)
159 {
160 ret = PROXY_ERROR_CANCELED;
161 goto FAILURE;
162 }
163
164 // Receive the results
165 h = RecvHttpHeader(s);
166 if (h == NULL)
167 {
168 FreeHttpHeader(h);
169 ret = PROXY_ERROR_GENERIC;
170 goto FAILURE;
171 }
172
173 ret = 0;
174 if (StrLen(h->Method) == 8)
175 {
176 if (Cmp(h->Method, "HTTP/1.", 7) == 0)
177 {
178 ret = ToInt(h->Target);
179 }
180 }
181 FreeHttpHeader(h);
182
183 // Check the code
184 switch (ret)
185 {
186 case 401:
187 case 403:
188 case 407:
189 // Authentication failure
190 ret = PROXY_ERROR_AUTHENTICATION;
191 goto FAILURE;
192
193 default:
194 if ((ret / 100) == 2)
195 {
196 // Success
197 SetTimeout(s, INFINITE);
198 out->Sock = s;
199 return PROXY_ERROR_SUCCESS;
200 }
201 else
202 {
203 // Unknown result
204 ret = PROXY_ERROR_GENERIC;
205 }
206 }
207
208 FAILURE:
209 Disconnect(s);
210 ReleaseSock(s);
211 return ret;
212 }
213
214 // Connect to a SOCKS5 proxy (RFC1928, RFC1929 defines username/password authentication)
ProxySocks5Connect(PROXY_PARAM_OUT * out,PROXY_PARAM_IN * in,volatile bool * cancel_flag)215 UINT ProxySocks5Connect(PROXY_PARAM_OUT *out, PROXY_PARAM_IN *in, volatile bool *cancel_flag)
216 {
217 bool dummy_cancel_flag = false;
218 UCHAR tmp, recv_buf[2], *recv_buf_final;
219 USHORT target_port;
220 IP target_ip;
221 UINT ret;
222 SOCK *s;
223 BUF *b;
224 // Validate arguments
225 if (out == NULL || in == NULL || in->Port == 0 || in->TargetPort == 0 || IsEmptyStr(in->Hostname) || IsEmptyStr(in->TargetHostname))
226 {
227 return PROXY_ERROR_PARAMETER;
228 }
229
230 if (cancel_flag == NULL)
231 {
232 cancel_flag = &dummy_cancel_flag;
233 }
234 else if (*cancel_flag)
235 {
236 return PROXY_ERROR_CANCELED;
237 }
238
239 Zero(out, sizeof(PROXY_PARAM_OUT));
240
241 // Open TCP connection to the proxy server
242 s = Internal_ProxyTcpConnect(in, cancel_flag, &out->ResolvedIp);
243 if (s == NULL)
244 {
245 return PROXY_ERROR_CONNECTION;
246 }
247
248 SetTimeout(s, MIN(PROXY_CONNECTION_TIMEOUT, (in->Timeout == 0 ? INFINITE : in->Timeout)));
249
250 // +----+----------+----------+
251 // |VER | NMETHODS | METHODS |
252 // +----+----------+----------+
253 // | 1 | 1 | 1 to 255 |
254 // +----+----------+----------+
255 //
256 // X'00' NO AUTHENTICATION REQUIRED
257 // X'01' GSSAPI
258 // X'02' USERNAME/PASSWORD
259 // X'03' to X'7F' IANA ASSIGNED
260 // X'80' to X'FE' RESERVED FOR PRIVATE METHODS
261 // X'FF' NO ACCEPTABLE METHOD
262
263 b = NewBuf();
264 tmp = 5;
265 WriteBuf(b, &tmp, sizeof(tmp)); // SOCKS version
266 tmp = 2;
267 WriteBuf(b, &tmp, sizeof(tmp)); // Number of supported methods
268 tmp = 0;
269 WriteBuf(b, &tmp, sizeof(tmp)); // No authentication
270 tmp = 2;
271 WriteBuf(b, &tmp, sizeof(tmp)); // Username/password
272
273 ret = SendAll(s, b->Buf, b->Size, false);
274
275 if (ret == false)
276 {
277 FreeBuf(b);
278 Debug("ProxySocks5Connect(): [Phase 1] Failed to send initial data to the server.\n");
279 ret = PROXY_ERROR_DISCONNECTED;
280 goto FAILURE;
281 }
282
283 // +----+--------+
284 // |VER | METHOD |
285 // +----+--------+
286 // | 1 | 1 |
287 // +----+--------+
288
289 if (RecvAll(s, recv_buf, sizeof(recv_buf), false) == false)
290 {
291 FreeBuf(b);
292 Debug("ProxySocks5Connect(): [Phase 1] Failed to receive initial data response from the server.\n");
293 ret = PROXY_ERROR_DISCONNECTED;
294 goto FAILURE;
295 }
296
297 if (recv_buf[0] != 5)
298 {
299 FreeBuf(b);
300 Debug("ProxySocks5Connect(): [Phase 1] Unmatching version: %u.\n", recv_buf[0]);
301 ret = PROXY_ERROR_VERSION;
302 goto FAILURE;
303 }
304
305 ClearBuf(b);
306
307 // Username/password authentication (RFC1929)
308 if (recv_buf[1] == 2)
309 {
310 // +----+------+----------+------+----------+
311 // |VER | ULEN | UNAME | PLEN | PASSWD |
312 // +----+------+----------+------+----------+
313 // | 1 | 1 | 1 to 255 | 1 | 1 to 255 |
314 // +----+------+----------+------+----------+
315
316 tmp = 1;
317 WriteBuf(b, &tmp, sizeof(tmp)); // Authentication protocol version
318 tmp = StrLen(in->Username);
319 WriteBuf(b, &tmp, sizeof(tmp)); // Username length
320 WriteBuf(b, in->Username, tmp); // Username
321 tmp = StrLen(in->Password);
322 WriteBuf(b, &tmp, sizeof(tmp)); // Password length
323 WriteBuf(b, in->Password, tmp); // Password
324
325 ret = SendAll(s, b->Buf, b->Size, false);
326
327 ClearBuf(b);
328
329 if (ret == false)
330 {
331 Debug("ProxySocks5Connect(): [Phase 1] Failed to send authentication data to the server.\n");
332 ret = PROXY_ERROR_DISCONNECTED;
333 goto FAILURE;
334 }
335
336 // +----+--------+
337 // |VER | STATUS |
338 // +----+--------+
339 // | 1 | 1 |
340 // +----+--------+
341
342 if (RecvAll(s, recv_buf, sizeof(recv_buf), false) == false)
343 {
344 Debug("ProxySocks5Connect(): [Phase 1] Failed to receive authentication data response from the server.\n");
345 ret = PROXY_ERROR_DISCONNECTED;
346 goto FAILURE;
347 }
348
349 if (recv_buf[1] != 0)
350 {
351 Debug("ProxySocks5Connect(): [Phase 1] Authentication failure, error code sent by the server: %u.\n", recv_buf[1]);
352 ret = PROXY_ERROR_AUTHENTICATION;
353 goto FAILURE;
354 }
355 }
356
357 // +----+-----+-------+------+----------+----------+
358 // |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT |
359 // +----+-----+-------+------+----------+----------+
360 // | 1 | 1 | X'00' | 1 | Variable | 2 |
361 // +----+-----+-------+------+----------+----------+
362 //
363 // VER protocol version: X'05'
364 // CMD
365 // CONNECT X'01'
366 // BIND X'02'
367 // UDP ASSOCIATE X'03'
368 // RSV RESERVED
369 // ATYP address type of following address
370 // IP V4 address X'01'
371 // DOMAINNAME X'03'
372 // IP V6 address X'04'
373 // DST.ADDR desired destination address
374 // DST.PORT desired destination port in network octet order
375
376 // Prepare data to send
377 tmp = 5;
378 WriteBuf(b, &tmp, sizeof(tmp)); // SOCKS version
379 tmp = 1;
380 WriteBuf(b, &tmp, sizeof(tmp)); // Command
381 tmp = 0;
382 WriteBuf(b, &tmp, sizeof(tmp)); // Reserved byte
383
384 // Convert the hostname to an IP structure (if it's an IP address)
385 StrToIP(&target_ip, in->TargetHostname);
386
387 // If the IP structure doesn't contain an IP address, the string should be an hostname
388 if (IsZeroIP(&target_ip))
389 {
390 UCHAR dest_length = StrLen(in->TargetHostname);
391 tmp = 3;
392 WriteBuf(b, &tmp, sizeof(tmp)); // Destination type (hostname)
393 WriteBuf(b, &dest_length, sizeof(dest_length)); // Destination hostname length
394 WriteBuf(b, in->TargetHostname, dest_length); // Destination hostname
395 }
396 else
397 {
398 if (IsIP6(&target_ip))
399 {
400 tmp = 4;
401 WriteBuf(b, &tmp, sizeof(tmp)); // Destination type (IPv6)
402 WriteBuf(b, target_ip.address, sizeof(target_ip.address)); // Destination IPv6 address
403 }
404 else
405 {
406 tmp = 1;
407 WriteBuf(b, &tmp, sizeof(tmp)); // Destination type (IPv4)
408 WriteBuf(b, IPV4(target_ip.address), IPV4_SIZE); // Destination IPv4 address
409 }
410 }
411
412 // Convert the port in network octet order
413 target_port = Endian16(in->TargetPort);
414 WriteBuf(b, &target_port, sizeof(target_port)); // Destination port
415
416 // Send data
417 ret = SendAll(s, b->Buf, b->Size, false);
418 FreeBuf(b);
419
420 if (ret == false)
421 {
422 Debug("ProxySocks5Connect(): [Phase 2] Failed to send data to the server.\n");
423 ret = PROXY_ERROR_DISCONNECTED;
424 goto FAILURE;
425 }
426
427 // +----+-----+-------+------+----------+----------+
428 // |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT |
429 // +----+-----+-------+------+----------+----------+
430 // | 1 | 1 | X’00’ | 1 | Variable | 2 |
431 // +----+-----+-------+------+----------+----------+
432 //
433 // VER protocol version: X’05’
434 // REP Reply field:
435 // X’00’ succeeded
436 // X’01’ general SOCKS server failure
437 // X’02’ connection not allowed by ruleset
438 // X’03’ Network unreachable
439 // X’04’ Host unreachable
440 // X’05’ Connection refused
441 // X’06’ TTL expired
442 // X’07’ Command not supported
443 // X’08’ Address type not supported
444 // X’09’ to X’FF’ unassigned
445
446 // The packet sent by the server should always have the same size as the one we sent to it.
447 // However, there are some implementations which send fixed values (aside from the first 2 bytes).
448 // In order to support such implementations, we read the first 4 bytes in order to know the address type before trying to read the rest of the packet.
449 recv_buf_final = Malloc(4);
450
451 if (RecvAll(s, recv_buf_final, 4, false) == false)
452 {
453 Free(recv_buf_final);
454 Debug("ProxySocks5Connect(): [Phase 2] Failed to receive response from the server.\n");
455 ret = PROXY_ERROR_DISCONNECTED;
456 goto FAILURE;
457 }
458
459 // We only need the first two bytes (version and response code), but we have to read the entire packet from the socket
460 recv_buf[0] = recv_buf_final[0];
461 recv_buf[1] = recv_buf_final[1];
462
463 // We receive the rest of the packet by knowing the size according to the address type
464 switch (recv_buf_final[3])
465 {
466 case 1:
467 // IPv4
468 recv_buf_final = ReAlloc(recv_buf_final, 6); // 4 bytes (IPv4) + 2 bytes (port)
469 ret = RecvAll(s, recv_buf_final, 6, false);
470 break;
471 case 4:
472 // IPv6
473 recv_buf_final = ReAlloc(recv_buf_final, 18); // 16 bytes (IPv6) + 2 bytes (port)
474 ret = RecvAll(s, recv_buf_final, 18, false);
475 break;
476 case 3:
477 // Hostname
478 ret = RecvAll(s, &tmp, 1, false);
479 if (ret == true)
480 {
481 recv_buf_final = ReAlloc(recv_buf_final, tmp + 2); // Hostname length + 2 bytes (port)
482 ret = RecvAll(s, recv_buf_final, tmp + 2, false);
483 }
484 }
485
486 Free(recv_buf_final);
487
488 if (ret == false)
489 {
490 Debug("ProxySocks5Connect(): [Phase 2] Malformed response received from the server.\n");
491 ret = PROXY_ERROR_DISCONNECTED;
492 goto FAILURE;
493 }
494
495 if (recv_buf[0] != 5)
496 {
497 Debug("ProxySocks5Connect(): [Phase 2] Unmatching version: %u.\n", recv_buf_final[0]);
498 ret = PROXY_ERROR_VERSION;
499 goto FAILURE;
500 }
501
502 switch (recv_buf[1])
503 {
504 case 0:
505 // Success
506 SetTimeout(s, INFINITE);
507 out->Sock = s;
508 return PROXY_ERROR_SUCCESS;
509 case 3:
510 case 4:
511 case 5:
512 Debug("ProxySocks5Connect(): [Phase 2] Connection to target failed with error: %u\n", recv_buf[1]);
513 ret = PROXY_ERROR_TARGET;
514 goto FAILURE;
515 default:
516 Debug("ProxySocks5Connect(): [Phase 2] Connection failed with error: %u\n", recv_buf[1]);
517 ret = PROXY_ERROR_GENERIC;
518 goto FAILURE;
519 }
520
521 FAILURE:
522 Disconnect(s);
523 ReleaseSock(s);
524 return ret;
525 }
526
527 // Connect to a SOCKS4 proxy
ProxySocks4Connect(PROXY_PARAM_OUT * out,PROXY_PARAM_IN * in,volatile bool * cancel_flag)528 UINT ProxySocks4Connect(PROXY_PARAM_OUT *out, PROXY_PARAM_IN *in, volatile bool *cancel_flag)
529 {
530 bool dummy_cancel_flag = false;
531 UCHAR tmp, recv_buf[8];
532 USHORT target_port;
533 IP target_ip;
534 UINT ret;
535 SOCK *s;
536 BUF *b;
537 // Validate arguments
538 if (out == NULL || in == NULL || in->Port == 0 || in->TargetPort == 0 || IsEmptyStr(in->Hostname) || IsEmptyStr(in->TargetHostname))
539 {
540 return PROXY_ERROR_PARAMETER;
541 }
542
543 if (cancel_flag == NULL)
544 {
545 cancel_flag = &dummy_cancel_flag;
546 }
547 else if (*cancel_flag)
548 {
549 return PROXY_ERROR_CANCELED;
550 }
551
552 Zero(out, sizeof(PROXY_PARAM_OUT));
553
554 // Get the IPv4 address of the destination server (SOCKS4 does not support IPv6).
555 if (GetIP4(&target_ip, in->TargetHostname) == false)
556 {
557 return PROXY_ERROR_CONNECTION;
558 }
559
560 // Open TCP connection to the proxy server
561 s = Internal_ProxyTcpConnect(in, cancel_flag, &out->ResolvedIp);
562 if (s == NULL)
563 {
564 return PROXY_ERROR_CONNECTION;
565 }
566
567 SetTimeout(s, MIN(PROXY_CONNECTION_TIMEOUT, (in->Timeout == 0 ? INFINITE : in->Timeout)));
568
569 // Send request packet
570 b = NewBuf();
571 tmp = 4;
572 WriteBuf(b, &tmp, sizeof(tmp));
573 tmp = 1;
574 WriteBuf(b, &tmp, sizeof(tmp));
575 target_port = Endian16(in->TargetPort);
576 WriteBuf(b, &target_port, sizeof(target_port));
577 WriteBuf(b, IPV4(target_ip.address), IPV4_SIZE);
578 WriteBuf(b, in->Username, StrLen(in->Username) + 1);
579
580 ret = SendAll(s, b->Buf, b->Size, false);
581
582 FreeBuf(b);
583
584 if (ret == false)
585 {
586 ret = PROXY_ERROR_DISCONNECTED;
587 goto FAILURE;
588 }
589
590 // Receive response packet
591 if (RecvAll(s, recv_buf, sizeof(recv_buf), false) == false)
592 {
593 ret = PROXY_ERROR_DISCONNECTED;
594 goto FAILURE;
595 }
596
597 if (recv_buf[0] != 0)
598 {
599 ret = PROXY_ERROR_GENERIC;
600 goto FAILURE;
601 }
602
603 switch (recv_buf[1])
604 {
605 case 90:
606 // Success
607 SetTimeout(s, INFINITE);
608 out->Sock = s;
609 return PROXY_ERROR_SUCCESS;
610 case 93:
611 // Authentication failure
612 ret = PROXY_ERROR_AUTHENTICATION;
613 goto FAILURE;
614 default:
615 // Failed to connect to the target server
616 ret = PROXY_ERROR_TARGET;
617 }
618
619 FAILURE:
620 Disconnect(s);
621 ReleaseSock(s);
622 return ret;
623 }
624