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