FreeRDP
Loading...
Searching...
No Matches
proxy.c
1
20#include <ctype.h>
21#include <errno.h>
22#include <limits.h>
23
24#include <openssl/err.h>
25
26#include "settings.h"
27#include "proxy.h"
28#include <freerdp/settings.h>
29#include <freerdp/utils/proxy_utils.h>
30#include <freerdp/crypto/crypto.h>
31#include "tcp.h"
32
33#include <winpr/assert.h>
34#include <winpr/sysinfo.h>
35#include <winpr/environment.h> /* For GetEnvironmentVariableA */
36
37#define CRLF "\r\n"
38
39#include <freerdp/log.h>
40#define TAG FREERDP_TAG("core.proxy")
41
42/* SOCKS Proxy auth methods by rfc1928 */
43enum
44{
45 AUTH_M_NO_AUTH = 0,
46 AUTH_M_GSSAPI = 1,
47 AUTH_M_USR_PASS = 2
48};
49
50enum
51{
52 SOCKS_CMD_CONNECT = 1,
53 SOCKS_CMD_BIND = 2,
54 SOCKS_CMD_UDP_ASSOCIATE = 3
55};
56
57enum
58{
59 SOCKS_ADDR_IPV4 = 1,
60 SOCKS_ADDR_FQDN = 3,
61 SOCKS_ADDR_IPV6 = 4,
62};
63
64static const char logprefix[] = "SOCKS Proxy:";
65
66/* CONN REQ replies in enum. order */
67static const char* rplstat[] = { "succeeded",
68 "general SOCKS server failure",
69 "connection not allowed by ruleset",
70 "Network unreachable",
71 "Host unreachable",
72 "Connection refused",
73 "TTL expired",
74 "Command not supported",
75 "Address type not supported" };
76
77static BOOL http_proxy_connect(rdpContext* context, BIO* bufferedBio, const char* proxyUsername,
78 const char* proxyPassword, const char* hostname, UINT16 port);
79static BOOL socks_proxy_connect(rdpContext* context, BIO* bufferedBio, const char* proxyUsername,
80 const char* proxyPassword, const char* hostname, UINT16 port);
81static void proxy_read_environment(rdpSettings* settings, char* envname);
82
83BOOL proxy_prepare(rdpSettings* settings, const char** lpPeerHostname, UINT16* lpPeerPort,
84 const char** lpProxyUsername, const char** lpProxyPassword)
85{
86 if (freerdp_settings_get_uint32(settings, FreeRDP_ProxyType) == PROXY_TYPE_IGNORE)
87 return FALSE;
88
89 /* For TSGateway, find the system HTTPS proxy automatically */
90 if (freerdp_settings_get_uint32(settings, FreeRDP_ProxyType) == PROXY_TYPE_NONE)
91 proxy_read_environment(settings, "https_proxy");
92
93 if (freerdp_settings_get_uint32(settings, FreeRDP_ProxyType) == PROXY_TYPE_NONE)
94 proxy_read_environment(settings, "HTTPS_PROXY");
95
96 if (freerdp_settings_get_uint32(settings, FreeRDP_ProxyType) != PROXY_TYPE_NONE)
97 proxy_read_environment(settings, "no_proxy");
98
99 if (freerdp_settings_get_uint32(settings, FreeRDP_ProxyType) != PROXY_TYPE_NONE)
100 proxy_read_environment(settings, "NO_PROXY");
101
102 if (freerdp_settings_get_uint32(settings, FreeRDP_ProxyType) != PROXY_TYPE_NONE)
103 {
104 *lpPeerHostname = freerdp_settings_get_string(settings, FreeRDP_ProxyHostname);
105 *lpPeerPort = freerdp_settings_get_uint16(settings, FreeRDP_ProxyPort);
106 *lpProxyUsername = freerdp_settings_get_string(settings, FreeRDP_ProxyUsername);
107 *lpProxyPassword = freerdp_settings_get_string(settings, FreeRDP_ProxyPassword);
108 return TRUE;
109 }
110
111 return FALSE;
112}
113
114static BOOL value_to_int(const char* value, LONGLONG* result, LONGLONG min, LONGLONG max)
115{
116 long long rc = 0;
117
118 if (!value || !result)
119 return FALSE;
120
121 errno = 0;
122 rc = _strtoi64(value, nullptr, 0);
123
124 if (errno != 0)
125 return FALSE;
126
127 if ((rc < min) || (rc > max))
128 return FALSE;
129
130 *result = rc;
131 return TRUE;
132}
133
134static BOOL cidr4_match(const struct in_addr* addr, const struct in_addr* net, BYTE bits)
135{
136 if (bits == 0)
137 return TRUE;
138
139 const uint32_t mask = htonl(0xFFFFFFFFu << (32 - bits));
140 const uint32_t amask = addr->s_addr & mask;
141 const uint32_t nmask = net->s_addr & mask;
142 return amask == nmask;
143}
144
145static BOOL cidr6_match(const struct in6_addr* address, const struct in6_addr* network,
146 uint8_t bits)
147{
148 const uint32_t* a = (const uint32_t*)address;
149 const uint32_t* n = (const uint32_t*)network;
150 const size_t bits_whole = bits >> 5;
151 const size_t bits_incomplete = bits & 0x1F;
152
153 if (bits_whole)
154 {
155 if (memcmp(a, n, bits_whole << 2) != 0)
156 return FALSE;
157 }
158
159 if (bits_incomplete)
160 {
161 uint32_t mask = htonl((0xFFFFFFFFu) << (32 - bits_incomplete));
162
163 if ((a[bits_whole] ^ n[bits_whole]) & mask)
164 return FALSE;
165 }
166
167 return TRUE;
168}
169
170static BOOL option_ends_with(const char* str, const char* ext)
171{
172 WINPR_ASSERT(str);
173 WINPR_ASSERT(ext);
174 const size_t strLen = strlen(str);
175 const size_t extLen = strlen(ext);
176
177 if (strLen < extLen)
178 return FALSE;
179
180 return _strnicmp(&str[strLen - extLen], ext, extLen) == 0;
181}
182
183/* no_proxy has no proper definition, so use curl as reference:
184 * https://about.gitlab.com/blog/2021/01/27/we-need-to-talk-no-proxy/
185 */
186static BOOL no_proxy_match_host(const char* val, const char* hostname)
187{
188 WINPR_ASSERT(val);
189 WINPR_ASSERT(hostname);
190
191 /* match all */
192 if (_stricmp("*", val) == 0)
193 return TRUE;
194
195 /* Strip leading . */
196 if (val[0] == '.')
197 val++;
198
199 /* Match suffix */
200 return option_ends_with(hostname, val);
201}
202
203static BOOL starts_with(const char* val, const char* prefix)
204{
205 const size_t plen = strlen(prefix);
206 const size_t vlen = strlen(val);
207 if (vlen < plen)
208 return FALSE;
209 return _strnicmp(val, prefix, plen) == 0;
210}
211
212static BOOL no_proxy_match_ip(const char* val, const char* hostname)
213{
214 WINPR_ASSERT(val);
215 WINPR_ASSERT(hostname);
216
217 struct sockaddr_in sa4 = WINPR_C_ARRAY_INIT;
218 struct sockaddr_in6 sa6 = WINPR_C_ARRAY_INIT;
219
220 if (inet_pton(AF_INET, hostname, &sa4.sin_addr) == 1)
221 {
222 /* Prefix match */
223 if (starts_with(hostname, val))
224 return TRUE;
225
226 char* sub = strchr(val, '/');
227 if (sub)
228 *sub++ = '\0';
229
230 struct sockaddr_in mask = WINPR_C_ARRAY_INIT;
231 if (inet_pton(AF_INET, val, &mask.sin_addr) == 0)
232 return FALSE;
233
234 /* IP address match */
235 if (memcmp(&mask, &sa4, sizeof(mask)) == 0)
236 return TRUE;
237
238 if (sub)
239 {
240 const unsigned long usub = strtoul(sub, nullptr, 0);
241 if ((errno == 0) && (usub <= UINT8_MAX))
242 return cidr4_match(&sa4.sin_addr, &mask.sin_addr, (UINT8)usub);
243 }
244 }
245 else if (inet_pton(AF_INET6, hostname, &sa6.sin6_addr) == 1)
246 {
247 if (val[0] == '[')
248 val++;
249
250 char str[INET6_ADDRSTRLEN + 1] = WINPR_C_ARRAY_INIT;
251 strncpy(str, val, INET6_ADDRSTRLEN);
252
253 const size_t len = strnlen(str, INET6_ADDRSTRLEN);
254 if (len > 0)
255 {
256 if (str[len - 1] == ']')
257 str[len - 1] = '\0';
258 }
259
260 /* Prefix match */
261 if (starts_with(hostname, str))
262 return TRUE;
263
264 char* sub = strchr(str, '/');
265 if (sub)
266 *sub++ = '\0';
267
268 struct sockaddr_in6 mask = WINPR_C_ARRAY_INIT;
269 if (inet_pton(AF_INET6, str, &mask.sin6_addr) == 0)
270 return FALSE;
271
272 /* Address match */
273 if (memcmp(&mask, &sa6, sizeof(mask)) == 0)
274 return TRUE;
275
276 if (sub)
277 {
278 const unsigned long usub = strtoul(sub, nullptr, 0);
279 if ((errno == 0) && (usub <= UINT8_MAX))
280 return cidr6_match(&sa6.sin6_addr, &mask.sin6_addr, (UINT8)usub);
281 }
282 }
283
284 return FALSE;
285}
286
287static BOOL check_no_proxy(rdpSettings* settings, const char* no_proxy)
288{
289 const char* delimiter = ", ";
290 BOOL result = FALSE;
291 char* context = nullptr;
292
293 if (!no_proxy || !settings)
294 return FALSE;
295
296 char* copy = _strdup(no_proxy);
297
298 if (!copy)
299 return FALSE;
300
301 char* current = strtok_s(copy, delimiter, &context);
302
303 while (current && !result)
304 {
305 const size_t currentlen = strlen(current);
306
307 if (currentlen > 0)
308 {
309 const char* ServerHostname =
310 freerdp_settings_get_string(settings, FreeRDP_ServerHostname);
311 WLog_DBG(TAG, "%s => %s (%" PRIuz ")", ServerHostname, current, currentlen);
312
313 if (no_proxy_match_host(current, ServerHostname))
314 result = TRUE;
315 else if (no_proxy_match_ip(current, ServerHostname))
316 result = TRUE;
317 }
318
319 current = strtok_s(nullptr, delimiter, &context);
320 }
321
322 free(copy);
323 return result;
324}
325
326void proxy_read_environment(rdpSettings* settings, char* envname)
327{
328 const DWORD envlen = GetEnvironmentVariableA(envname, nullptr, 0);
329
330 if (!envlen || (envlen <= 1))
331 return;
332
333 char* env = calloc(1, envlen);
334
335 if (!env)
336 {
337 WLog_ERR(TAG, "Not enough memory");
338 return;
339 }
340
341 if (GetEnvironmentVariableA(envname, env, envlen) == envlen - 1)
342 {
343 if (_strnicmp("NO_PROXY", envname, 9) == 0)
344 {
345 if (check_no_proxy(settings, env))
346 {
347 WLog_INFO(TAG, "deactivating proxy: %s [%s=%s]",
348 freerdp_settings_get_string(settings, FreeRDP_ServerHostname), envname,
349 env);
350 if (!freerdp_settings_set_uint32(settings, FreeRDP_ProxyType, PROXY_TYPE_NONE))
351 WLog_WARN(TAG, "failed to set FreeRDP_ProxyType=PROXY_TYPE_NONE");
352 }
353 }
354 else
355 {
356 if (!proxy_parse_uri(settings, env))
357 {
358 WLog_WARN(
359 TAG, "Error while parsing proxy URI from environment variable; ignoring proxy");
360 }
361 }
362 }
363
364 free(env);
365}
366
367BOOL proxy_parse_uri(rdpSettings* settings, const char* uri_in)
368{
369 BOOL rc = FALSE;
370 const char* protocol = "";
371 UINT16 port = 0;
372
373 if (!settings || !uri_in)
374 return FALSE;
375
376 char* uri_copy = _strdup(uri_in);
377 char* uri = uri_copy;
378 if (!uri)
379 goto fail;
380
381 {
382 char* p = strstr(uri, "://");
383 if (p)
384 {
385 *p = '\0';
386
387 if (_stricmp("no_proxy", uri) == 0)
388 {
389 if (!freerdp_settings_set_uint32(settings, FreeRDP_ProxyType, PROXY_TYPE_IGNORE))
390 goto fail;
391 }
392 if (_stricmp("http", uri) == 0)
393 {
394 if (!freerdp_settings_set_uint32(settings, FreeRDP_ProxyType, PROXY_TYPE_HTTP))
395 goto fail;
396 protocol = "http";
397 }
398 else if (_stricmp("socks5", uri) == 0)
399 {
400 if (!freerdp_settings_set_uint32(settings, FreeRDP_ProxyType, PROXY_TYPE_SOCKS))
401 goto fail;
402 protocol = "socks5";
403 }
404 else
405 {
406 WLog_ERR(TAG, "Only HTTP and SOCKS5 proxies supported by now");
407 goto fail;
408 }
409
410 uri = p + 3;
411 }
412 else
413 {
414 /* default proxy protocol is http */
415 if (!freerdp_settings_set_uint32(settings, FreeRDP_ProxyType, PROXY_TYPE_HTTP))
416 goto fail;
417 protocol = "http";
418 }
419 }
420
421 /* uri is now [user:password@]hostname:port */
422 {
423 char* atPtr = strrchr(uri, '@');
424
425 if (atPtr)
426 {
427 /* got a login / password,
428 * atPtr
429 * v
430 * [user:password@]hostname:port
431 * ^
432 * colonPtr
433 */
434 char* colonPtr = strchr(uri, ':');
435
436 if (!colonPtr || (colonPtr > atPtr))
437 {
438 WLog_ERR(TAG, "invalid syntax for proxy (contains no password)");
439 goto fail;
440 }
441
442 *colonPtr = '\0';
443 if (!freerdp_settings_set_string(settings, FreeRDP_ProxyUsername, uri))
444 {
445 WLog_ERR(TAG, "unable to allocate proxy username");
446 goto fail;
447 }
448
449 *atPtr = '\0';
450
451 if (!freerdp_settings_set_string(settings, FreeRDP_ProxyPassword, colonPtr + 1))
452 {
453 WLog_ERR(TAG, "unable to allocate proxy password");
454 goto fail;
455 }
456
457 uri = atPtr + 1;
458 }
459 }
460
461 {
462 char* p = strchr(uri, ':');
463
464 if (p)
465 {
466 LONGLONG val = 0;
467
468 if (!value_to_int(&p[1], &val, 0, UINT16_MAX))
469 {
470 WLog_ERR(TAG, "invalid syntax for proxy (invalid port)");
471 goto fail;
472 }
473
474 if (val == 0)
475 {
476 WLog_ERR(TAG, "invalid syntax for proxy (port missing)");
477 goto fail;
478 }
479
480 port = (UINT16)val;
481 *p = '\0';
482 }
483 else
484 {
485 if (_stricmp("http", protocol) == 0)
486 {
487 /* The default is 80. Also for Proxies. */
488 port = 80;
489 }
490 else
491 {
492 port = 1080;
493 }
494
495 WLog_DBG(TAG, "setting default proxy port: %" PRIu16, port);
496 }
497
498 if (!freerdp_settings_set_uint16(settings, FreeRDP_ProxyPort, port))
499 goto fail;
500 }
501 {
502 char* p = strchr(uri, '/');
503 if (p)
504 *p = '\0';
505 if (!freerdp_settings_set_string(settings, FreeRDP_ProxyHostname, uri))
506 goto fail;
507 }
508
509 if (_stricmp("", uri) == 0)
510 {
511 WLog_ERR(TAG, "invalid syntax for proxy (hostname missing)");
512 goto fail;
513 }
514
515 if (freerdp_settings_get_string(settings, FreeRDP_ProxyUsername))
516 {
517 WLog_INFO(TAG, "Parsed proxy configuration: %s://%s:%s@%s:%" PRIu16, protocol,
518 freerdp_settings_get_string(settings, FreeRDP_ProxyUsername), "******",
519 freerdp_settings_get_string(settings, FreeRDP_ProxyHostname),
520 freerdp_settings_get_uint16(settings, FreeRDP_ProxyPort));
521 }
522 else
523 {
524 WLog_INFO(TAG, "Parsed proxy configuration: %s://%s:%" PRIu16, protocol,
525 freerdp_settings_get_string(settings, FreeRDP_ProxyHostname),
526 freerdp_settings_get_uint16(settings, FreeRDP_ProxyPort));
527 }
528 rc = TRUE;
529
530fail:
531 if (!rc)
532 WLog_WARN(TAG, "Failed to parse proxy configuration: %s://%s:%" PRIu16, protocol, uri,
533 port);
534 free(uri_copy);
535 return rc;
536}
537
538BOOL proxy_connect(rdpContext* context, BIO* bufferedBio, const char* proxyUsername,
539 const char* proxyPassword, const char* hostname, UINT16 port)
540{
541 WINPR_ASSERT(context);
542 rdpSettings* settings = context->settings;
543
544 switch (freerdp_settings_get_uint32(settings, FreeRDP_ProxyType))
545 {
546 case PROXY_TYPE_NONE:
547 case PROXY_TYPE_IGNORE:
548 return TRUE;
549
550 case PROXY_TYPE_HTTP:
551 return http_proxy_connect(context, bufferedBio, proxyUsername, proxyPassword, hostname,
552 port);
553
554 case PROXY_TYPE_SOCKS:
555 return socks_proxy_connect(context, bufferedBio, proxyUsername, proxyPassword, hostname,
556 port);
557
558 default:
559 WLog_ERR(TAG, "Invalid internal proxy configuration");
560 return FALSE;
561 }
562}
563
564static const char* get_response_header(char* response)
565{
566 char* current_pos = strchr(response, '\r');
567 if (!current_pos)
568 current_pos = strchr(response, '\n');
569
570 if (current_pos)
571 *current_pos = '\0';
572
573 return response;
574}
575
576static BOOL http_proxy_connect(rdpContext* context, BIO* bufferedBio, const char* proxyUsername,
577 const char* proxyPassword, const char* hostname, UINT16 port)
578{
579 BOOL rc = FALSE;
580 int status = 0;
581 wStream* s = nullptr;
582 char port_str[10] = WINPR_C_ARRAY_INIT;
583 char recv_buf[256] = WINPR_C_ARRAY_INIT;
584 char* eol = nullptr;
585 size_t resultsize = 0;
586 size_t reserveSize = 0;
587 size_t portLen = 0;
588 size_t hostLen = 0;
589 const char connect[] = "CONNECT ";
590 const char httpheader[] = " HTTP/1.1" CRLF "Host: ";
591
592 WINPR_ASSERT(context);
593 WINPR_ASSERT(bufferedBio);
594 WINPR_ASSERT(hostname);
595 const UINT32 timeout =
596 freerdp_settings_get_uint32(context->settings, FreeRDP_TcpConnectTimeout);
597
598 if (_itoa_s(port, port_str, sizeof(port_str), 10) < 0)
599 {
600 WLog_ERR(TAG, "itoa %s failed", port_str);
601 return FALSE;
602 }
603
604 hostLen = strlen(hostname);
605 portLen = strnlen(port_str, sizeof(port_str));
606 reserveSize = strlen(connect) + (hostLen + 1ull + portLen) * 2ull + strlen(httpheader);
607 s = Stream_New(nullptr, reserveSize);
608 if (!s)
609 goto fail;
610
611 Stream_Write(s, connect, strlen(connect));
612 Stream_Write(s, hostname, hostLen);
613 Stream_Write_UINT8(s, ':');
614 Stream_Write(s, port_str, portLen);
615 Stream_Write(s, httpheader, strlen(httpheader));
616 Stream_Write(s, hostname, hostLen);
617 Stream_Write_UINT8(s, ':');
618 Stream_Write(s, port_str, portLen);
619
620 if (proxyUsername && proxyPassword)
621 {
622 const int length = _scprintf("%s:%s", proxyUsername, proxyPassword);
623 if (length > 0)
624 {
625 const size_t size = (size_t)length + 1ull;
626 char* creds = (char*)malloc(size);
627
628 if (!creds)
629 goto fail;
630 else
631 {
632 const char basic[] = CRLF "Proxy-Authorization: Basic ";
633
634 (void)sprintf_s(creds, size, "%s:%s", proxyUsername, proxyPassword);
635
636 size_t b64len = 0;
637 char* base64 = crypto_base64_encode_len(creds, size - 1, &b64len);
638
639 if (!base64 ||
640 !Stream_EnsureRemainingCapacity(s, strnlen(basic, sizeof(basic) + b64len)))
641 {
642 free(base64);
643 free(creds);
644 goto fail;
645 }
646 Stream_Write(s, basic, strlen(basic));
647 Stream_Write(s, base64, strlen(base64));
648
649 free(base64);
650 }
651 free(creds);
652 }
653 }
654
655 if (!Stream_EnsureRemainingCapacity(s, 4))
656 goto fail;
657
658 Stream_Write(s, CRLF CRLF, 4);
659 ERR_clear_error();
660
661 {
662 const size_t pos = Stream_GetPosition(s);
663 if (pos > INT32_MAX)
664 goto fail;
665
666 status = BIO_write(bufferedBio, Stream_Buffer(s), WINPR_ASSERTING_INT_CAST(int, pos));
667 }
668
669 if ((status < 0) || ((size_t)status != Stream_GetPosition(s)))
670 {
671 WLog_ERR(TAG, "HTTP proxy: failed to write CONNECT request");
672 goto fail;
673 }
674
675 /* Read result until CR-LF-CR-LF.
676 * Keep recv_buf a null-terminated string. */
677 {
678 const UINT64 start = GetTickCount64();
679 while (strstr(recv_buf, CRLF CRLF) == nullptr)
680 {
681 if (resultsize >= sizeof(recv_buf) - 1)
682 {
683 WLog_ERR(TAG, "HTTP Reply headers too long: %s", get_response_header(recv_buf));
684 goto fail;
685 }
686 const size_t rdsize = sizeof(recv_buf) - resultsize - 1ULL;
687
688 ERR_clear_error();
689
690 WINPR_ASSERT(rdsize <= INT32_MAX);
691 status = BIO_read(bufferedBio, (BYTE*)recv_buf + resultsize, (int)rdsize);
692
693 if (status < 0)
694 {
695 /* Error? */
696 if (!freerdp_shall_disconnect_context(context) && BIO_should_retry(bufferedBio))
697 {
698 USleep(100);
699 continue;
700 }
701
702 WLog_ERR(TAG, "Failed reading reply from HTTP proxy (Status %d)", status);
703 goto fail;
704 }
705 else if (status == 0)
706 {
707 const UINT64 now = GetTickCount64();
708 const UINT64 diff = now - start;
709 if (freerdp_shall_disconnect_context(context) || (now < start) || (diff > timeout))
710 {
711 /* Error? */
712 WLog_ERR(TAG, "Failed reading reply from HTTP proxy (BIO_read returned zero)");
713 goto fail;
714 }
715 Sleep(10);
716 }
717
718 resultsize += WINPR_ASSERTING_INT_CAST(size_t, status);
719 }
720 }
721
722 /* Extract HTTP status line */
723 eol = strchr(recv_buf, '\r');
724
725 if (!eol)
726 {
727 /* should never happen */
728 goto fail;
729 }
730
731 *eol = '\0';
732 WLog_INFO(TAG, "HTTP Proxy: %s", recv_buf);
733
734 if (strnlen(recv_buf, sizeof(recv_buf)) < 12)
735 goto fail;
736
737 recv_buf[7] = 'X';
738
739 if (strncmp(recv_buf, "HTTP/1.X 200", 12) != 0)
740 goto fail;
741
742 rc = TRUE;
743fail:
744 WLog_ERR(TAG, "Failed to connect to proxy");
745 Stream_Free(s, TRUE);
746 return rc;
747}
748
749static int recv_socks_reply(rdpContext* context, BIO* bufferedBio, BYTE* buf, int len, char* reason,
750 BYTE checkVer)
751{
752 int status = 0;
753
754 WINPR_ASSERT(context);
755
756 const UINT32 timeout =
757 freerdp_settings_get_uint32(context->settings, FreeRDP_TcpConnectTimeout);
758 const UINT64 start = GetTickCount64();
759 for (;;)
760 {
761 ERR_clear_error();
762 status = BIO_read(bufferedBio, buf, len);
763
764 if (status > 0)
765 {
766 break;
767 }
768 else if (status < 0)
769 {
770 /* Error? */
771 if (!freerdp_shall_disconnect_context(context) && BIO_should_retry(bufferedBio))
772 {
773 USleep(100);
774 continue;
775 }
776
777 WLog_ERR(TAG, "Failed reading %s reply from SOCKS proxy (Status %d)", reason, status);
778 return -1;
779 }
780 else if (status == 0)
781 {
782 const UINT64 now = GetTickCount64();
783 const UINT64 diff = now - start;
784 if (freerdp_shall_disconnect_context(context) || (now < start) || (diff > timeout))
785 {
786 /* Error? */
787 WLog_ERR(TAG, "Failed reading %s reply from SOCKS proxy (BIO_read returned zero)",
788 reason);
789 return status;
790 }
791 Sleep(10);
792 }
793 else // if (status == 0)
794 {
795 /* Error? */
796 WLog_ERR(TAG, "Failed reading %s reply from SOCKS proxy (BIO_read returned zero)",
797 reason);
798 return -1;
799 }
800 }
801
802 if (status < 2)
803 {
804 WLog_ERR(TAG, "SOCKS Proxy reply packet too short (%s)", reason);
805 return -1;
806 }
807
808 if (buf[0] != checkVer)
809 {
810 WLog_ERR(TAG, "SOCKS Proxy version is not 5 (%s)", reason);
811 return -1;
812 }
813
814 return status;
815}
816
817static BOOL socks_proxy_userpass(rdpContext* context, BIO* bufferedBio, const char* proxyUsername,
818 const char* proxyPassword)
819{
820 WINPR_ASSERT(context);
821 WINPR_ASSERT(bufferedBio);
822
823 if (!proxyUsername || !proxyPassword)
824 {
825 WLog_ERR(TAG, "%s invalid username (%p) or password (%p)", logprefix,
826 WINPR_CXX_COMPAT_CAST(const void*, proxyUsername),
827 WINPR_CXX_COMPAT_CAST(const void*, proxyPassword));
828 return FALSE;
829 }
830
831 const size_t usernameLen = (BYTE)strnlen(proxyUsername, 256);
832 if (usernameLen > 255)
833 {
834 WLog_ERR(TAG, "%s username too long (%" PRIuz ", max=255)", logprefix, usernameLen);
835 return FALSE;
836 }
837
838 const size_t userpassLen = (BYTE)strnlen(proxyPassword, 256);
839 if (userpassLen > 255)
840 {
841 WLog_ERR(TAG, "%s password too long (%" PRIuz ", max=255)", logprefix, userpassLen);
842 return FALSE;
843 }
844
845 /* user/password v1 method */
846 {
847 BYTE buf[2 * 255 + 3] = WINPR_C_ARRAY_INIT;
848 size_t offset = 0;
849 buf[offset++] = 1;
850
851 buf[offset++] = WINPR_ASSERTING_INT_CAST(uint8_t, usernameLen);
852 memcpy(&buf[offset], proxyUsername, usernameLen);
853 offset += usernameLen;
854
855 buf[offset++] = WINPR_ASSERTING_INT_CAST(uint8_t, userpassLen);
856 memcpy(&buf[offset], proxyPassword, userpassLen);
857 offset += userpassLen;
858
859 ERR_clear_error();
860 const int ioffset = WINPR_ASSERTING_INT_CAST(int, offset);
861 const int status = BIO_write(bufferedBio, buf, ioffset);
862
863 if (status != ioffset)
864 {
865 WLog_ERR(TAG, "%s error writing user/password request", logprefix);
866 return FALSE;
867 }
868 }
869
870 BYTE buf[2] = WINPR_C_ARRAY_INIT;
871 const int status = recv_socks_reply(context, bufferedBio, buf, sizeof(buf), "AUTH REQ", 1);
872
873 if (status < 2)
874 return FALSE;
875
876 if (buf[1] != 0x00)
877 {
878 WLog_ERR(TAG, "%s invalid user/password", logprefix);
879 return FALSE;
880 }
881 return TRUE;
882}
883
884static BOOL socks_proxy_connect(rdpContext* context, BIO* bufferedBio, const char* proxyUsername,
885 const char* proxyPassword, const char* hostname, UINT16 port)
886{
887 BYTE nauthMethods = 1;
888 const size_t hostnlen = strnlen(hostname, 255);
889
890 if (proxyUsername || proxyPassword)
891 nauthMethods++;
892
893 /* select auth. method */
894 {
895 const BYTE buf[] = { 5, /* SOCKS version */
896 nauthMethods, /* #of methods offered */
897 AUTH_M_NO_AUTH, AUTH_M_USR_PASS };
898
899 size_t writeLen = sizeof(buf);
900 if (nauthMethods <= 1)
901 writeLen--;
902
903 ERR_clear_error();
904 const int iwriteLen = WINPR_ASSERTING_INT_CAST(int, writeLen);
905 const int status = BIO_write(bufferedBio, buf, iwriteLen);
906
907 if (status != iwriteLen)
908 {
909 WLog_ERR(TAG, "%s SOCKS proxy: failed to write AUTH METHOD request", logprefix);
910 return FALSE;
911 }
912 }
913
914 {
915 BYTE buf[2] = WINPR_C_ARRAY_INIT;
916 const int status = recv_socks_reply(context, bufferedBio, buf, sizeof(buf), "AUTH REQ", 5);
917
918 if (status <= 0)
919 return FALSE;
920
921 switch (buf[1])
922 {
923 case AUTH_M_NO_AUTH:
924 WLog_DBG(TAG, "%s (NO AUTH) method was selected", logprefix);
925 break;
926
927 case AUTH_M_USR_PASS:
928 if (nauthMethods < 2)
929 {
930 WLog_ERR(TAG, "%s USER/PASS method was not proposed to server", logprefix);
931 return FALSE;
932 }
933 if (!socks_proxy_userpass(context, bufferedBio, proxyUsername, proxyPassword))
934 return FALSE;
935 break;
936
937 default:
938 WLog_ERR(TAG, "%s unknown method 0x%x was selected by proxy", logprefix, buf[1]);
939 return FALSE;
940 }
941 }
942 /* CONN request */
943 {
944 BYTE buf[262] = WINPR_C_ARRAY_INIT;
945 size_t offset = 0;
946 buf[offset++] = 5; /* SOCKS version */
947 buf[offset++] = SOCKS_CMD_CONNECT; /* command */
948 buf[offset++] = 0; /* 3rd octet is reserved x00 */
949
950 if (inet_pton(AF_INET6, hostname, &buf[offset + 1]) == 1)
951 {
952 buf[offset++] = SOCKS_ADDR_IPV6;
953 offset += 16;
954 }
955 else if (inet_pton(AF_INET, hostname, &buf[offset + 1]) == 1)
956 {
957 buf[offset++] = SOCKS_ADDR_IPV4;
958 offset += 4;
959 }
960 else
961 {
962 buf[offset++] = SOCKS_ADDR_FQDN;
963 buf[offset++] = WINPR_ASSERTING_INT_CAST(uint8_t, hostnlen);
964 memcpy(&buf[offset], hostname, hostnlen);
965 offset += hostnlen;
966 }
967
968 if (offset > sizeof(buf) - 2)
969 {
970 WLog_ERR(TAG, "Invalid offset %" PRIuz, offset);
971 return FALSE;
972 }
973
974 /* follows DST.PORT in netw. format */
975 buf[offset++] = (port >> 8) & 0xff;
976 buf[offset++] = port & 0xff;
977
978 ERR_clear_error();
979 const int ioffset = WINPR_ASSERTING_INT_CAST(int, offset);
980 const int status = BIO_write(bufferedBio, buf, ioffset);
981
982 if ((status < 0) || (status != ioffset))
983 {
984 WLog_ERR(TAG, "%s SOCKS proxy: failed to write CONN REQ", logprefix);
985 return FALSE;
986 }
987 }
988
989 BYTE buf[255] = WINPR_C_ARRAY_INIT;
990 const int status = recv_socks_reply(context, bufferedBio, buf, sizeof(buf), "CONN REQ", 5);
991
992 if (status < 4)
993 return FALSE;
994
995 if (buf[1] == 0)
996 {
997 WLog_INFO(TAG, "Successfully connected to %s:%" PRIu16, hostname, port);
998 return TRUE;
999 }
1000
1001 if ((buf[1] > 0) && (buf[1] < 9))
1002 WLog_INFO(TAG, "SOCKS Proxy replied: %s", rplstat[buf[1]]);
1003 else
1004 WLog_INFO(TAG, "SOCKS Proxy replied: %" PRIu8 " status not listed in rfc1928", buf[1]);
1005
1006 return FALSE;
1007}
WINPR_ATTR_NODISCARD FREERDP_API const char * freerdp_settings_get_string(const rdpSettings *settings, FreeRDP_Settings_Keys_String id)
Returns a immutable string settings value.
WINPR_ATTR_NODISCARD FREERDP_API UINT16 freerdp_settings_get_uint16(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt16 id)
Returns a UINT16 settings value.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_set_uint32(rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id, UINT32 val)
Sets a UINT32 settings value.
WINPR_ATTR_NODISCARD FREERDP_API UINT32 freerdp_settings_get_uint32(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id)
Returns a UINT32 settings value.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_set_uint16(rdpSettings *settings, FreeRDP_Settings_Keys_UInt16 id, UINT16 val)
Sets a UINT16 settings value.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_set_string(rdpSettings *settings, FreeRDP_Settings_Keys_String id, const char *val)
Sets a string settings value. The param is copied.