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