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