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