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