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 WLog_DBG(TAG, "%s => %s (%" PRIuz ")", settings->ServerHostname, current, currentlen);
310
311 if (no_proxy_match_host(current, settings->ServerHostname))
312 result = TRUE;
313 else if (no_proxy_match_ip(current, settings->ServerHostname))
314 result = TRUE;
315 }
316
317 current = strtok_s(nullptr, delimiter, &context);
318 }
319
320 free(copy);
321 return result;
322}
323
324void proxy_read_environment(rdpSettings* settings, char* envname)
325{
326 const DWORD envlen = GetEnvironmentVariableA(envname, nullptr, 0);
327
328 if (!envlen || (envlen <= 1))
329 return;
330
331 char* env = calloc(1, envlen);
332
333 if (!env)
334 {
335 WLog_ERR(TAG, "Not enough memory");
336 return;
337 }
338
339 if (GetEnvironmentVariableA(envname, env, envlen) == envlen - 1)
340 {
341 if (_strnicmp("NO_PROXY", envname, 9) == 0)
342 {
343 if (check_no_proxy(settings, env))
344 {
345 WLog_INFO(TAG, "deactivating proxy: %s [%s=%s]",
346 freerdp_settings_get_string(settings, FreeRDP_ServerHostname), envname,
347 env);
348 if (!freerdp_settings_set_uint32(settings, FreeRDP_ProxyType, PROXY_TYPE_NONE))
349 WLog_WARN(TAG, "failed to set FreeRDP_ProxyType=PROXY_TYPE_NONE");
350 }
351 }
352 else
353 {
354 if (!proxy_parse_uri(settings, env))
355 {
356 WLog_WARN(
357 TAG, "Error while parsing proxy URI from environment variable; ignoring proxy");
358 }
359 }
360 }
361
362 free(env);
363}
364
365BOOL proxy_parse_uri(rdpSettings* settings, const char* uri_in)
366{
367 BOOL rc = FALSE;
368 const char* protocol = "";
369 UINT16 port = 0;
370
371 if (!settings || !uri_in)
372 return FALSE;
373
374 char* uri_copy = _strdup(uri_in);
375 char* uri = uri_copy;
376 if (!uri)
377 goto fail;
378
379 {
380 char* p = strstr(uri, "://");
381 if (p)
382 {
383 *p = '\0';
384
385 if (_stricmp("no_proxy", uri) == 0)
386 {
387 if (!freerdp_settings_set_uint32(settings, FreeRDP_ProxyType, PROXY_TYPE_IGNORE))
388 goto fail;
389 }
390 if (_stricmp("http", uri) == 0)
391 {
392 if (!freerdp_settings_set_uint32(settings, FreeRDP_ProxyType, PROXY_TYPE_HTTP))
393 goto fail;
394 protocol = "http";
395 }
396 else if (_stricmp("socks5", uri) == 0)
397 {
398 if (!freerdp_settings_set_uint32(settings, FreeRDP_ProxyType, PROXY_TYPE_SOCKS))
399 goto fail;
400 protocol = "socks5";
401 }
402 else
403 {
404 WLog_ERR(TAG, "Only HTTP and SOCKS5 proxies supported by now");
405 goto fail;
406 }
407
408 uri = p + 3;
409 }
410 else
411 {
412 /* default proxy protocol is http */
413 if (!freerdp_settings_set_uint32(settings, FreeRDP_ProxyType, PROXY_TYPE_HTTP))
414 goto fail;
415 protocol = "http";
416 }
417 }
418
419 /* uri is now [user:password@]hostname:port */
420 {
421 char* atPtr = strrchr(uri, '@');
422
423 if (atPtr)
424 {
425 /* got a login / password,
426 * atPtr
427 * v
428 * [user:password@]hostname:port
429 * ^
430 * colonPtr
431 */
432 char* colonPtr = strchr(uri, ':');
433
434 if (!colonPtr || (colonPtr > atPtr))
435 {
436 WLog_ERR(TAG, "invalid syntax for proxy (contains no password)");
437 goto fail;
438 }
439
440 *colonPtr = '\0';
441 if (!freerdp_settings_set_string(settings, FreeRDP_ProxyUsername, uri))
442 {
443 WLog_ERR(TAG, "unable to allocate proxy username");
444 goto fail;
445 }
446
447 *atPtr = '\0';
448
449 if (!freerdp_settings_set_string(settings, FreeRDP_ProxyPassword, colonPtr + 1))
450 {
451 WLog_ERR(TAG, "unable to allocate proxy password");
452 goto fail;
453 }
454
455 uri = atPtr + 1;
456 }
457 }
458
459 {
460 char* p = strchr(uri, ':');
461
462 if (p)
463 {
464 LONGLONG val = 0;
465
466 if (!value_to_int(&p[1], &val, 0, UINT16_MAX))
467 {
468 WLog_ERR(TAG, "invalid syntax for proxy (invalid port)");
469 goto fail;
470 }
471
472 if (val == 0)
473 {
474 WLog_ERR(TAG, "invalid syntax for proxy (port missing)");
475 goto fail;
476 }
477
478 port = (UINT16)val;
479 *p = '\0';
480 }
481 else
482 {
483 if (_stricmp("http", protocol) == 0)
484 {
485 /* The default is 80. Also for Proxies. */
486 port = 80;
487 }
488 else
489 {
490 port = 1080;
491 }
492
493 WLog_DBG(TAG, "setting default proxy port: %" PRIu16, port);
494 }
495
496 if (!freerdp_settings_set_uint16(settings, FreeRDP_ProxyPort, port))
497 goto fail;
498 }
499 {
500 char* p = strchr(uri, '/');
501 if (p)
502 *p = '\0';
503 if (!freerdp_settings_set_string(settings, FreeRDP_ProxyHostname, uri))
504 goto fail;
505 }
506
507 if (_stricmp("", uri) == 0)
508 {
509 WLog_ERR(TAG, "invalid syntax for proxy (hostname missing)");
510 goto fail;
511 }
512
513 if (freerdp_settings_get_string(settings, FreeRDP_ProxyUsername))
514 {
515 WLog_INFO(TAG, "Parsed proxy configuration: %s://%s:%s@%s:%" PRIu16, protocol,
516 freerdp_settings_get_string(settings, FreeRDP_ProxyUsername), "******",
517 freerdp_settings_get_string(settings, FreeRDP_ProxyHostname),
518 freerdp_settings_get_uint16(settings, FreeRDP_ProxyPort));
519 }
520 else
521 {
522 WLog_INFO(TAG, "Parsed proxy configuration: %s://%s:%" PRIu16, protocol,
523 freerdp_settings_get_string(settings, FreeRDP_ProxyHostname),
524 freerdp_settings_get_uint16(settings, FreeRDP_ProxyPort));
525 }
526 rc = TRUE;
527
528fail:
529 if (!rc)
530 WLog_WARN(TAG, "Failed to parse proxy configuration: %s://%s:%" PRIu16, protocol, uri,
531 port);
532 free(uri_copy);
533 return rc;
534}
535
536BOOL proxy_connect(rdpContext* context, BIO* bufferedBio, const char* proxyUsername,
537 const char* proxyPassword, const char* hostname, UINT16 port)
538{
539 WINPR_ASSERT(context);
540 rdpSettings* settings = context->settings;
541
542 switch (freerdp_settings_get_uint32(settings, FreeRDP_ProxyType))
543 {
544 case PROXY_TYPE_NONE:
545 case PROXY_TYPE_IGNORE:
546 return TRUE;
547
548 case PROXY_TYPE_HTTP:
549 return http_proxy_connect(context, bufferedBio, proxyUsername, proxyPassword, hostname,
550 port);
551
552 case PROXY_TYPE_SOCKS:
553 return socks_proxy_connect(context, bufferedBio, proxyUsername, proxyPassword, hostname,
554 port);
555
556 default:
557 WLog_ERR(TAG, "Invalid internal proxy configuration");
558 return FALSE;
559 }
560}
561
562static const char* get_response_header(char* response)
563{
564 char* current_pos = strchr(response, '\r');
565 if (!current_pos)
566 current_pos = strchr(response, '\n');
567
568 if (current_pos)
569 *current_pos = '\0';
570
571 return response;
572}
573
574static BOOL http_proxy_connect(rdpContext* context, BIO* bufferedBio, const char* proxyUsername,
575 const char* proxyPassword, const char* hostname, UINT16 port)
576{
577 BOOL rc = FALSE;
578 int status = 0;
579 wStream* s = nullptr;
580 char port_str[10] = WINPR_C_ARRAY_INIT;
581 char recv_buf[256] = WINPR_C_ARRAY_INIT;
582 char* eol = nullptr;
583 size_t resultsize = 0;
584 size_t reserveSize = 0;
585 size_t portLen = 0;
586 size_t hostLen = 0;
587 const char connect[] = "CONNECT ";
588 const char httpheader[] = " HTTP/1.1" CRLF "Host: ";
589
590 WINPR_ASSERT(context);
591 WINPR_ASSERT(bufferedBio);
592 WINPR_ASSERT(hostname);
593 const UINT32 timeout =
594 freerdp_settings_get_uint32(context->settings, FreeRDP_TcpConnectTimeout);
595
596 if (_itoa_s(port, port_str, sizeof(port_str), 10) < 0)
597 return FALSE;
598
599 hostLen = strlen(hostname);
600 portLen = strnlen(port_str, sizeof(port_str));
601 reserveSize = strlen(connect) + (hostLen + 1ull + portLen) * 2ull + strlen(httpheader);
602 s = Stream_New(nullptr, reserveSize);
603 if (!s)
604 goto fail;
605
606 Stream_Write(s, connect, strlen(connect));
607 Stream_Write(s, hostname, hostLen);
608 Stream_Write_UINT8(s, ':');
609 Stream_Write(s, port_str, portLen);
610 Stream_Write(s, httpheader, strlen(httpheader));
611 Stream_Write(s, hostname, hostLen);
612 Stream_Write_UINT8(s, ':');
613 Stream_Write(s, port_str, portLen);
614
615 if (proxyUsername && proxyPassword)
616 {
617 const int length = _scprintf("%s:%s", proxyUsername, proxyPassword);
618 if (length > 0)
619 {
620 const size_t size = (size_t)length + 1ull;
621 char* creds = (char*)malloc(size);
622
623 if (!creds)
624 goto fail;
625 else
626 {
627 const char basic[] = CRLF "Proxy-Authorization: Basic ";
628 char* base64 = nullptr;
629
630 (void)sprintf_s(creds, size, "%s:%s", proxyUsername, proxyPassword);
631 base64 = crypto_base64_encode((const BYTE*)creds, size - 1);
632
633 if (!base64 || !Stream_EnsureRemainingCapacity(s, strlen(basic) + strlen(base64)))
634 {
635 free(base64);
636 free(creds);
637 goto fail;
638 }
639 Stream_Write(s, basic, strlen(basic));
640 Stream_Write(s, base64, strlen(base64));
641
642 free(base64);
643 }
644 free(creds);
645 }
646 }
647
648 if (!Stream_EnsureRemainingCapacity(s, 4))
649 goto fail;
650
651 Stream_Write(s, CRLF CRLF, 4);
652 ERR_clear_error();
653
654 {
655 const size_t pos = Stream_GetPosition(s);
656 if (pos > INT32_MAX)
657 goto fail;
658
659 status = BIO_write(bufferedBio, Stream_Buffer(s), WINPR_ASSERTING_INT_CAST(int, pos));
660 }
661
662 if ((status < 0) || ((size_t)status != Stream_GetPosition(s)))
663 {
664 WLog_ERR(TAG, "HTTP proxy: failed to write CONNECT request");
665 goto fail;
666 }
667
668 /* Read result until CR-LF-CR-LF.
669 * Keep recv_buf a null-terminated string. */
670 {
671 const UINT64 start = GetTickCount64();
672 while (strstr(recv_buf, CRLF CRLF) == nullptr)
673 {
674 if (resultsize >= sizeof(recv_buf) - 1)
675 {
676 WLog_ERR(TAG, "HTTP Reply headers too long: %s", get_response_header(recv_buf));
677 goto fail;
678 }
679 const size_t rdsize = sizeof(recv_buf) - resultsize - 1ULL;
680
681 ERR_clear_error();
682
683 WINPR_ASSERT(rdsize <= INT32_MAX);
684 status = BIO_read(bufferedBio, (BYTE*)recv_buf + resultsize, (int)rdsize);
685
686 if (status < 0)
687 {
688 /* Error? */
689 if (!freerdp_shall_disconnect_context(context) && BIO_should_retry(bufferedBio))
690 {
691 USleep(100);
692 continue;
693 }
694
695 WLog_ERR(TAG, "Failed reading reply from HTTP proxy (Status %d)", status);
696 goto fail;
697 }
698 else if (status == 0)
699 {
700 const UINT64 now = GetTickCount64();
701 const UINT64 diff = now - start;
702 if (freerdp_shall_disconnect_context(context) || (now < start) || (diff > timeout))
703 {
704 /* Error? */
705 WLog_ERR(TAG, "Failed reading reply from HTTP proxy (BIO_read returned zero)");
706 goto fail;
707 }
708 Sleep(10);
709 }
710
711 resultsize += WINPR_ASSERTING_INT_CAST(size_t, status);
712 }
713 }
714
715 /* Extract HTTP status line */
716 eol = strchr(recv_buf, '\r');
717
718 if (!eol)
719 {
720 /* should never happen */
721 goto fail;
722 }
723
724 *eol = '\0';
725 WLog_INFO(TAG, "HTTP Proxy: %s", recv_buf);
726
727 if (strnlen(recv_buf, sizeof(recv_buf)) < 12)
728 goto fail;
729
730 recv_buf[7] = 'X';
731
732 if (strncmp(recv_buf, "HTTP/1.X 200", 12) != 0)
733 goto fail;
734
735 rc = TRUE;
736fail:
737 Stream_Free(s, TRUE);
738 return rc;
739}
740
741static int recv_socks_reply(rdpContext* context, BIO* bufferedBio, BYTE* buf, int len, char* reason,
742 BYTE checkVer)
743{
744 int status = 0;
745
746 WINPR_ASSERT(context);
747
748 const UINT32 timeout =
749 freerdp_settings_get_uint32(context->settings, FreeRDP_TcpConnectTimeout);
750 const UINT64 start = GetTickCount64();
751 for (;;)
752 {
753 ERR_clear_error();
754 status = BIO_read(bufferedBio, buf, len);
755
756 if (status > 0)
757 {
758 break;
759 }
760 else if (status < 0)
761 {
762 /* Error? */
763 if (!freerdp_shall_disconnect_context(context) && BIO_should_retry(bufferedBio))
764 {
765 USleep(100);
766 continue;
767 }
768
769 WLog_ERR(TAG, "Failed reading %s reply from SOCKS proxy (Status %d)", reason, status);
770 return -1;
771 }
772 else if (status == 0)
773 {
774 const UINT64 now = GetTickCount64();
775 const UINT64 diff = now - start;
776 if (freerdp_shall_disconnect_context(context) || (now < start) || (diff > timeout))
777 {
778 /* Error? */
779 WLog_ERR(TAG, "Failed reading %s reply from SOCKS proxy (BIO_read returned zero)",
780 reason);
781 return status;
782 }
783 Sleep(10);
784 }
785 else // if (status == 0)
786 {
787 /* Error? */
788 WLog_ERR(TAG, "Failed reading %s reply from SOCKS proxy (BIO_read returned zero)",
789 reason);
790 return -1;
791 }
792 }
793
794 if (status < 2)
795 {
796 WLog_ERR(TAG, "SOCKS Proxy reply packet too short (%s)", reason);
797 return -1;
798 }
799
800 if (buf[0] != checkVer)
801 {
802 WLog_ERR(TAG, "SOCKS Proxy version is not 5 (%s)", reason);
803 return -1;
804 }
805
806 return status;
807}
808
809static BOOL socks_proxy_userpass(rdpContext* context, BIO* bufferedBio, const char* proxyUsername,
810 const char* proxyPassword)
811{
812 WINPR_ASSERT(context);
813 WINPR_ASSERT(bufferedBio);
814
815 if (!proxyUsername || !proxyPassword)
816 {
817 WLog_ERR(TAG, "%s invalid username (%p) or password (%p)", logprefix,
818 WINPR_CXX_COMPAT_CAST(const void*, proxyUsername),
819 WINPR_CXX_COMPAT_CAST(const void*, proxyPassword));
820 return FALSE;
821 }
822
823 const size_t usernameLen = (BYTE)strnlen(proxyUsername, 256);
824 if (usernameLen > 255)
825 {
826 WLog_ERR(TAG, "%s username too long (%" PRIuz ", max=255)", logprefix, usernameLen);
827 return FALSE;
828 }
829
830 const size_t userpassLen = (BYTE)strnlen(proxyPassword, 256);
831 if (userpassLen > 255)
832 {
833 WLog_ERR(TAG, "%s password too long (%" PRIuz ", max=255)", logprefix, userpassLen);
834 return FALSE;
835 }
836
837 /* user/password v1 method */
838 {
839 BYTE buf[2 * 255 + 3] = WINPR_C_ARRAY_INIT;
840 size_t offset = 0;
841 buf[offset++] = 1;
842
843 buf[offset++] = WINPR_ASSERTING_INT_CAST(uint8_t, usernameLen);
844 memcpy(&buf[offset], proxyUsername, usernameLen);
845 offset += usernameLen;
846
847 buf[offset++] = WINPR_ASSERTING_INT_CAST(uint8_t, userpassLen);
848 memcpy(&buf[offset], proxyPassword, userpassLen);
849 offset += userpassLen;
850
851 ERR_clear_error();
852 const int ioffset = WINPR_ASSERTING_INT_CAST(int, offset);
853 const int status = BIO_write(bufferedBio, buf, ioffset);
854
855 if (status != ioffset)
856 {
857 WLog_ERR(TAG, "%s error writing user/password request", logprefix);
858 return FALSE;
859 }
860 }
861
862 BYTE buf[2] = WINPR_C_ARRAY_INIT;
863 const int status = recv_socks_reply(context, bufferedBio, buf, sizeof(buf), "AUTH REQ", 1);
864
865 if (status < 2)
866 return FALSE;
867
868 if (buf[1] != 0x00)
869 {
870 WLog_ERR(TAG, "%s invalid user/password", logprefix);
871 return FALSE;
872 }
873 return TRUE;
874}
875
876static BOOL socks_proxy_connect(rdpContext* context, BIO* bufferedBio, const char* proxyUsername,
877 const char* proxyPassword, const char* hostname, UINT16 port)
878{
879 BYTE nauthMethods = 1;
880 const size_t hostnlen = strnlen(hostname, 255);
881
882 if (proxyUsername || proxyPassword)
883 nauthMethods++;
884
885 /* select auth. method */
886 {
887 const BYTE buf[] = { 5, /* SOCKS version */
888 nauthMethods, /* #of methods offered */
889 AUTH_M_NO_AUTH, AUTH_M_USR_PASS };
890
891 size_t writeLen = sizeof(buf);
892 if (nauthMethods <= 1)
893 writeLen--;
894
895 ERR_clear_error();
896 const int iwriteLen = WINPR_ASSERTING_INT_CAST(int, writeLen);
897 const int status = BIO_write(bufferedBio, buf, iwriteLen);
898
899 if (status != iwriteLen)
900 {
901 WLog_ERR(TAG, "%s SOCKS proxy: failed to write AUTH METHOD request", logprefix);
902 return FALSE;
903 }
904 }
905
906 {
907 BYTE buf[2] = WINPR_C_ARRAY_INIT;
908 const int status = recv_socks_reply(context, bufferedBio, buf, sizeof(buf), "AUTH REQ", 5);
909
910 if (status <= 0)
911 return FALSE;
912
913 switch (buf[1])
914 {
915 case AUTH_M_NO_AUTH:
916 WLog_DBG(TAG, "%s (NO AUTH) method was selected", logprefix);
917 break;
918
919 case AUTH_M_USR_PASS:
920 if (nauthMethods < 2)
921 {
922 WLog_ERR(TAG, "%s USER/PASS method was not proposed to server", logprefix);
923 return FALSE;
924 }
925 if (!socks_proxy_userpass(context, bufferedBio, proxyUsername, proxyPassword))
926 return FALSE;
927 break;
928
929 default:
930 WLog_ERR(TAG, "%s unknown method 0x%x was selected by proxy", logprefix, buf[1]);
931 return FALSE;
932 }
933 }
934 /* CONN request */
935 {
936 BYTE buf[262] = WINPR_C_ARRAY_INIT;
937 size_t offset = 0;
938 buf[offset++] = 5; /* SOCKS version */
939 buf[offset++] = SOCKS_CMD_CONNECT; /* command */
940 buf[offset++] = 0; /* 3rd octet is reserved x00 */
941
942 if (inet_pton(AF_INET6, hostname, &buf[offset + 1]) == 1)
943 {
944 buf[offset++] = SOCKS_ADDR_IPV6;
945 offset += 16;
946 }
947 else if (inet_pton(AF_INET, hostname, &buf[offset + 1]) == 1)
948 {
949 buf[offset++] = SOCKS_ADDR_IPV4;
950 offset += 4;
951 }
952 else
953 {
954 buf[offset++] = SOCKS_ADDR_FQDN;
955 buf[offset++] = WINPR_ASSERTING_INT_CAST(uint8_t, hostnlen);
956 memcpy(&buf[offset], hostname, hostnlen);
957 offset += hostnlen;
958 }
959
960 if (offset > sizeof(buf) - 2)
961 return FALSE;
962
963 /* follows DST.PORT in netw. format */
964 buf[offset++] = (port >> 8) & 0xff;
965 buf[offset++] = port & 0xff;
966
967 ERR_clear_error();
968 const int ioffset = WINPR_ASSERTING_INT_CAST(int, offset);
969 const int status = BIO_write(bufferedBio, buf, ioffset);
970
971 if ((status < 0) || (status != ioffset))
972 {
973 WLog_ERR(TAG, "%s SOCKS proxy: failed to write CONN REQ", logprefix);
974 return FALSE;
975 }
976 }
977
978 BYTE buf[255] = WINPR_C_ARRAY_INIT;
979 const int status = recv_socks_reply(context, bufferedBio, buf, sizeof(buf), "CONN REQ", 5);
980
981 if (status < 4)
982 return FALSE;
983
984 if (buf[1] == 0)
985 {
986 WLog_INFO(TAG, "Successfully connected to %s:%" PRIu16, hostname, port);
987 return TRUE;
988 }
989
990 if ((buf[1] > 0) && (buf[1] < 9))
991 WLog_INFO(TAG, "SOCKS Proxy replied: %s", rplstat[buf[1]]);
992 else
993 WLog_INFO(TAG, "SOCKS Proxy replied: %" PRIu8 " status not listed in rfc1928", buf[1]);
994
995 return FALSE;
996}
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 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 UINT32 freerdp_settings_get_uint32(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id)
Returns a UINT32 settings value.
FREERDP_API BOOL freerdp_settings_set_uint16(rdpSettings *settings, FreeRDP_Settings_Keys_UInt16 id, UINT16 val)
Sets a UINT16 settings value.
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.