FreeRDP
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Modules Pages
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
64/* CONN REQ replies in enum. order */
65static const char* rplstat[] = { "succeeded",
66 "general SOCKS server failure",
67 "connection not allowed by ruleset",
68 "Network unreachable",
69 "Host unreachable",
70 "Connection refused",
71 "TTL expired",
72 "Command not supported",
73 "Address type not supported" };
74
75static BOOL http_proxy_connect(rdpContext* context, BIO* bufferedBio, const char* proxyUsername,
76 const char* proxyPassword, const char* hostname, UINT16 port);
77static BOOL socks_proxy_connect(rdpContext* context, BIO* bufferedBio, const char* proxyUsername,
78 const char* proxyPassword, const char* hostname, UINT16 port);
79static void proxy_read_environment(rdpSettings* settings, char* envname);
80
81BOOL proxy_prepare(rdpSettings* settings, const char** lpPeerHostname, UINT16* lpPeerPort,
82 const char** lpProxyUsername, const char** lpProxyPassword)
83{
84 if (freerdp_settings_get_uint32(settings, FreeRDP_ProxyType) == PROXY_TYPE_IGNORE)
85 return FALSE;
86
87 /* For TSGateway, find the system HTTPS proxy automatically */
88 if (freerdp_settings_get_uint32(settings, FreeRDP_ProxyType) == PROXY_TYPE_NONE)
89 proxy_read_environment(settings, "https_proxy");
90
91 if (freerdp_settings_get_uint32(settings, FreeRDP_ProxyType) == PROXY_TYPE_NONE)
92 proxy_read_environment(settings, "HTTPS_PROXY");
93
94 if (freerdp_settings_get_uint32(settings, FreeRDP_ProxyType) != PROXY_TYPE_NONE)
95 proxy_read_environment(settings, "no_proxy");
96
97 if (freerdp_settings_get_uint32(settings, FreeRDP_ProxyType) != PROXY_TYPE_NONE)
98 proxy_read_environment(settings, "NO_PROXY");
99
100 if (freerdp_settings_get_uint32(settings, FreeRDP_ProxyType) != PROXY_TYPE_NONE)
101 {
102 *lpPeerHostname = freerdp_settings_get_string(settings, FreeRDP_ProxyHostname);
103 *lpPeerPort = freerdp_settings_get_uint16(settings, FreeRDP_ProxyPort);
104 *lpProxyUsername = freerdp_settings_get_string(settings, FreeRDP_ProxyUsername);
105 *lpProxyPassword = freerdp_settings_get_string(settings, FreeRDP_ProxyPassword);
106 return TRUE;
107 }
108
109 return FALSE;
110}
111
112static BOOL value_to_int(const char* value, LONGLONG* result, LONGLONG min, LONGLONG max)
113{
114 long long rc = 0;
115
116 if (!value || !result)
117 return FALSE;
118
119 errno = 0;
120 rc = _strtoi64(value, NULL, 0);
121
122 if (errno != 0)
123 return FALSE;
124
125 if ((rc < min) || (rc > max))
126 return FALSE;
127
128 *result = rc;
129 return TRUE;
130}
131
132static BOOL cidr4_match(const struct in_addr* addr, const struct in_addr* net, BYTE bits)
133{
134 if (bits == 0)
135 return TRUE;
136
137 const uint32_t mask = htonl(0xFFFFFFFFu << (32 - bits));
138 const uint32_t amask = addr->s_addr & mask;
139 const uint32_t nmask = net->s_addr & mask;
140 return amask == nmask;
141}
142
143static BOOL cidr6_match(const struct in6_addr* address, const struct in6_addr* network,
144 uint8_t bits)
145{
146 const uint32_t* a = (const uint32_t*)address;
147 const uint32_t* n = (const uint32_t*)network;
148 const size_t bits_whole = bits >> 5;
149 const size_t bits_incomplete = bits & 0x1F;
150
151 if (bits_whole)
152 {
153 if (memcmp(a, n, bits_whole << 2) != 0)
154 return FALSE;
155 }
156
157 if (bits_incomplete)
158 {
159 uint32_t mask = htonl((0xFFFFFFFFu) << (32 - bits_incomplete));
160
161 if ((a[bits_whole] ^ n[bits_whole]) & mask)
162 return FALSE;
163 }
164
165 return TRUE;
166}
167
168static BOOL option_ends_with(const char* str, const char* ext)
169{
170 WINPR_ASSERT(str);
171 WINPR_ASSERT(ext);
172 const size_t strLen = strlen(str);
173 const size_t extLen = strlen(ext);
174
175 if (strLen < extLen)
176 return FALSE;
177
178 return _strnicmp(&str[strLen - extLen], ext, extLen) == 0;
179}
180
181/* no_proxy has no proper definition, so use curl as reference:
182 * https://about.gitlab.com/blog/2021/01/27/we-need-to-talk-no-proxy/
183 */
184static BOOL no_proxy_match_host(const char* val, const char* hostname)
185{
186 WINPR_ASSERT(val);
187 WINPR_ASSERT(hostname);
188
189 /* match all */
190 if (_stricmp("*", val) == 0)
191 return TRUE;
192
193 /* Strip leading . */
194 if (val[0] == '.')
195 val++;
196
197 /* Match suffix */
198 return option_ends_with(hostname, val);
199}
200
201static BOOL starts_with(const char* val, const char* prefix)
202{
203 const size_t plen = strlen(prefix);
204 const size_t vlen = strlen(val);
205 if (vlen < plen)
206 return FALSE;
207 return _strnicmp(val, prefix, plen) == 0;
208}
209
210static BOOL no_proxy_match_ip(const char* val, const char* hostname)
211{
212 WINPR_ASSERT(val);
213 WINPR_ASSERT(hostname);
214
215 struct sockaddr_in sa4 = { 0 };
216 struct sockaddr_in6 sa6 = { 0 };
217
218 if (inet_pton(AF_INET, hostname, &sa4.sin_addr) == 1)
219 {
220 /* Prefix match */
221 if (starts_with(hostname, val))
222 return TRUE;
223
224 char* sub = strchr(val, '/');
225 if (sub)
226 *sub++ = '\0';
227
228 struct sockaddr_in mask = { 0 };
229 if (inet_pton(AF_INET, val, &mask.sin_addr) == 0)
230 return FALSE;
231
232 /* IP address match */
233 if (memcmp(&mask, &sa4, sizeof(mask)) == 0)
234 return TRUE;
235
236 if (sub)
237 {
238 const unsigned long usub = strtoul(sub, NULL, 0);
239 if ((errno == 0) && (usub <= UINT8_MAX))
240 return cidr4_match(&sa4.sin_addr, &mask.sin_addr, (UINT8)usub);
241 }
242 }
243 else if (inet_pton(AF_INET6, hostname, &sa6.sin6_addr) == 1)
244 {
245 if (val[0] == '[')
246 val++;
247
248 char str[INET6_ADDRSTRLEN + 1] = { 0 };
249 strncpy(str, val, INET6_ADDRSTRLEN);
250
251 const size_t len = strnlen(str, INET6_ADDRSTRLEN);
252 if (len > 0)
253 {
254 if (str[len - 1] == ']')
255 str[len - 1] = '\0';
256 }
257
258 /* Prefix match */
259 if (starts_with(hostname, str))
260 return TRUE;
261
262 char* sub = strchr(str, '/');
263 if (sub)
264 *sub++ = '\0';
265
266 struct sockaddr_in6 mask = { 0 };
267 if (inet_pton(AF_INET6, str, &mask.sin6_addr) == 0)
268 return FALSE;
269
270 /* Address match */
271 if (memcmp(&mask, &sa6, sizeof(mask)) == 0)
272 return TRUE;
273
274 if (sub)
275 {
276 const unsigned long usub = strtoul(sub, NULL, 0);
277 if ((errno == 0) && (usub <= UINT8_MAX))
278 return cidr6_match(&sa6.sin6_addr, &mask.sin6_addr, (UINT8)usub);
279 }
280 }
281
282 return FALSE;
283}
284
285static BOOL check_no_proxy(rdpSettings* settings, const char* no_proxy)
286{
287 const char* delimiter = ", ";
288 BOOL result = FALSE;
289 char* context = NULL;
290
291 if (!no_proxy || !settings)
292 return FALSE;
293
294 char* copy = _strdup(no_proxy);
295
296 if (!copy)
297 return FALSE;
298
299 char* current = strtok_s(copy, delimiter, &context);
300
301 while (current && !result)
302 {
303 const size_t currentlen = strlen(current);
304
305 if (currentlen > 0)
306 {
307 WLog_DBG(TAG, "%s => %s (%" PRIdz ")", settings->ServerHostname, current, currentlen);
308
309 if (no_proxy_match_host(current, settings->ServerHostname))
310 result = TRUE;
311 else if (no_proxy_match_ip(current, settings->ServerHostname))
312 result = TRUE;
313 }
314
315 current = strtok_s(NULL, delimiter, &context);
316 }
317
318 free(copy);
319 return result;
320}
321
322void proxy_read_environment(rdpSettings* settings, char* envname)
323{
324 const DWORD envlen = GetEnvironmentVariableA(envname, NULL, 0);
325
326 if (!envlen || (envlen <= 1))
327 return;
328
329 char* env = calloc(1, envlen);
330
331 if (!env)
332 {
333 WLog_ERR(TAG, "Not enough memory");
334 return;
335 }
336
337 if (GetEnvironmentVariableA(envname, env, envlen) == envlen - 1)
338 {
339 if (_strnicmp("NO_PROXY", envname, 9) == 0)
340 {
341 if (check_no_proxy(settings, env))
342 {
343 WLog_INFO(TAG, "deactivating proxy: %s [%s=%s]",
344 freerdp_settings_get_string(settings, FreeRDP_ServerHostname), envname,
345 env);
346 if (!freerdp_settings_set_uint32(settings, FreeRDP_ProxyType, PROXY_TYPE_NONE))
347 WLog_WARN(TAG, "failed to set FreeRDP_ProxyType=PROXY_TYPE_NONE");
348 }
349 }
350 else
351 {
352 if (!proxy_parse_uri(settings, env))
353 {
354 WLog_WARN(
355 TAG, "Error while parsing proxy URI from environment variable; ignoring proxy");
356 }
357 }
358 }
359
360 free(env);
361}
362
363BOOL proxy_parse_uri(rdpSettings* settings, const char* uri_in)
364{
365 BOOL rc = FALSE;
366 const char* protocol = "";
367 UINT16 port = 0;
368
369 if (!settings || !uri_in)
370 return FALSE;
371
372 char* uri_copy = _strdup(uri_in);
373 char* uri = uri_copy;
374 if (!uri)
375 goto fail;
376
377 char* p = strstr(uri, "://");
378
379 if (p)
380 {
381 *p = '\0';
382
383 if (_stricmp("no_proxy", uri) == 0)
384 {
385 if (!freerdp_settings_set_uint32(settings, FreeRDP_ProxyType, PROXY_TYPE_IGNORE))
386 goto fail;
387 }
388 if (_stricmp("http", uri) == 0)
389 {
390 if (!freerdp_settings_set_uint32(settings, FreeRDP_ProxyType, PROXY_TYPE_HTTP))
391 goto fail;
392 protocol = "http";
393 }
394 else if (_stricmp("socks5", uri) == 0)
395 {
396 if (!freerdp_settings_set_uint32(settings, FreeRDP_ProxyType, PROXY_TYPE_SOCKS))
397 goto fail;
398 protocol = "socks5";
399 }
400 else
401 {
402 WLog_ERR(TAG, "Only HTTP and SOCKS5 proxies supported by now");
403 goto fail;
404 }
405
406 uri = p + 3;
407 }
408 else
409 {
410 /* default proxy protocol is http */
411 if (!freerdp_settings_set_uint32(settings, FreeRDP_ProxyType, PROXY_TYPE_HTTP))
412 goto fail;
413 protocol = "http";
414 }
415
416 /* uri is now [user:password@]hostname:port */
417 char* atPtr = strrchr(uri, '@');
418
419 if (atPtr)
420 {
421 /* got a login / password,
422 * atPtr
423 * v
424 * [user:password@]hostname:port
425 * ^
426 * colonPtr
427 */
428 char* colonPtr = strchr(uri, ':');
429
430 if (!colonPtr || (colonPtr > atPtr))
431 {
432 WLog_ERR(TAG, "invalid syntax for proxy (contains no password)");
433 goto fail;
434 }
435
436 *colonPtr = '\0';
437 if (!freerdp_settings_set_string(settings, FreeRDP_ProxyUsername, uri))
438 {
439 WLog_ERR(TAG, "unable to allocate proxy username");
440 goto fail;
441 }
442
443 *atPtr = '\0';
444
445 if (!freerdp_settings_set_string(settings, FreeRDP_ProxyPassword, colonPtr + 1))
446 {
447 WLog_ERR(TAG, "unable to allocate proxy password");
448 goto fail;
449 }
450
451 uri = atPtr + 1;
452 }
453
454 p = strchr(uri, ':');
455
456 if (p)
457 {
458 LONGLONG val = 0;
459
460 if (!value_to_int(&p[1], &val, 0, UINT16_MAX))
461 {
462 WLog_ERR(TAG, "invalid syntax for proxy (invalid port)");
463 goto fail;
464 }
465
466 if (val == 0)
467 {
468 WLog_ERR(TAG, "invalid syntax for proxy (port missing)");
469 goto fail;
470 }
471
472 port = (UINT16)val;
473 *p = '\0';
474 }
475 else
476 {
477 if (_stricmp("http", protocol) == 0)
478 {
479 /* The default is 80. Also for Proxies. */
480 port = 80;
481 }
482 else
483 {
484 port = 1080;
485 }
486
487 WLog_DBG(TAG, "setting default proxy port: %" PRIu16, port);
488 }
489
490 if (!freerdp_settings_set_uint16(settings, FreeRDP_ProxyPort, port))
491 goto fail;
492
493 p = strchr(uri, '/');
494 if (p)
495 *p = '\0';
496 if (!freerdp_settings_set_string(settings, FreeRDP_ProxyHostname, uri))
497 goto fail;
498
499 if (_stricmp("", uri) == 0)
500 {
501 WLog_ERR(TAG, "invalid syntax for proxy (hostname missing)");
502 goto fail;
503 }
504
505 if (freerdp_settings_get_string(settings, FreeRDP_ProxyUsername))
506 {
507 WLog_INFO(TAG, "Parsed proxy configuration: %s://%s:%s@%s:%" PRIu16, protocol,
508 freerdp_settings_get_string(settings, FreeRDP_ProxyUsername), "******",
509 freerdp_settings_get_string(settings, FreeRDP_ProxyHostname),
510 freerdp_settings_get_uint16(settings, FreeRDP_ProxyPort));
511 }
512 else
513 {
514 WLog_INFO(TAG, "Parsed proxy configuration: %s://%s:%" PRIu16, protocol,
515 freerdp_settings_get_string(settings, FreeRDP_ProxyHostname),
516 freerdp_settings_get_uint16(settings, FreeRDP_ProxyPort));
517 }
518 rc = TRUE;
519
520fail:
521 if (!rc)
522 WLog_WARN(TAG, "Failed to parse proxy configuration: %s://%s:%" PRIu16, protocol, uri,
523 port);
524 free(uri_copy);
525 return rc;
526}
527
528BOOL proxy_connect(rdpContext* context, BIO* bufferedBio, const char* proxyUsername,
529 const char* proxyPassword, const char* hostname, UINT16 port)
530{
531 WINPR_ASSERT(context);
532 rdpSettings* settings = context->settings;
533
534 switch (freerdp_settings_get_uint32(settings, FreeRDP_ProxyType))
535 {
536 case PROXY_TYPE_NONE:
537 case PROXY_TYPE_IGNORE:
538 return TRUE;
539
540 case PROXY_TYPE_HTTP:
541 return http_proxy_connect(context, bufferedBio, proxyUsername, proxyPassword, hostname,
542 port);
543
544 case PROXY_TYPE_SOCKS:
545 return socks_proxy_connect(context, bufferedBio, proxyUsername, proxyPassword, hostname,
546 port);
547
548 default:
549 WLog_ERR(TAG, "Invalid internal proxy configuration");
550 return FALSE;
551 }
552}
553
554static const char* get_response_header(char* response)
555{
556 char* current_pos = strchr(response, '\r');
557 if (!current_pos)
558 current_pos = strchr(response, '\n');
559
560 if (current_pos)
561 *current_pos = '\0';
562
563 return response;
564}
565
566static BOOL http_proxy_connect(rdpContext* context, BIO* bufferedBio, const char* proxyUsername,
567 const char* proxyPassword, const char* hostname, UINT16 port)
568{
569 BOOL rc = FALSE;
570 int status = 0;
571 wStream* s = NULL;
572 char port_str[10] = { 0 };
573 char recv_buf[256] = { 0 };
574 char* eol = NULL;
575 size_t resultsize = 0;
576 size_t reserveSize = 0;
577 size_t portLen = 0;
578 size_t hostLen = 0;
579 const char connect[] = "CONNECT ";
580 const char httpheader[] = " HTTP/1.1" CRLF "Host: ";
581
582 WINPR_ASSERT(context);
583 WINPR_ASSERT(bufferedBio);
584 WINPR_ASSERT(hostname);
585 const UINT32 timeout =
586 freerdp_settings_get_uint32(context->settings, FreeRDP_TcpConnectTimeout);
587
588 _itoa_s(port, port_str, sizeof(port_str), 10);
589
590 hostLen = strlen(hostname);
591 portLen = strnlen(port_str, sizeof(port_str));
592 reserveSize = strlen(connect) + (hostLen + 1 + portLen) * 2 + strlen(httpheader);
593 s = Stream_New(NULL, reserveSize);
594 if (!s)
595 goto fail;
596
597 Stream_Write(s, connect, strlen(connect));
598 Stream_Write(s, hostname, hostLen);
599 Stream_Write_UINT8(s, ':');
600 Stream_Write(s, port_str, portLen);
601 Stream_Write(s, httpheader, strlen(httpheader));
602 Stream_Write(s, hostname, hostLen);
603 Stream_Write_UINT8(s, ':');
604 Stream_Write(s, port_str, portLen);
605
606 if (proxyUsername && proxyPassword)
607 {
608 const int length = _scprintf("%s:%s", proxyUsername, proxyPassword);
609 if (length > 0)
610 {
611 const size_t size = (size_t)length + 1ull;
612 char* creds = (char*)malloc(size);
613
614 if (!creds)
615 goto fail;
616 else
617 {
618 const char basic[] = CRLF "Proxy-Authorization: Basic ";
619 char* base64 = NULL;
620
621 (void)sprintf_s(creds, size, "%s:%s", proxyUsername, proxyPassword);
622 base64 = crypto_base64_encode((const BYTE*)creds, size - 1);
623
624 if (!base64 || !Stream_EnsureRemainingCapacity(s, strlen(basic) + strlen(base64)))
625 {
626 free(base64);
627 free(creds);
628 goto fail;
629 }
630 Stream_Write(s, basic, strlen(basic));
631 Stream_Write(s, base64, strlen(base64));
632
633 free(base64);
634 }
635 free(creds);
636 }
637 }
638
639 if (!Stream_EnsureRemainingCapacity(s, 4))
640 goto fail;
641
642 Stream_Write(s, CRLF CRLF, 4);
643 ERR_clear_error();
644 const size_t pos = Stream_GetPosition(s);
645 if (pos > INT32_MAX)
646 goto fail;
647
648 status = BIO_write(bufferedBio, Stream_Buffer(s), (int)pos);
649
650 if ((status < 0) || ((size_t)status != Stream_GetPosition(s)))
651 {
652 WLog_ERR(TAG, "HTTP proxy: failed to write CONNECT request");
653 goto fail;
654 }
655
656 /* Read result until CR-LF-CR-LF.
657 * Keep recv_buf a null-terminated string. */
658 const UINT64 start = GetTickCount64();
659 while (strstr(recv_buf, CRLF CRLF) == NULL)
660 {
661 if (resultsize >= sizeof(recv_buf) - 1)
662 {
663 WLog_ERR(TAG, "HTTP Reply headers too long: %s", get_response_header(recv_buf));
664 goto fail;
665 }
666 const size_t rdsize = sizeof(recv_buf) - resultsize - 1ULL;
667
668 ERR_clear_error();
669
670 WINPR_ASSERT(rdsize <= INT32_MAX);
671 status = BIO_read(bufferedBio, (BYTE*)recv_buf + resultsize, (int)rdsize);
672
673 if (status < 0)
674 {
675 /* Error? */
676 if (!freerdp_shall_disconnect_context(context) && BIO_should_retry(bufferedBio))
677 {
678 USleep(100);
679 continue;
680 }
681
682 WLog_ERR(TAG, "Failed reading reply from HTTP proxy (Status %d)", status);
683 goto fail;
684 }
685 else if (status == 0)
686 {
687 const UINT64 now = GetTickCount64();
688 const UINT64 diff = now - start;
689 if (freerdp_shall_disconnect_context(context) || (now < start) || (diff > timeout))
690 {
691 /* Error? */
692 WLog_ERR(TAG, "Failed reading reply from HTTP proxy (BIO_read returned zero)");
693 goto fail;
694 }
695 Sleep(10);
696 }
697 else
698 {
699 /* Error? */
700 WLog_ERR(TAG, "Failed reading reply from HTTP proxy (BIO_read returned zero)");
701 goto fail;
702 }
703
704 resultsize += WINPR_ASSERTING_INT_CAST(size_t, status);
705 }
706
707 /* Extract HTTP status line */
708 eol = strchr(recv_buf, '\r');
709
710 if (!eol)
711 {
712 /* should never happen */
713 goto fail;
714 }
715
716 *eol = '\0';
717 WLog_INFO(TAG, "HTTP Proxy: %s", recv_buf);
718
719 if (strnlen(recv_buf, sizeof(recv_buf)) < 12)
720 goto fail;
721
722 recv_buf[7] = 'X';
723
724 if (strncmp(recv_buf, "HTTP/1.X 200", 12) != 0)
725 goto fail;
726
727 rc = TRUE;
728fail:
729 Stream_Free(s, TRUE);
730 return rc;
731}
732
733static int recv_socks_reply(rdpContext* context, BIO* bufferedBio, BYTE* buf, int len, char* reason,
734 BYTE checkVer)
735{
736 int status = 0;
737
738 WINPR_ASSERT(context);
739
740 const UINT32 timeout =
741 freerdp_settings_get_uint32(context->settings, FreeRDP_TcpConnectTimeout);
742 const UINT64 start = GetTickCount64();
743 for (;;)
744 {
745 ERR_clear_error();
746 status = BIO_read(bufferedBio, buf, len);
747
748 if (status > 0)
749 {
750 break;
751 }
752 else if (status < 0)
753 {
754 /* Error? */
755 if (!freerdp_shall_disconnect_context(context) && BIO_should_retry(bufferedBio))
756 {
757 USleep(100);
758 continue;
759 }
760
761 WLog_ERR(TAG, "Failed reading %s reply from SOCKS proxy (Status %d)", reason, status);
762 return -1;
763 }
764 else if (status == 0)
765 {
766 const UINT64 now = GetTickCount64();
767 const UINT64 diff = now - start;
768 if (freerdp_shall_disconnect_context(context) || (now < start) || (diff > timeout))
769 {
770 /* Error? */
771 WLog_ERR(TAG, "Failed reading %s reply from SOCKS proxy (BIO_read returned zero)",
772 reason);
773 return status;
774 }
775 Sleep(10);
776 }
777 else // if (status == 0)
778 {
779 /* Error? */
780 WLog_ERR(TAG, "Failed reading %s reply from SOCKS proxy (BIO_read returned zero)",
781 reason);
782 return -1;
783 }
784 }
785
786 if (status < 2)
787 {
788 WLog_ERR(TAG, "SOCKS Proxy reply packet too short (%s)", reason);
789 return -1;
790 }
791
792 if (buf[0] != checkVer)
793 {
794 WLog_ERR(TAG, "SOCKS Proxy version is not 5 (%s)", reason);
795 return -1;
796 }
797
798 return status;
799}
800
801static BOOL socks_proxy_connect(rdpContext* context, BIO* bufferedBio, const char* proxyUsername,
802 const char* proxyPassword, const char* hostname, UINT16 port)
803{
804 int status = 0;
805 BYTE nauthMethods = 1;
806 int writeLen = 3;
807 BYTE buf[3 + 255 + 255] = { 0 }; /* biggest packet is user/pass auth */
808 const size_t hostnlen = strnlen(hostname, 255);
809
810 if (proxyUsername && proxyPassword)
811 {
812 nauthMethods++;
813 writeLen++;
814 }
815
816 /* select auth. method */
817 buf[0] = 5; /* SOCKS version */
818 buf[1] = nauthMethods; /* #of methods offered */
819 buf[2] = AUTH_M_NO_AUTH;
820
821 if (nauthMethods > 1)
822 buf[3] = AUTH_M_USR_PASS;
823
824 ERR_clear_error();
825 status = BIO_write(bufferedBio, buf, writeLen);
826
827 if (status != writeLen)
828 {
829 WLog_ERR(TAG, "SOCKS proxy: failed to write AUTH METHOD request");
830 return FALSE;
831 }
832
833 status = recv_socks_reply(context, bufferedBio, buf, 2, "AUTH REQ", 5);
834
835 if (status <= 0)
836 return FALSE;
837
838 switch (buf[1])
839 {
840 case AUTH_M_NO_AUTH:
841 WLog_DBG(TAG, "SOCKS Proxy: (NO AUTH) method was selected");
842 break;
843
844 case AUTH_M_USR_PASS:
845 if (!proxyUsername || !proxyPassword)
846 return FALSE;
847 else
848 {
849 const BYTE usernameLen = (BYTE)strnlen(proxyUsername, 255);
850 const BYTE userpassLen = (BYTE)strnlen(proxyPassword, 255);
851 BYTE* ptr = NULL;
852
853 if (nauthMethods < 2)
854 {
855 WLog_ERR(TAG, "SOCKS Proxy: USER/PASS method was not proposed to server");
856 return FALSE;
857 }
858
859 /* user/password v1 method */
860 ptr = buf + 2;
861 buf[0] = 1;
862 buf[1] = usernameLen;
863 memcpy(ptr, proxyUsername, usernameLen);
864 ptr += usernameLen;
865 *ptr = userpassLen;
866 ptr++;
867 memcpy(ptr, proxyPassword, userpassLen);
868 ERR_clear_error();
869 status = BIO_write(bufferedBio, buf, 3 + usernameLen + userpassLen);
870
871 if (status != 3 + usernameLen + userpassLen)
872 {
873 WLog_ERR(TAG, "SOCKS Proxy: error writing user/password request");
874 return FALSE;
875 }
876
877 status = recv_socks_reply(context, bufferedBio, buf, 2, "AUTH REQ", 1);
878
879 if (status < 2)
880 return FALSE;
881
882 if (buf[1] != 0x00)
883 {
884 WLog_ERR(TAG, "SOCKS Proxy: invalid user/password");
885 return FALSE;
886 }
887 }
888
889 break;
890
891 default:
892 WLog_ERR(TAG, "SOCKS Proxy: unknown method 0x%x was selected by proxy", buf[1]);
893 return FALSE;
894 }
895
896 /* CONN request */
897 buf[0] = 5; /* SOCKS version */
898 buf[1] = SOCKS_CMD_CONNECT; /* command */
899 buf[2] = 0; /* 3rd octet is reserved x00 */
900 buf[3] = SOCKS_ADDR_FQDN; /* addr.type */
901 buf[4] = (BYTE)hostnlen; /* DST.ADDR */
902 memcpy(buf + 5, hostname, hostnlen);
903 /* follows DST.PORT in netw. format */
904 buf[hostnlen + 5] = (port >> 8) & 0xff;
905 buf[hostnlen + 6] = port & 0xff;
906 ERR_clear_error();
907 status = BIO_write(bufferedBio, buf, (int)hostnlen + 7);
908
909 if ((status < 0) || ((size_t)status != (hostnlen + 7U)))
910 {
911 WLog_ERR(TAG, "SOCKS proxy: failed to write CONN REQ");
912 return FALSE;
913 }
914
915 status = recv_socks_reply(context, bufferedBio, buf, sizeof(buf), "CONN REQ", 5);
916
917 if (status < 4)
918 return FALSE;
919
920 if (buf[1] == 0)
921 {
922 WLog_INFO(TAG, "Successfully connected to %s:%" PRIu16, hostname, port);
923 return TRUE;
924 }
925
926 if (buf[1] > 0 && buf[1] < 9)
927 WLog_INFO(TAG, "SOCKS Proxy replied: %s", rplstat[buf[1]]);
928 else
929 WLog_INFO(TAG, "SOCKS Proxy replied: %" PRIu8 " status not listed in rfc1928", buf[1]);
930
931 return FALSE;
932}
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.