24#include <openssl/err.h>
28#include <freerdp/settings.h>
29#include <freerdp/utils/proxy_utils.h>
30#include <freerdp/crypto/crypto.h>
34#include <winpr/assert.h>
35#include <winpr/sysinfo.h>
36#include <winpr/environment.h>
40#include <freerdp/log.h>
41#define TAG FREERDP_TAG("core.proxy")
53 SOCKS_CMD_CONNECT = 1,
55 SOCKS_CMD_UDP_ASSOCIATE = 3
65static const char logprefix[] =
"SOCKS Proxy:";
68static const char* rplstat[] = {
"succeeded",
69 "general SOCKS server failure",
70 "connection not allowed by ruleset",
71 "Network unreachable",
75 "Command not supported",
76 "Address type not supported" };
79static BOOL http_proxy_connect(rdpContext* context, BIO* bufferedBio,
const char* proxyUsername,
80 const char* proxyPassword,
const char* hostname, UINT16 port);
83static BOOL socks_proxy_connect(rdpContext* context, BIO* bufferedBio,
const char* proxyUsername,
84 const char* proxyPassword,
const char* hostname, UINT16 port);
85static void proxy_read_environment(rdpSettings* settings,
char* envname);
87BOOL proxy_prepare(rdpSettings* settings,
const char** lpPeerHostname, UINT16* lpPeerPort,
88 const char** lpProxyUsername,
const char** lpProxyPassword)
95 proxy_read_environment(settings,
"https_proxy");
98 proxy_read_environment(settings,
"HTTPS_PROXY");
101 proxy_read_environment(settings,
"no_proxy");
104 proxy_read_environment(settings,
"NO_PROXY");
109 if (!*lpPeerHostname ||
110 (!utils_is_valid_ip(*lpPeerHostname) && !winpr_str_is_valid_hostname(*lpPeerHostname)))
122static BOOL value_to_int(
const char* value, LONGLONG* result, LONGLONG min, LONGLONG max)
126 if (!value || !result)
130 rc = _strtoi64(value,
nullptr, 0);
135 if ((rc < min) || (rc > max))
143static BOOL cidr4_match(
const struct in_addr* addr,
const struct in_addr* net, BYTE bits)
148 const uint32_t mask = htonl(0xFFFFFFFFu << (32 - bits));
149 const uint32_t amask = addr->s_addr & mask;
150 const uint32_t nmask = net->s_addr & mask;
151 return amask == nmask;
155static BOOL cidr6_match(
const struct in6_addr* address,
const struct in6_addr* network,
158 const uint32_t* a = (
const uint32_t*)address;
159 const uint32_t* n = (
const uint32_t*)network;
160 const size_t bits_whole = bits >> 5;
161 const size_t bits_incomplete = bits & 0x1F;
165 if (memcmp(a, n, bits_whole << 2) != 0)
171 uint32_t mask = htonl((0xFFFFFFFFu) << (32 - bits_incomplete));
173 if ((a[bits_whole] ^ n[bits_whole]) & mask)
181static BOOL option_ends_with(
const char* str,
const char* ext)
185 const size_t strLen = strlen(str);
186 const size_t extLen = strlen(ext);
191 return _strnicmp(&str[strLen - extLen], ext, extLen) == 0;
198static BOOL no_proxy_match_host(
const char* val,
const char* hostname)
201 WINPR_ASSERT(hostname);
204 if (_stricmp(
"*", val) == 0)
212 return option_ends_with(hostname, val);
216static BOOL starts_with(
const char* val,
const char* prefix)
218 const size_t plen = strlen(prefix);
219 const size_t vlen = strlen(val);
222 return _strnicmp(val, prefix, plen) == 0;
226static BOOL no_proxy_match_ip(
const char* val,
const char* hostname)
229 WINPR_ASSERT(hostname);
231 struct sockaddr_in sa4 = WINPR_C_ARRAY_INIT;
232 struct sockaddr_in6 sa6 = WINPR_C_ARRAY_INIT;
234 if (inet_pton(AF_INET, hostname, &sa4.sin_addr) == 1)
237 if (starts_with(hostname, val))
240 char* sub = strchr(val,
'/');
244 struct sockaddr_in mask = WINPR_C_ARRAY_INIT;
245 if (inet_pton(AF_INET, val, &mask.sin_addr) == 0)
249 if (memcmp(&mask, &sa4,
sizeof(mask)) == 0)
254 const unsigned long usub = strtoul(sub,
nullptr, 0);
255 if ((errno == 0) && (usub <= UINT8_MAX))
256 return cidr4_match(&sa4.sin_addr, &mask.sin_addr, (UINT8)usub);
259 else if (inet_pton(AF_INET6, hostname, &sa6.sin6_addr) == 1)
264 char str[INET6_ADDRSTRLEN + 1] = WINPR_C_ARRAY_INIT;
265 strncpy(str, val, INET6_ADDRSTRLEN);
267 const size_t len = strnlen(str, INET6_ADDRSTRLEN);
270 if (str[len - 1] ==
']')
275 if (starts_with(hostname, str))
278 char* sub = strchr(str,
'/');
282 struct sockaddr_in6 mask = WINPR_C_ARRAY_INIT;
283 if (inet_pton(AF_INET6, str, &mask.sin6_addr) == 0)
287 if (memcmp(&mask, &sa6,
sizeof(mask)) == 0)
292 const unsigned long usub = strtoul(sub,
nullptr, 0);
293 if ((errno == 0) && (usub <= UINT8_MAX))
294 return cidr6_match(&sa6.sin6_addr, &mask.sin6_addr, (UINT8)usub);
302static BOOL check_no_proxy(rdpSettings* settings,
const char* no_proxy)
304 const char* delimiter =
", ";
306 char* context =
nullptr;
308 if (!no_proxy || !settings)
311 char* copy = _strdup(no_proxy);
316 char* current = strtok_s(copy, delimiter, &context);
318 while (current && !result)
320 const size_t currentlen = strlen(current);
324 const char* ServerHostname =
326 WLog_DBG(TAG,
"%s => %s (%" PRIuz
")", ServerHostname, current, currentlen);
328 if (no_proxy_match_host(current, ServerHostname))
330 else if (no_proxy_match_ip(current, ServerHostname))
334 current = strtok_s(
nullptr, delimiter, &context);
341void proxy_read_environment(rdpSettings* settings,
char* envname)
343 const DWORD envlen = GetEnvironmentVariableA(envname,
nullptr, 0);
345 if (!envlen || (envlen <= 1))
348 char* env = calloc(1, envlen);
352 WLog_ERR(TAG,
"Not enough memory");
356 if (GetEnvironmentVariableA(envname, env, envlen) == envlen - 1)
358 if (_strnicmp(
"NO_PROXY", envname, 9) == 0)
360 if (check_no_proxy(settings, env))
362 WLog_INFO(TAG,
"deactivating proxy: %s [%s=%s]",
366 WLog_WARN(TAG,
"failed to set FreeRDP_ProxyType=PROXY_TYPE_NONE");
371 if (!proxy_parse_uri(settings, env))
374 TAG,
"Error while parsing proxy URI from environment variable; ignoring proxy");
382BOOL proxy_parse_uri(rdpSettings* settings,
const char* uri_in)
385 const char* protocol =
"";
388 if (!settings || !uri_in)
391 if ((_stricmp(
"no_proxy", uri_in) == 0) || (_stricmp(
"ignore", uri_in) == 0))
393 WLog_INFO(TAG,
"Parsed proxy configuration: all proxies disabled");
397 char* uri_copy = _strdup(uri_in);
398 char* uri = uri_copy;
403 char* p = strstr(uri,
"://");
408 if (_stricmp(
"http", uri) == 0)
414 else if (_stricmp(
"socks5", uri) == 0)
422 WLog_ERR(TAG,
"Only HTTP and SOCKS5 proxies supported by now");
439 char* atPtr = strrchr(uri,
'@');
450 char* colonPtr = strchr(uri,
':');
452 if (!colonPtr || (colonPtr > atPtr))
454 WLog_ERR(TAG,
"invalid syntax for proxy (contains no password)");
461 WLog_ERR(TAG,
"unable to allocate proxy username");
469 WLog_ERR(TAG,
"unable to allocate proxy password");
478 char* p = strchr(uri,
':');
484 if (!value_to_int(&p[1], &val, 0, UINT16_MAX))
486 WLog_ERR(TAG,
"invalid syntax for proxy (invalid port)");
492 WLog_ERR(TAG,
"invalid syntax for proxy (port missing)");
501 if (_stricmp(
"http", protocol) == 0)
511 WLog_DBG(TAG,
"setting default proxy port: %" PRIu16, port);
518 char* p = strchr(uri,
'/');
525 if (_stricmp(
"", uri) == 0)
527 WLog_ERR(TAG,
"invalid syntax for proxy (hostname missing)");
533 WLog_INFO(TAG,
"Parsed proxy configuration: %s://%s:%s@%s:%" PRIu16, protocol,
540 WLog_INFO(TAG,
"Parsed proxy configuration: %s://%s:%" PRIu16, protocol,
548 WLog_WARN(TAG,
"Failed to parse proxy configuration: %s://%s:%" PRIu16, protocol, uri,
554BOOL proxy_connect(rdpContext* context, BIO* bufferedBio,
const char* proxyUsername,
555 const char* proxyPassword,
const char* hostname, UINT16 port)
557 WINPR_ASSERT(context);
558 rdpSettings* settings = context->settings;
562 case PROXY_TYPE_NONE:
563 case PROXY_TYPE_IGNORE:
566 case PROXY_TYPE_HTTP:
567 return http_proxy_connect(context, bufferedBio, proxyUsername, proxyPassword, hostname,
570 case PROXY_TYPE_SOCKS:
571 return socks_proxy_connect(context, bufferedBio, proxyUsername, proxyPassword, hostname,
575 WLog_ERR(TAG,
"Invalid internal proxy configuration");
581static const char* get_response_header(
char* response)
583 char* current_pos = strchr(response,
'\r');
585 current_pos = strchr(response,
'\n');
593static BOOL http_proxy_write_hostname(
wStream* s,
const char* hostname,
size_t len)
595 const BOOL isIPv6 = utils_is_valid_ipV6N(hostname, len);
599 if (!Stream_EnsureRemainingCapacity(s, 1))
601 Stream_Write_UINT8(s,
'[');
604 if (!Stream_EnsureRemainingCapacity(s, len))
606 Stream_Write(s, hostname, len);
610 if (!Stream_EnsureRemainingCapacity(s, 1))
612 Stream_Write_UINT8(s,
']');
619static BOOL http_proxy_connect(rdpContext* context, BIO* bufferedBio,
const char* proxyUsername,
620 const char* proxyPassword,
const char* hostname, UINT16 port)
624 char port_str[10] = WINPR_C_ARRAY_INIT;
625 char recv_buf[256] = WINPR_C_ARRAY_INIT;
627 size_t resultsize = 0;
628 const char connect[] =
"CONNECT ";
629 const char httpheader[] =
" HTTP/1.1" CRLF
"Host: ";
631 WINPR_ASSERT(context);
632 WINPR_ASSERT(bufferedBio);
633 WINPR_ASSERT(hostname);
634 const UINT32 timeout =
639 const size_t hostLen = strnlen(hostname, 255 + 1);
640 if (!utils_is_valid_ipN(hostname, hostLen) && !winpr_str_is_valid_hostnameN(hostname, hostLen))
643 if (_itoa_s(port, port_str,
sizeof(port_str), 10) < 0)
645 WLog_ERR(TAG,
"itoa %s failed", port_str);
649 const size_t portLen = strnlen(port_str,
sizeof(port_str));
650 wStream* s = Stream_New(
nullptr, 1024);
654 const size_t clen = strnlen(connect,
sizeof(connect));
655 if (!Stream_EnsureRemainingCapacity(s, clen))
657 Stream_Write(s, connect, clen);
659 if (!http_proxy_write_hostname(s, hostname, hostLen))
662 if (!Stream_EnsureRemainingCapacity(s, 1))
664 Stream_Write_UINT8(s,
':');
666 if (!Stream_EnsureRemainingCapacity(s, portLen))
668 Stream_Write(s, port_str, portLen);
670 const size_t httplen = strnlen(httpheader,
sizeof(httpheader));
671 if (!Stream_EnsureRemainingCapacity(s, httplen))
673 Stream_Write(s, httpheader, httplen);
675 if (!http_proxy_write_hostname(s, hostname, hostLen))
678 if (!Stream_EnsureRemainingCapacity(s, 1))
680 Stream_Write_UINT8(s,
':');
682 if (!Stream_EnsureRemainingCapacity(s, portLen))
684 Stream_Write(s, port_str, portLen);
686 if (proxyUsername && proxyPassword && (strnlen(proxyUsername, 1) > 0) &&
687 (strnlen(proxyPassword, 1) > 0))
689 const char basic[] = CRLF
"Proxy-Authorization: Basic ";
690 const size_t balen = strnlen(basic,
sizeof(basic));
691 if (!Stream_EnsureRemainingCapacity(s, balen))
693 Stream_Write(s, basic, balen);
695 char* creds =
nullptr;
697 (void)winpr_asprintf(&creds, &size,
"%s:%s", proxyUsername, proxyPassword);
698 if (!creds || (size < 1))
704 char* base64 = crypto_base64_encode_len(creds, size, &b64len);
706 if (!base64 || !Stream_EnsureRemainingCapacity(s, b64len))
712 Stream_Write(s, base64, strlen(base64));
716 if (!Stream_EnsureRemainingCapacity(s, 4))
719 Stream_Write(s, CRLF CRLF, 4);
723 const size_t pos = Stream_GetPosition(s);
727 status = BIO_write(bufferedBio, Stream_Buffer(s), WINPR_ASSERTING_INT_CAST(
int, pos));
730 if ((status < 0) || ((
size_t)status != Stream_GetPosition(s)))
732 WLog_ERR(TAG,
"HTTP proxy: failed to write CONNECT request");
739 const UINT64 start = GetTickCount64();
740 while (strstr(recv_buf, CRLF CRLF) ==
nullptr)
742 if (resultsize >=
sizeof(recv_buf) - 1)
744 WLog_ERR(TAG,
"HTTP Reply headers too long: %s", get_response_header(recv_buf));
747 const size_t rdsize =
sizeof(recv_buf) - resultsize - 1ULL;
751 WINPR_ASSERT(rdsize <= INT32_MAX);
752 status = BIO_read(bufferedBio, (BYTE*)recv_buf + resultsize, (
int)rdsize);
757 if (!freerdp_shall_disconnect_context(context) && BIO_should_retry(bufferedBio))
763 WLog_ERR(TAG,
"Failed reading reply from HTTP proxy (Status %d)", status);
766 else if (status == 0)
768 const UINT64 now = GetTickCount64();
769 const UINT64 diff = now - start;
770 if (freerdp_shall_disconnect_context(context) || (now < start) || (diff > timeout))
773 WLog_ERR(TAG,
"Failed reading reply from HTTP proxy (BIO_read returned zero)");
779 resultsize += WINPR_ASSERTING_INT_CAST(
size_t, status);
784 eol = strchr(recv_buf,
'\r');
793 WLog_INFO(TAG,
"HTTP Proxy: %s", recv_buf);
795 if (strnlen(recv_buf,
sizeof(recv_buf)) < 12)
800 if (strncmp(recv_buf,
"HTTP/1.X 200", 12) != 0)
806 WLog_ERR(TAG,
"Failed to connect to proxy");
807 Stream_Free(s, TRUE);
812static int recv_socks_reply(rdpContext* context, BIO* bufferedBio, BYTE* buf,
int len,
char* reason,
817 WINPR_ASSERT(context);
819 const UINT32 timeout =
821 const UINT64 start = GetTickCount64();
825 status = BIO_read(bufferedBio, buf, len);
834 if (!freerdp_shall_disconnect_context(context) && BIO_should_retry(bufferedBio))
840 WLog_ERR(TAG,
"Failed reading %s reply from SOCKS proxy (Status %d)", reason, status);
843 else if (status == 0)
845 const UINT64 now = GetTickCount64();
846 const UINT64 diff = now - start;
847 if (freerdp_shall_disconnect_context(context) || (now < start) || (diff > timeout))
850 WLog_ERR(TAG,
"Failed reading %s reply from SOCKS proxy (BIO_read returned zero)",
859 WLog_ERR(TAG,
"Failed reading %s reply from SOCKS proxy (BIO_read returned zero)",
867 WLog_ERR(TAG,
"SOCKS Proxy reply packet too short (%s)", reason);
871 if (buf[0] != checkVer)
873 WLog_ERR(TAG,
"SOCKS Proxy version is not 5 (%s)", reason);
881static BOOL socks_proxy_userpass(rdpContext* context, BIO* bufferedBio,
const char* proxyUsername,
882 const char* proxyPassword)
884 WINPR_ASSERT(context);
885 WINPR_ASSERT(bufferedBio);
887 if (!proxyUsername || !proxyPassword)
889 WLog_ERR(TAG,
"%s invalid username (%p) or password (%p)", logprefix,
890 WINPR_CXX_COMPAT_CAST(
const void*, proxyUsername),
891 WINPR_CXX_COMPAT_CAST(
const void*, proxyPassword));
895 const size_t usernameLen = (BYTE)strnlen(proxyUsername, 256);
896 if (usernameLen > 255)
898 WLog_ERR(TAG,
"%s username too long (%" PRIuz
", max=255)", logprefix, usernameLen);
902 const size_t userpassLen = (BYTE)strnlen(proxyPassword, 256);
903 if (userpassLen > 255)
905 WLog_ERR(TAG,
"%s password too long (%" PRIuz
", max=255)", logprefix, userpassLen);
911 BYTE buf[2 * 255 + 3] = WINPR_C_ARRAY_INIT;
915 buf[offset++] = WINPR_ASSERTING_INT_CAST(uint8_t, usernameLen);
916 memcpy(&buf[offset], proxyUsername, usernameLen);
917 offset += usernameLen;
919 buf[offset++] = WINPR_ASSERTING_INT_CAST(uint8_t, userpassLen);
920 memcpy(&buf[offset], proxyPassword, userpassLen);
921 offset += userpassLen;
924 const int ioffset = WINPR_ASSERTING_INT_CAST(
int, offset);
925 const int status = BIO_write(bufferedBio, buf, ioffset);
927 if (status != ioffset)
929 WLog_ERR(TAG,
"%s error writing user/password request", logprefix);
934 BYTE buf[2] = WINPR_C_ARRAY_INIT;
935 const int status = recv_socks_reply(context, bufferedBio, buf,
sizeof(buf),
"AUTH REQ", 1);
942 WLog_ERR(TAG,
"%s invalid user/password", logprefix);
949static BOOL socks_proxy_connect(rdpContext* context, BIO* bufferedBio,
const char* proxyUsername,
950 const char* proxyPassword,
const char* hostname, UINT16 port)
952 WINPR_ASSERT(hostname);
955 const size_t hostnlen = strnlen(hostname, 255 + 1);
956 if (!utils_is_valid_ipN(hostname, hostnlen) &&
957 !winpr_str_is_valid_hostnameN(hostname, hostnlen))
960 BYTE nauthMethods = 1;
962 if (proxyUsername || proxyPassword)
967 const BYTE buf[] = { 5,
969 AUTH_M_NO_AUTH, AUTH_M_USR_PASS };
971 size_t writeLen =
sizeof(buf);
972 if (nauthMethods <= 1)
976 const int iwriteLen = WINPR_ASSERTING_INT_CAST(
int, writeLen);
977 const int status = BIO_write(bufferedBio, buf, iwriteLen);
979 if (status != iwriteLen)
981 WLog_ERR(TAG,
"%s SOCKS proxy: failed to write AUTH METHOD request", logprefix);
987 BYTE buf[2] = WINPR_C_ARRAY_INIT;
988 const int status = recv_socks_reply(context, bufferedBio, buf,
sizeof(buf),
"AUTH REQ", 5);
996 WLog_DBG(TAG,
"%s (NO AUTH) method was selected", logprefix);
999 case AUTH_M_USR_PASS:
1000 if (nauthMethods < 2)
1002 WLog_ERR(TAG,
"%s USER/PASS method was not proposed to server", logprefix);
1005 if (!socks_proxy_userpass(context, bufferedBio, proxyUsername, proxyPassword))
1010 WLog_ERR(TAG,
"%s unknown method 0x%x was selected by proxy", logprefix, buf[1]);
1016 BYTE buf[262] = WINPR_C_ARRAY_INIT;
1019 buf[offset++] = SOCKS_CMD_CONNECT;
1022 if (inet_pton(AF_INET6, hostname, &buf[offset + 1]) == 1)
1024 buf[offset++] = SOCKS_ADDR_IPV6;
1027 else if (inet_pton(AF_INET, hostname, &buf[offset + 1]) == 1)
1029 buf[offset++] = SOCKS_ADDR_IPV4;
1034 buf[offset++] = SOCKS_ADDR_FQDN;
1035 buf[offset++] = WINPR_ASSERTING_INT_CAST(uint8_t, hostnlen);
1036 memcpy(&buf[offset], hostname, hostnlen);
1040 if (offset >
sizeof(buf) - 2)
1042 WLog_ERR(TAG,
"Invalid offset %" PRIuz, offset);
1047 buf[offset++] = (port >> 8) & 0xff;
1048 buf[offset++] = port & 0xff;
1051 const int ioffset = WINPR_ASSERTING_INT_CAST(
int, offset);
1052 const int status = BIO_write(bufferedBio, buf, ioffset);
1054 if ((status < 0) || (status != ioffset))
1056 WLog_ERR(TAG,
"%s SOCKS proxy: failed to write CONN REQ", logprefix);
1061 BYTE buf[255] = WINPR_C_ARRAY_INIT;
1062 const int status = recv_socks_reply(context, bufferedBio, buf,
sizeof(buf),
"CONN REQ", 5);
1069 WLog_INFO(TAG,
"Successfully connected to %s:%" PRIu16, hostname, port);
1073 if ((buf[1] > 0) && (buf[1] < 9))
1074 WLog_INFO(TAG,
"SOCKS Proxy replied: %s", rplstat[buf[1]]);
1076 WLog_INFO(TAG,
"SOCKS Proxy replied: %" PRIu8
" status not listed in rfc1928", buf[1]);
WINPR_ATTR_NODISCARD FREERDP_API const char * freerdp_settings_get_string(const rdpSettings *settings, FreeRDP_Settings_Keys_String id)
Returns a immutable string settings value.
WINPR_ATTR_NODISCARD FREERDP_API UINT16 freerdp_settings_get_uint16(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt16 id)
Returns a UINT16 settings value.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_set_uint32(rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id, UINT32 val)
Sets a UINT32 settings value.
WINPR_ATTR_NODISCARD FREERDP_API UINT32 freerdp_settings_get_uint32(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id)
Returns a UINT32 settings value.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_set_uint16(rdpSettings *settings, FreeRDP_Settings_Keys_UInt16 id, UINT16 val)
Sets a UINT16 settings value.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_set_string(rdpSettings *settings, FreeRDP_Settings_Keys_String id, const char *val)
Sets a string settings value. The param is copied.