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 char* base64 = nullptr;
634
635 (void)sprintf_s(creds, size, "%s:%s", proxyUsername, proxyPassword);
636 base64 = crypto_base64_encode((const BYTE*)creds, size - 1);
637
638 if (!base64 || !Stream_EnsureRemainingCapacity(s, strlen(basic) + strlen(base64)))
639 {
640 free(base64);
641 free(creds);
642 goto fail;
643 }
644 Stream_Write(s, basic, strlen(basic));
645 Stream_Write(s, base64, strlen(base64));
646
647 free(base64);
648 }
649 free(creds);
650 }
651 }
652
653 if (!Stream_EnsureRemainingCapacity(s, 4))
654 goto fail;
655
656 Stream_Write(s, CRLF CRLF, 4);
657 ERR_clear_error();
658
659 {
660 const size_t pos = Stream_GetPosition(s);
661 if (pos > INT32_MAX)
662 goto fail;
663
664 status = BIO_write(bufferedBio, Stream_Buffer(s), WINPR_ASSERTING_INT_CAST(int, pos));
665 }
666
667 if ((status < 0) || ((size_t)status != Stream_GetPosition(s)))
668 {
669 WLog_ERR(TAG, "HTTP proxy: failed to write CONNECT request");
670 goto fail;
671 }
672
673 /* Read result until CR-LF-CR-LF.
674 * Keep recv_buf a null-terminated string. */
675 {
676 const UINT64 start = GetTickCount64();
677 while (strstr(recv_buf, CRLF CRLF) == nullptr)
678 {
679 if (resultsize >= sizeof(recv_buf) - 1)
680 {
681 WLog_ERR(TAG, "HTTP Reply headers too long: %s", get_response_header(recv_buf));
682 goto fail;
683 }
684 const size_t rdsize = sizeof(recv_buf) - resultsize - 1ULL;
685
686 ERR_clear_error();
687
688 WINPR_ASSERT(rdsize <= INT32_MAX);
689 status = BIO_read(bufferedBio, (BYTE*)recv_buf + resultsize, (int)rdsize);
690
691 if (status < 0)
692 {
693 /* Error? */
694 if (!freerdp_shall_disconnect_context(context) && BIO_should_retry(bufferedBio))
695 {
696 USleep(100);
697 continue;
698 }
699
700 WLog_ERR(TAG, "Failed reading reply from HTTP proxy (Status %d)", status);
701 goto fail;
702 }
703 else if (status == 0)
704 {
705 const UINT64 now = GetTickCount64();
706 const UINT64 diff = now - start;
707 if (freerdp_shall_disconnect_context(context) || (now < start) || (diff > timeout))
708 {
709 /* Error? */
710 WLog_ERR(TAG, "Failed reading reply from HTTP proxy (BIO_read returned zero)");
711 goto fail;
712 }
713 Sleep(10);
714 }
715
716 resultsize += WINPR_ASSERTING_INT_CAST(size_t, status);
717 }
718 }
719
720 /* Extract HTTP status line */
721 eol = strchr(recv_buf, '\r');
722
723 if (!eol)
724 {
725 /* should never happen */
726 goto fail;
727 }
728
729 *eol = '\0';
730 WLog_INFO(TAG, "HTTP Proxy: %s", recv_buf);
731
732 if (strnlen(recv_buf, sizeof(recv_buf)) < 12)
733 goto fail;
734
735 recv_buf[7] = 'X';
736
737 if (strncmp(recv_buf, "HTTP/1.X 200", 12) != 0)
738 goto fail;
739
740 rc = TRUE;
741fail:
742 WLog_ERR(TAG, "Failed to connect to proxy");
743 Stream_Free(s, TRUE);
744 return rc;
745}
746
747static int recv_socks_reply(rdpContext* context, BIO* bufferedBio, BYTE* buf, int len, char* reason,
748 BYTE checkVer)
749{
750 int status = 0;
751
752 WINPR_ASSERT(context);
753
754 const UINT32 timeout =
755 freerdp_settings_get_uint32(context->settings, FreeRDP_TcpConnectTimeout);
756 const UINT64 start = GetTickCount64();
757 for (;;)
758 {
759 ERR_clear_error();
760 status = BIO_read(bufferedBio, buf, len);
761
762 if (status > 0)
763 {
764 break;
765 }
766 else if (status < 0)
767 {
768 /* Error? */
769 if (!freerdp_shall_disconnect_context(context) && BIO_should_retry(bufferedBio))
770 {
771 USleep(100);
772 continue;
773 }
774
775 WLog_ERR(TAG, "Failed reading %s reply from SOCKS proxy (Status %d)", reason, status);
776 return -1;
777 }
778 else if (status == 0)
779 {
780 const UINT64 now = GetTickCount64();
781 const UINT64 diff = now - start;
782 if (freerdp_shall_disconnect_context(context) || (now < start) || (diff > timeout))
783 {
784 /* Error? */
785 WLog_ERR(TAG, "Failed reading %s reply from SOCKS proxy (BIO_read returned zero)",
786 reason);
787 return status;
788 }
789 Sleep(10);
790 }
791 else // if (status == 0)
792 {
793 /* Error? */
794 WLog_ERR(TAG, "Failed reading %s reply from SOCKS proxy (BIO_read returned zero)",
795 reason);
796 return -1;
797 }
798 }
799
800 if (status < 2)
801 {
802 WLog_ERR(TAG, "SOCKS Proxy reply packet too short (%s)", reason);
803 return -1;
804 }
805
806 if (buf[0] != checkVer)
807 {
808 WLog_ERR(TAG, "SOCKS Proxy version is not 5 (%s)", reason);
809 return -1;
810 }
811
812 return status;
813}
814
815static BOOL socks_proxy_userpass(rdpContext* context, BIO* bufferedBio, const char* proxyUsername,
816 const char* proxyPassword)
817{
818 WINPR_ASSERT(context);
819 WINPR_ASSERT(bufferedBio);
820
821 if (!proxyUsername || !proxyPassword)
822 {
823 WLog_ERR(TAG, "%s invalid username (%p) or password (%p)", logprefix,
824 WINPR_CXX_COMPAT_CAST(const void*, proxyUsername),
825 WINPR_CXX_COMPAT_CAST(const void*, proxyPassword));
826 return FALSE;
827 }
828
829 const size_t usernameLen = (BYTE)strnlen(proxyUsername, 256);
830 if (usernameLen > 255)
831 {
832 WLog_ERR(TAG, "%s username too long (%" PRIuz ", max=255)", logprefix, usernameLen);
833 return FALSE;
834 }
835
836 const size_t userpassLen = (BYTE)strnlen(proxyPassword, 256);
837 if (userpassLen > 255)
838 {
839 WLog_ERR(TAG, "%s password too long (%" PRIuz ", max=255)", logprefix, userpassLen);
840 return FALSE;
841 }
842
843 /* user/password v1 method */
844 {
845 BYTE buf[2 * 255 + 3] = WINPR_C_ARRAY_INIT;
846 size_t offset = 0;
847 buf[offset++] = 1;
848
849 buf[offset++] = WINPR_ASSERTING_INT_CAST(uint8_t, usernameLen);
850 memcpy(&buf[offset], proxyUsername, usernameLen);
851 offset += usernameLen;
852
853 buf[offset++] = WINPR_ASSERTING_INT_CAST(uint8_t, userpassLen);
854 memcpy(&buf[offset], proxyPassword, userpassLen);
855 offset += userpassLen;
856
857 ERR_clear_error();
858 const int ioffset = WINPR_ASSERTING_INT_CAST(int, offset);
859 const int status = BIO_write(bufferedBio, buf, ioffset);
860
861 if (status != ioffset)
862 {
863 WLog_ERR(TAG, "%s error writing user/password request", logprefix);
864 return FALSE;
865 }
866 }
867
868 BYTE buf[2] = WINPR_C_ARRAY_INIT;
869 const int status = recv_socks_reply(context, bufferedBio, buf, sizeof(buf), "AUTH REQ", 1);
870
871 if (status < 2)
872 return FALSE;
873
874 if (buf[1] != 0x00)
875 {
876 WLog_ERR(TAG, "%s invalid user/password", logprefix);
877 return FALSE;
878 }
879 return TRUE;
880}
881
882static BOOL socks_proxy_connect(rdpContext* context, BIO* bufferedBio, const char* proxyUsername,
883 const char* proxyPassword, const char* hostname, UINT16 port)
884{
885 BYTE nauthMethods = 1;
886 const size_t hostnlen = strnlen(hostname, 255);
887
888 if (proxyUsername || proxyPassword)
889 nauthMethods++;
890
891 /* select auth. method */
892 {
893 const BYTE buf[] = { 5, /* SOCKS version */
894 nauthMethods, /* #of methods offered */
895 AUTH_M_NO_AUTH, AUTH_M_USR_PASS };
896
897 size_t writeLen = sizeof(buf);
898 if (nauthMethods <= 1)
899 writeLen--;
900
901 ERR_clear_error();
902 const int iwriteLen = WINPR_ASSERTING_INT_CAST(int, writeLen);
903 const int status = BIO_write(bufferedBio, buf, iwriteLen);
904
905 if (status != iwriteLen)
906 {
907 WLog_ERR(TAG, "%s SOCKS proxy: failed to write AUTH METHOD request", logprefix);
908 return FALSE;
909 }
910 }
911
912 {
913 BYTE buf[2] = WINPR_C_ARRAY_INIT;
914 const int status = recv_socks_reply(context, bufferedBio, buf, sizeof(buf), "AUTH REQ", 5);
915
916 if (status <= 0)
917 return FALSE;
918
919 switch (buf[1])
920 {
921 case AUTH_M_NO_AUTH:
922 WLog_DBG(TAG, "%s (NO AUTH) method was selected", logprefix);
923 break;
924
925 case AUTH_M_USR_PASS:
926 if (nauthMethods < 2)
927 {
928 WLog_ERR(TAG, "%s USER/PASS method was not proposed to server", logprefix);
929 return FALSE;
930 }
931 if (!socks_proxy_userpass(context, bufferedBio, proxyUsername, proxyPassword))
932 return FALSE;
933 break;
934
935 default:
936 WLog_ERR(TAG, "%s unknown method 0x%x was selected by proxy", logprefix, buf[1]);
937 return FALSE;
938 }
939 }
940 /* CONN request */
941 {
942 BYTE buf[262] = WINPR_C_ARRAY_INIT;
943 size_t offset = 0;
944 buf[offset++] = 5; /* SOCKS version */
945 buf[offset++] = SOCKS_CMD_CONNECT; /* command */
946 buf[offset++] = 0; /* 3rd octet is reserved x00 */
947
948 if (inet_pton(AF_INET6, hostname, &buf[offset + 1]) == 1)
949 {
950 buf[offset++] = SOCKS_ADDR_IPV6;
951 offset += 16;
952 }
953 else if (inet_pton(AF_INET, hostname, &buf[offset + 1]) == 1)
954 {
955 buf[offset++] = SOCKS_ADDR_IPV4;
956 offset += 4;
957 }
958 else
959 {
960 buf[offset++] = SOCKS_ADDR_FQDN;
961 buf[offset++] = WINPR_ASSERTING_INT_CAST(uint8_t, hostnlen);
962 memcpy(&buf[offset], hostname, hostnlen);
963 offset += hostnlen;
964 }
965
966 if (offset > sizeof(buf) - 2)
967 {
968 WLog_ERR(TAG, "Invalid offset %" PRIuz, offset);
969 return FALSE;
970 }
971
972 /* follows DST.PORT in netw. format */
973 buf[offset++] = (port >> 8) & 0xff;
974 buf[offset++] = port & 0xff;
975
976 ERR_clear_error();
977 const int ioffset = WINPR_ASSERTING_INT_CAST(int, offset);
978 const int status = BIO_write(bufferedBio, buf, ioffset);
979
980 if ((status < 0) || (status != ioffset))
981 {
982 WLog_ERR(TAG, "%s SOCKS proxy: failed to write CONN REQ", logprefix);
983 return FALSE;
984 }
985 }
986
987 BYTE buf[255] = WINPR_C_ARRAY_INIT;
988 const int status = recv_socks_reply(context, bufferedBio, buf, sizeof(buf), "CONN REQ", 5);
989
990 if (status < 4)
991 return FALSE;
992
993 if (buf[1] == 0)
994 {
995 WLog_INFO(TAG, "Successfully connected to %s:%" PRIu16, hostname, port);
996 return TRUE;
997 }
998
999 if ((buf[1] > 0) && (buf[1] < 9))
1000 WLog_INFO(TAG, "SOCKS Proxy replied: %s", rplstat[buf[1]]);
1001 else
1002 WLog_INFO(TAG, "SOCKS Proxy replied: %" PRIu8 " status not listed in rfc1928", buf[1]);
1003
1004 return FALSE;
1005}
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.