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