24 #include <openssl/err.h>
28 #include <freerdp/settings.h>
29 #include <freerdp/utils/proxy_utils.h>
30 #include <freerdp/crypto/crypto.h>
33 #include <winpr/assert.h>
34 #include <winpr/sysinfo.h>
35 #include <winpr/environment.h>
39 #include <freerdp/log.h>
40 #define TAG FREERDP_TAG("core.proxy")
52 SOCKS_CMD_CONNECT = 1,
54 SOCKS_CMD_UDP_ASSOCIATE = 3
65 static const char* rplstat[] = {
"succeeded",
66 "general SOCKS server failure",
67 "connection not allowed by ruleset",
68 "Network unreachable",
72 "Command not supported",
73 "Address type not supported" };
75 static BOOL http_proxy_connect(rdpContext* context, BIO* bufferedBio,
const char* proxyUsername,
76 const char* proxyPassword,
const char* hostname, UINT16 port);
77 static BOOL socks_proxy_connect(rdpContext* context, BIO* bufferedBio,
const char* proxyUsername,
78 const char* proxyPassword,
const char* hostname, UINT16 port);
79 static void proxy_read_environment(rdpSettings* settings,
char* envname);
81 BOOL proxy_prepare(rdpSettings* settings,
const char** lpPeerHostname, UINT16* lpPeerPort,
82 const char** lpProxyUsername,
const char** lpProxyPassword)
89 proxy_read_environment(settings,
"https_proxy");
92 proxy_read_environment(settings,
"HTTPS_PROXY");
95 proxy_read_environment(settings,
"no_proxy");
98 proxy_read_environment(settings,
"NO_PROXY");
112 static BOOL value_to_int(
const char* value, LONGLONG* result, LONGLONG min, LONGLONG max)
116 if (!value || !result)
120 rc = _strtoi64(value, NULL, 0);
125 if ((rc < min) || (rc > max))
132 static BOOL cidr4_match(
const struct in_addr* addr,
const struct in_addr* net, BYTE bits)
141 mask = htonl(0xFFFFFFFFu << (32 - bits));
142 amask = addr->s_addr & mask;
143 nmask = net->s_addr & mask;
144 return amask == nmask;
147 static BOOL cidr6_match(
const struct in6_addr* address,
const struct in6_addr* network,
150 const uint32_t* a = (
const uint32_t*)address;
151 const uint32_t* n = (
const uint32_t*)network;
152 size_t bits_whole = 0;
153 size_t bits_incomplete = 0;
154 bits_whole = bits >> 5;
155 bits_incomplete = bits & 0x1F;
159 if (memcmp(a, n, bits_whole << 2) != 0)
165 uint32_t mask = htonl((0xFFFFFFFFu) << (32 - bits_incomplete));
167 if ((a[bits_whole] ^ n[bits_whole]) & mask)
174 static BOOL check_no_proxy(rdpSettings* settings,
const char* no_proxy)
176 const char* delimiter =
",";
178 char* current = NULL;
180 char* context = NULL;
182 struct sockaddr_in sa4;
183 struct sockaddr_in6 sa6;
184 BOOL is_ipv4 = FALSE;
185 BOOL is_ipv6 = FALSE;
187 if (!no_proxy || !settings)
190 if (inet_pton(AF_INET, settings->ServerHostname, &sa4.sin_addr) == 1)
192 else if (inet_pton(AF_INET6, settings->ServerHostname, &sa6.sin6_addr) == 1)
195 host_len = strlen(settings->ServerHostname);
196 copy = _strdup(no_proxy);
201 current = strtok_s(copy, delimiter, &context);
203 while (current && !result)
205 const size_t currentlen = strlen(current);
209 WLog_DBG(TAG,
"%s => %s (%" PRIdz
")", settings->ServerHostname, current, currentlen);
212 if (current[0] ==
'*')
214 if (host_len >= currentlen)
216 const size_t offset = host_len + 1 - currentlen;
217 const char* name = settings->ServerHostname + offset;
219 if (strncmp(current + 1, name, currentlen - 1) == 0)
223 else if (current[currentlen - 1] ==
'*')
225 if (strncmp(current, settings->ServerHostname, currentlen - 1) == 0)
228 else if (current[0] ==
231 if (host_len > currentlen)
233 const size_t offset = host_len - currentlen;
234 const char* name = settings->ServerHostname + offset;
236 if (strncmp(current, name, currentlen) == 0)
240 else if (strcmp(current, settings->ServerHostname) == 0)
242 else if (is_ipv4 || is_ipv6)
244 char* rangedelim = strchr(current,
'/');
247 if (rangedelim != NULL)
249 const char* range = rangedelim + 1;
250 const unsigned long sub = strtoul(range, NULL, 0);
252 if ((errno == 0) && (sub <= UINT8_MAX))
258 struct sockaddr_in mask;
260 if (inet_pton(AF_INET, current, &mask.sin_addr))
261 result = cidr4_match(&sa4.sin_addr, &mask.sin_addr, (UINT8)sub);
265 struct sockaddr_in6 mask;
267 if (inet_pton(AF_INET6, current, &mask.sin6_addr))
268 result = cidr6_match(&sa6.sin6_addr, &mask.sin6_addr, (UINT8)sub);
272 WLog_WARN(TAG,
"NO_PROXY invalid entry %s", current);
274 else if (strncmp(current, settings->ServerHostname, currentlen) == 0)
279 current = strtok_s(NULL, delimiter, &context);
286 void proxy_read_environment(rdpSettings* settings,
char* envname)
288 const DWORD envlen = GetEnvironmentVariableA(envname, NULL, 0);
290 if (!envlen || (envlen <= 1))
293 char* env = calloc(1, envlen);
297 WLog_ERR(TAG,
"Not enough memory");
301 if (GetEnvironmentVariableA(envname, env, envlen) == envlen - 1)
303 if (_strnicmp(
"NO_PROXY", envname, 9) == 0)
305 if (check_no_proxy(settings, env))
307 WLog_INFO(TAG,
"deactivating proxy: %s [%s=%s]",
311 WLog_WARN(TAG,
"failed to set FreeRDP_ProxyType=PROXY_TYPE_NONE");
316 if (!proxy_parse_uri(settings, env))
319 TAG,
"Error while parsing proxy URI from environment variable; ignoring proxy");
327 BOOL proxy_parse_uri(rdpSettings* settings,
const char* uri_in)
330 const char* protocol =
"";
333 if (!settings || !uri_in)
336 char* uri_copy = _strdup(uri_in);
337 char* uri = uri_copy;
341 char* p = strstr(uri,
"://");
347 if (_stricmp(
"no_proxy", uri) == 0)
352 if (_stricmp(
"http", uri) == 0)
358 else if (_stricmp(
"socks5", uri) == 0)
366 WLog_ERR(TAG,
"Only HTTP and SOCKS5 proxies supported by now");
381 char* atPtr = strrchr(uri,
'@');
392 char* colonPtr = strchr(uri,
':');
394 if (!colonPtr || (colonPtr > atPtr))
396 WLog_ERR(TAG,
"invalid syntax for proxy (contains no password)");
403 WLog_ERR(TAG,
"unable to allocate proxy username");
411 WLog_ERR(TAG,
"unable to allocate proxy password");
418 p = strchr(uri,
':');
424 if (!value_to_int(&p[1], &val, 0, UINT16_MAX))
426 WLog_ERR(TAG,
"invalid syntax for proxy (invalid port)");
432 WLog_ERR(TAG,
"invalid syntax for proxy (port missing)");
441 if (_stricmp(
"http", protocol) == 0)
451 WLog_DBG(TAG,
"setting default proxy port: %" PRIu16, port);
457 p = strchr(uri,
'/');
463 if (_stricmp(
"", uri) == 0)
465 WLog_ERR(TAG,
"invalid syntax for proxy (hostname missing)");
471 WLog_INFO(TAG,
"Parsed proxy configuration: %s://%s:%s@%s:%" PRIu16, protocol,
478 WLog_INFO(TAG,
"Parsed proxy configuration: %s://%s:%" PRIu16, protocol,
486 WLog_WARN(TAG,
"Failed to parse proxy configuration: %s://%s:%" PRIu16, protocol, uri,
492 BOOL proxy_connect(rdpContext* context, BIO* bufferedBio,
const char* proxyUsername,
493 const char* proxyPassword,
const char* hostname, UINT16 port)
495 WINPR_ASSERT(context);
496 rdpSettings* settings = context->settings;
500 case PROXY_TYPE_NONE:
501 case PROXY_TYPE_IGNORE:
504 case PROXY_TYPE_HTTP:
505 return http_proxy_connect(context, bufferedBio, proxyUsername, proxyPassword, hostname,
508 case PROXY_TYPE_SOCKS:
509 return socks_proxy_connect(context, bufferedBio, proxyUsername, proxyPassword, hostname,
513 WLog_ERR(TAG,
"Invalid internal proxy configuration");
518 static const char* get_response_header(
char* response)
520 char* current_pos = strchr(response,
'\r');
522 current_pos = strchr(response,
'\n');
530 static BOOL http_proxy_connect(rdpContext* context, BIO* bufferedBio,
const char* proxyUsername,
531 const char* proxyPassword,
const char* hostname, UINT16 port)
536 char port_str[10] = { 0 };
537 char recv_buf[256] = { 0 };
539 size_t resultsize = 0;
540 size_t reserveSize = 0;
543 const char connect[] =
"CONNECT ";
544 const char httpheader[] =
" HTTP/1.1" CRLF
"Host: ";
546 WINPR_ASSERT(context);
547 WINPR_ASSERT(bufferedBio);
548 WINPR_ASSERT(hostname);
549 const UINT32 timeout =
552 _itoa_s(port, port_str,
sizeof(port_str), 10);
554 hostLen = strlen(hostname);
555 portLen = strnlen(port_str,
sizeof(port_str));
556 reserveSize = strlen(connect) + (hostLen + 1 + portLen) * 2 + strlen(httpheader);
557 s = Stream_New(NULL, reserveSize);
561 Stream_Write(s, connect, strlen(connect));
562 Stream_Write(s, hostname, hostLen);
563 Stream_Write_UINT8(s,
':');
564 Stream_Write(s, port_str, portLen);
565 Stream_Write(s, httpheader, strlen(httpheader));
566 Stream_Write(s, hostname, hostLen);
567 Stream_Write_UINT8(s,
':');
568 Stream_Write(s, port_str, portLen);
570 if (proxyUsername && proxyPassword)
572 const int length = _scprintf(
"%s:%s", proxyUsername, proxyPassword);
575 const size_t size = (size_t)length + 1ull;
576 char* creds = (
char*)malloc(size);
582 const char basic[] = CRLF
"Proxy-Authorization: Basic ";
585 (void)sprintf_s(creds, size,
"%s:%s", proxyUsername, proxyPassword);
586 base64 = crypto_base64_encode((
const BYTE*)creds, size - 1);
588 if (!base64 || !Stream_EnsureRemainingCapacity(s, strlen(basic) + strlen(base64)))
594 Stream_Write(s, basic, strlen(basic));
595 Stream_Write(s, base64, strlen(base64));
603 if (!Stream_EnsureRemainingCapacity(s, 4))
606 Stream_Write(s, CRLF CRLF, 4);
608 const size_t pos = Stream_GetPosition(s);
612 status = BIO_write(bufferedBio, Stream_Buffer(s), (
int)pos);
614 if ((status < 0) || ((
size_t)status != Stream_GetPosition(s)))
616 WLog_ERR(TAG,
"HTTP proxy: failed to write CONNECT request");
622 const UINT64 start = GetTickCount64();
623 while (strstr(recv_buf, CRLF CRLF) == NULL)
625 if (resultsize >=
sizeof(recv_buf) - 1)
627 WLog_ERR(TAG,
"HTTP Reply headers too long: %s", get_response_header(recv_buf));
630 const size_t rdsize =
sizeof(recv_buf) - resultsize - 1ULL;
634 WINPR_ASSERT(rdsize <= INT32_MAX);
635 status = BIO_read(bufferedBio, (BYTE*)recv_buf + resultsize, (
int)rdsize);
640 if (!freerdp_shall_disconnect_context(context) && BIO_should_retry(bufferedBio))
646 WLog_ERR(TAG,
"Failed reading reply from HTTP proxy (Status %d)", status);
649 else if (status == 0)
651 const UINT64 now = GetTickCount64();
652 const UINT64 diff = now - start;
653 if (freerdp_shall_disconnect_context(context) || (now < start) || (diff > timeout))
656 WLog_ERR(TAG,
"Failed reading reply from HTTP proxy (BIO_read returned zero)");
664 WLog_ERR(TAG,
"Failed reading reply from HTTP proxy (BIO_read returned zero)");
668 resultsize += status;
672 eol = strchr(recv_buf,
'\r');
681 WLog_INFO(TAG,
"HTTP Proxy: %s", recv_buf);
683 if (strnlen(recv_buf,
sizeof(recv_buf)) < 12)
688 if (strncmp(recv_buf,
"HTTP/1.X 200", 12) != 0)
693 Stream_Free(s, TRUE);
697 static int recv_socks_reply(rdpContext* context, BIO* bufferedBio, BYTE* buf,
int len,
char* reason,
702 WINPR_ASSERT(context);
704 const UINT32 timeout =
706 const UINT64 start = GetTickCount64();
710 status = BIO_read(bufferedBio, buf, len);
719 if (!freerdp_shall_disconnect_context(context) && BIO_should_retry(bufferedBio))
725 WLog_ERR(TAG,
"Failed reading %s reply from SOCKS proxy (Status %d)", reason, status);
728 else if (status == 0)
730 const UINT64 now = GetTickCount64();
731 const UINT64 diff = now - start;
732 if (freerdp_shall_disconnect_context(context) || (now < start) || (diff > timeout))
735 WLog_ERR(TAG,
"Failed reading %s reply from SOCKS proxy (BIO_read returned zero)",
744 WLog_ERR(TAG,
"Failed reading %s reply from SOCKS proxy (BIO_read returned zero)",
752 WLog_ERR(TAG,
"SOCKS Proxy reply packet too short (%s)", reason);
756 if (buf[0] != checkVer)
758 WLog_ERR(TAG,
"SOCKS Proxy version is not 5 (%s)", reason);
765 static BOOL socks_proxy_connect(rdpContext* context, BIO* bufferedBio,
const char* proxyUsername,
766 const char* proxyPassword,
const char* hostname, UINT16 port)
769 BYTE nauthMethods = 1;
771 BYTE buf[3 + 255 + 255] = { 0 };
772 const size_t hostnlen = strnlen(hostname, 255);
774 if (proxyUsername && proxyPassword)
782 buf[1] = nauthMethods;
783 buf[2] = AUTH_M_NO_AUTH;
785 if (nauthMethods > 1)
786 buf[3] = AUTH_M_USR_PASS;
789 status = BIO_write(bufferedBio, buf, writeLen);
791 if (status != writeLen)
793 WLog_ERR(TAG,
"SOCKS proxy: failed to write AUTH METHOD request");
797 status = recv_socks_reply(context, bufferedBio, buf, 2,
"AUTH REQ", 5);
805 WLog_DBG(TAG,
"SOCKS Proxy: (NO AUTH) method was selected");
808 case AUTH_M_USR_PASS:
809 if (!proxyUsername || !proxyPassword)
813 const BYTE usernameLen = (BYTE)strnlen(proxyUsername, 255);
814 const BYTE userpassLen = (BYTE)strnlen(proxyPassword, 255);
817 if (nauthMethods < 2)
819 WLog_ERR(TAG,
"SOCKS Proxy: USER/PASS method was not proposed to server");
826 buf[1] = usernameLen;
827 memcpy(ptr, proxyUsername, usernameLen);
831 memcpy(ptr, proxyPassword, userpassLen);
833 status = BIO_write(bufferedBio, buf, 3 + usernameLen + userpassLen);
835 if (status != 3 + usernameLen + userpassLen)
837 WLog_ERR(TAG,
"SOCKS Proxy: error writing user/password request");
841 status = recv_socks_reply(context, bufferedBio, buf, 2,
"AUTH REQ", 1);
848 WLog_ERR(TAG,
"SOCKS Proxy: invalid user/password");
856 WLog_ERR(TAG,
"SOCKS Proxy: unknown method 0x%x was selected by proxy", buf[1]);
862 buf[1] = SOCKS_CMD_CONNECT;
864 buf[3] = SOCKS_ADDR_FQDN;
865 buf[4] = (BYTE)hostnlen;
866 memcpy(buf + 5, hostname, hostnlen);
868 buf[hostnlen + 5] = (port >> 8) & 0xff;
869 buf[hostnlen + 6] = port & 0xff;
871 status = BIO_write(bufferedBio, buf, (
int)hostnlen + 7);
873 if ((status < 0) || ((
size_t)status != (hostnlen + 7U)))
875 WLog_ERR(TAG,
"SOCKS proxy: failed to write CONN REQ");
879 status = recv_socks_reply(context, bufferedBio, buf,
sizeof(buf),
"CONN REQ", 5);
886 WLog_INFO(TAG,
"Successfully connected to %s:%" PRIu16, hostname, port);
890 if (buf[1] > 0 && buf[1] < 9)
891 WLog_INFO(TAG,
"SOCKS Proxy replied: %s", rplstat[buf[1]]);
893 WLog_INFO(TAG,
"SOCKS Proxy replied: %" PRIu8
" status not listed in rfc1928", buf[1]);
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 const char * freerdp_settings_get_string(const rdpSettings *settings, FreeRDP_Settings_Keys_String id)
Returns a immutable string settings value.
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 BOOL freerdp_settings_set_uint16(rdpSettings *settings, FreeRDP_Settings_Keys_UInt16 id, UINT16 param)
Sets a UINT16 settings value.