Coverage Report

Created: 2024-09-08 06:20

/src/FreeRDP/libfreerdp/core/proxy.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * FreeRDP: A Remote Desktop Protocol Implementation
3
 * HTTP Proxy support
4
 *
5
 * Copyright 2016 Christian Plattner <ccpp@gmx.at>
6
 *
7
 * Licensed under the Apache License, Version 2.0 (the "License");
8
 * you may not use this file except in compliance with the License.
9
 * You may obtain a copy of the License at
10
 *
11
 *     http://www.apache.org/licenses/LICENSE-2.0
12
 *
13
 * Unless required by applicable law or agreed to in writing, software
14
 * distributed under the License is distributed on an "AS IS" BASIS,
15
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
 * See the License for the specific language governing permissions and
17
 * limitations under the License.
18
 */
19
20
#include <ctype.h>
21
#include <errno.h>
22
23
#include <openssl/err.h>
24
25
#include "settings.h"
26
#include "proxy.h"
27
#include <freerdp/settings.h>
28
#include <freerdp/utils/proxy_utils.h>
29
#include <freerdp/crypto/crypto.h>
30
#include "tcp.h"
31
32
#include <winpr/assert.h>
33
#include <winpr/environment.h> /* For GetEnvironmentVariableA */
34
35
0
#define CRLF "\r\n"
36
37
#include <freerdp/log.h>
38
#define TAG FREERDP_TAG("core.proxy")
39
40
/* SOCKS Proxy auth methods by rfc1928 */
41
enum
42
{
43
  AUTH_M_NO_AUTH = 0,
44
  AUTH_M_GSSAPI = 1,
45
  AUTH_M_USR_PASS = 2
46
};
47
48
enum
49
{
50
  SOCKS_CMD_CONNECT = 1,
51
  SOCKS_CMD_BIND = 2,
52
  SOCKS_CMD_UDP_ASSOCIATE = 3
53
};
54
55
enum
56
{
57
  SOCKS_ADDR_IPV4 = 1,
58
  SOCKS_ADDR_FQDN = 3,
59
  SOCKS_ADDR_IPV6 = 4,
60
};
61
62
/* CONN REQ replies in enum. order */
63
static const char* rplstat[] = { "succeeded",
64
                               "general SOCKS server failure",
65
                               "connection not allowed by ruleset",
66
                               "Network unreachable",
67
                               "Host unreachable",
68
                               "Connection refused",
69
                               "TTL expired",
70
                               "Command not supported",
71
                               "Address type not supported" };
72
73
static BOOL http_proxy_connect(BIO* bufferedBio, const char* proxyUsername,
74
                               const char* proxyPassword, const char* hostname, UINT16 port);
75
static BOOL socks_proxy_connect(BIO* bufferedBio, const char* proxyUsername,
76
                                const char* proxyPassword, const char* hostname, UINT16 port);
77
static void proxy_read_environment(rdpSettings* settings, char* envname);
78
79
BOOL proxy_prepare(rdpSettings* settings, const char** lpPeerHostname, UINT16* lpPeerPort,
80
                   const char** lpProxyUsername, const char** lpProxyPassword)
81
0
{
82
0
  if (freerdp_settings_get_uint32(settings, FreeRDP_ProxyType) == PROXY_TYPE_IGNORE)
83
0
    return FALSE;
84
85
  /* For TSGateway, find the system HTTPS proxy automatically */
86
0
  if (freerdp_settings_get_uint32(settings, FreeRDP_ProxyType) == PROXY_TYPE_NONE)
87
0
    proxy_read_environment(settings, "https_proxy");
88
89
0
  if (freerdp_settings_get_uint32(settings, FreeRDP_ProxyType) == PROXY_TYPE_NONE)
90
0
    proxy_read_environment(settings, "HTTPS_PROXY");
91
92
0
  if (freerdp_settings_get_uint32(settings, FreeRDP_ProxyType) != PROXY_TYPE_NONE)
93
0
    proxy_read_environment(settings, "no_proxy");
94
95
0
  if (freerdp_settings_get_uint32(settings, FreeRDP_ProxyType) != PROXY_TYPE_NONE)
96
0
    proxy_read_environment(settings, "NO_PROXY");
97
98
0
  if (freerdp_settings_get_uint32(settings, FreeRDP_ProxyType) != PROXY_TYPE_NONE)
99
0
  {
100
0
    *lpPeerHostname = freerdp_settings_get_string(settings, FreeRDP_ProxyHostname);
101
0
    *lpPeerPort = freerdp_settings_get_uint16(settings, FreeRDP_ProxyPort);
102
0
    *lpProxyUsername = freerdp_settings_get_string(settings, FreeRDP_ProxyUsername);
103
0
    *lpProxyPassword = freerdp_settings_get_string(settings, FreeRDP_ProxyPassword);
104
0
    return TRUE;
105
0
  }
106
107
0
  return FALSE;
108
0
}
109
110
static BOOL value_to_int(const char* value, LONGLONG* result, LONGLONG min, LONGLONG max)
111
0
{
112
0
  long long rc = 0;
113
114
0
  if (!value || !result)
115
0
    return FALSE;
116
117
0
  errno = 0;
118
0
  rc = _strtoi64(value, NULL, 0);
119
120
0
  if (errno != 0)
121
0
    return FALSE;
122
123
0
  if ((rc < min) || (rc > max))
124
0
    return FALSE;
125
126
0
  *result = rc;
127
0
  return TRUE;
128
0
}
129
130
static BOOL cidr4_match(const struct in_addr* addr, const struct in_addr* net, BYTE bits)
131
0
{
132
0
  uint32_t mask = 0;
133
0
  uint32_t amask = 0;
134
0
  uint32_t nmask = 0;
135
136
0
  if (bits == 0)
137
0
    return TRUE;
138
139
0
  mask = htonl(0xFFFFFFFFu << (32 - bits));
140
0
  amask = addr->s_addr & mask;
141
0
  nmask = net->s_addr & mask;
142
0
  return amask == nmask;
143
0
}
144
145
static BOOL cidr6_match(const struct in6_addr* address, const struct in6_addr* network,
146
                        uint8_t bits)
147
0
{
148
0
  const uint32_t* a = (const uint32_t*)address;
149
0
  const uint32_t* n = (const uint32_t*)network;
150
0
  size_t bits_whole = 0;
151
0
  size_t bits_incomplete = 0;
152
0
  bits_whole = bits >> 5;
153
0
  bits_incomplete = bits & 0x1F;
154
155
0
  if (bits_whole)
156
0
  {
157
0
    if (memcmp(a, n, bits_whole << 2) != 0)
158
0
      return FALSE;
159
0
  }
160
161
0
  if (bits_incomplete)
162
0
  {
163
0
    uint32_t mask = htonl((0xFFFFFFFFu) << (32 - bits_incomplete));
164
165
0
    if ((a[bits_whole] ^ n[bits_whole]) & mask)
166
0
      return FALSE;
167
0
  }
168
169
0
  return TRUE;
170
0
}
171
172
static BOOL check_no_proxy(rdpSettings* settings, const char* no_proxy)
173
0
{
174
0
  const char* delimiter = ",";
175
0
  BOOL result = FALSE;
176
0
  char* current = NULL;
177
0
  char* copy = NULL;
178
0
  char* context = NULL;
179
0
  size_t host_len = 0;
180
0
  struct sockaddr_in sa4;
181
0
  struct sockaddr_in6 sa6;
182
0
  BOOL is_ipv4 = FALSE;
183
0
  BOOL is_ipv6 = FALSE;
184
185
0
  if (!no_proxy || !settings)
186
0
    return FALSE;
187
188
0
  if (inet_pton(AF_INET, settings->ServerHostname, &sa4.sin_addr) == 1)
189
0
    is_ipv4 = TRUE;
190
0
  else if (inet_pton(AF_INET6, settings->ServerHostname, &sa6.sin6_addr) == 1)
191
0
    is_ipv6 = TRUE;
192
193
0
  host_len = strlen(settings->ServerHostname);
194
0
  copy = _strdup(no_proxy);
195
196
0
  if (!copy)
197
0
    return FALSE;
198
199
0
  current = strtok_s(copy, delimiter, &context);
200
201
0
  while (current && !result)
202
0
  {
203
0
    const size_t currentlen = strlen(current);
204
205
0
    if (currentlen > 0)
206
0
    {
207
0
      WLog_DBG(TAG, "%s => %s (%" PRIdz ")", settings->ServerHostname, current, currentlen);
208
209
      /* detect left and right "*" wildcard */
210
0
      if (current[0] == '*')
211
0
      {
212
0
        if (host_len >= currentlen)
213
0
        {
214
0
          const size_t offset = host_len + 1 - currentlen;
215
0
          const char* name = settings->ServerHostname + offset;
216
217
0
          if (strncmp(current + 1, name, currentlen - 1) == 0)
218
0
            result = TRUE;
219
0
        }
220
0
      }
221
0
      else if (current[currentlen - 1] == '*')
222
0
      {
223
0
        if (strncmp(current, settings->ServerHostname, currentlen - 1) == 0)
224
0
          result = TRUE;
225
0
      }
226
0
      else if (current[0] ==
227
0
               '.') /* Only compare if the no_proxy variable contains a whole domain. */
228
0
      {
229
0
        if (host_len > currentlen)
230
0
        {
231
0
          const size_t offset = host_len - currentlen;
232
0
          const char* name = settings->ServerHostname + offset;
233
234
0
          if (strncmp(current, name, currentlen) == 0)
235
0
            result = TRUE; /* right-aligned match for host names */
236
0
        }
237
0
      }
238
0
      else if (strcmp(current, settings->ServerHostname) == 0)
239
0
        result = TRUE; /* exact match */
240
0
      else if (is_ipv4 || is_ipv6)
241
0
      {
242
0
        char* rangedelim = strchr(current, '/');
243
244
        /* Check for IP ranges */
245
0
        if (rangedelim != NULL)
246
0
        {
247
0
          const char* range = rangedelim + 1;
248
0
          unsigned sub = 0;
249
0
          int rc = sscanf(range, "%u", &sub);
250
251
0
          if ((rc == 1) && (rc >= 0) && (sub <= UINT8_MAX))
252
0
          {
253
0
            *rangedelim = '\0';
254
255
0
            if (is_ipv4)
256
0
            {
257
0
              struct sockaddr_in mask;
258
259
0
              if (inet_pton(AF_INET, current, &mask.sin_addr))
260
0
                result = cidr4_match(&sa4.sin_addr, &mask.sin_addr, sub);
261
0
            }
262
0
            else if (is_ipv6)
263
0
            {
264
0
              struct sockaddr_in6 mask;
265
266
0
              if (inet_pton(AF_INET6, current, &mask.sin6_addr))
267
0
                result = cidr6_match(&sa6.sin6_addr, &mask.sin6_addr, sub);
268
0
            }
269
0
          }
270
0
          else
271
0
            WLog_WARN(TAG, "NO_PROXY invalid entry %s", current);
272
0
        }
273
0
        else if (strncmp(current, settings->ServerHostname, currentlen) == 0)
274
0
          result = TRUE; /* left-aligned match for IPs */
275
0
      }
276
0
    }
277
278
0
    current = strtok_s(NULL, delimiter, &context);
279
0
  }
280
281
0
  free(copy);
282
0
  return result;
283
0
}
284
285
void proxy_read_environment(rdpSettings* settings, char* envname)
286
0
{
287
0
  const DWORD envlen = GetEnvironmentVariableA(envname, NULL, 0);
288
289
0
  if (!envlen || (envlen <= 1))
290
0
    return;
291
292
0
  char* env = calloc(1, envlen);
293
294
0
  if (!env)
295
0
  {
296
0
    WLog_ERR(TAG, "Not enough memory");
297
0
    return;
298
0
  }
299
300
0
  if (GetEnvironmentVariableA(envname, env, envlen) == envlen - 1)
301
0
  {
302
0
    if (_strnicmp("NO_PROXY", envname, 9) == 0)
303
0
    {
304
0
      if (check_no_proxy(settings, env))
305
0
      {
306
0
        WLog_INFO(TAG, "deactivating proxy: %s [%s=%s]",
307
0
                  freerdp_settings_get_string(settings, FreeRDP_ServerHostname), envname,
308
0
                  env);
309
0
        if (!freerdp_settings_set_uint32(settings, FreeRDP_ProxyType, PROXY_TYPE_NONE))
310
0
          WLog_WARN(TAG, "failed to set FreeRDP_ProxyType=PROXY_TYPE_NONE");
311
0
      }
312
0
    }
313
0
    else
314
0
    {
315
0
      if (!proxy_parse_uri(settings, env))
316
0
      {
317
0
        WLog_WARN(
318
0
            TAG, "Error while parsing proxy URI from environment variable; ignoring proxy");
319
0
      }
320
0
    }
321
0
  }
322
323
0
  free(env);
324
0
}
325
326
BOOL proxy_parse_uri(rdpSettings* settings, const char* uri_in)
327
0
{
328
0
  BOOL rc = FALSE;
329
0
  const char* protocol = "";
330
0
  UINT16 port = 0;
331
0
  char* p = NULL;
332
0
  char* atPtr = NULL;
333
0
  char* uri_copy = _strdup(uri_in);
334
0
  char* uri = uri_copy;
335
0
  if (!uri)
336
0
    goto fail;
337
338
0
  p = strstr(uri, "://");
339
340
0
  if (p)
341
0
  {
342
0
    *p = '\0';
343
344
0
    if (_stricmp("no_proxy", uri) == 0)
345
0
    {
346
0
      if (!freerdp_settings_set_uint32(settings, FreeRDP_ProxyType, PROXY_TYPE_IGNORE))
347
0
        goto fail;
348
0
    }
349
0
    if (_stricmp("http", uri) == 0)
350
0
    {
351
0
      if (!freerdp_settings_set_uint32(settings, FreeRDP_ProxyType, PROXY_TYPE_HTTP))
352
0
        goto fail;
353
0
      protocol = "http";
354
0
    }
355
0
    else if (_stricmp("socks5", uri) == 0)
356
0
    {
357
0
      if (!freerdp_settings_set_uint32(settings, FreeRDP_ProxyType, PROXY_TYPE_SOCKS))
358
0
        goto fail;
359
0
      protocol = "socks5";
360
0
    }
361
0
    else
362
0
    {
363
0
      WLog_ERR(TAG, "Only HTTP and SOCKS5 proxies supported by now");
364
0
      goto fail;
365
0
    }
366
367
0
    uri = p + 3;
368
0
  }
369
0
  else
370
0
  {
371
    /* default proxy protocol is http */
372
0
    if (!freerdp_settings_set_uint32(settings, FreeRDP_ProxyType, PROXY_TYPE_HTTP))
373
0
      goto fail;
374
0
    protocol = "http";
375
0
  }
376
377
  /* uri is now [user:password@]hostname:port */
378
0
  atPtr = strrchr(uri, '@');
379
380
0
  if (atPtr)
381
0
  {
382
    /* got a login / password,
383
     *         atPtr
384
     *         v
385
     * [user:password@]hostname:port
386
     *    ^
387
     *    colonPtr
388
     */
389
0
    char* colonPtr = strchr(uri, ':');
390
391
0
    if (!colonPtr || (colonPtr > atPtr))
392
0
    {
393
0
      WLog_ERR(TAG, "invalid syntax for proxy (contains no password)");
394
0
      goto fail;
395
0
    }
396
397
0
    *colonPtr = '\0';
398
0
    if (!freerdp_settings_set_string(settings, FreeRDP_ProxyUsername, uri))
399
0
    {
400
0
      WLog_ERR(TAG, "unable to allocate proxy username");
401
0
      goto fail;
402
0
    }
403
404
0
    *atPtr = '\0';
405
406
0
    if (!freerdp_settings_set_string(settings, FreeRDP_ProxyPassword, colonPtr + 1))
407
0
    {
408
0
      WLog_ERR(TAG, "unable to allocate proxy password");
409
0
      goto fail;
410
0
    }
411
412
0
    uri = atPtr + 1;
413
0
  }
414
415
0
  p = strchr(uri, ':');
416
417
0
  if (p)
418
0
  {
419
0
    LONGLONG val = 0;
420
421
0
    if (!value_to_int(&p[1], &val, 0, UINT16_MAX))
422
0
    {
423
0
      WLog_ERR(TAG, "invalid syntax for proxy (invalid port)");
424
0
      goto fail;
425
0
    }
426
427
0
    if (val == 0)
428
0
    {
429
0
      WLog_ERR(TAG, "invalid syntax for proxy (port missing)");
430
0
      goto fail;
431
0
    }
432
433
0
    port = (UINT16)val;
434
0
    *p = '\0';
435
0
  }
436
0
  else
437
0
  {
438
0
    if (_stricmp("http", protocol) == 0)
439
0
    {
440
      /* The default is 80. Also for Proxys. */
441
0
      port = 80;
442
0
    }
443
0
    else
444
0
    {
445
0
      port = 1080;
446
0
    }
447
448
0
    WLog_DBG(TAG, "setting default proxy port: %" PRIu16, port);
449
0
  }
450
451
0
  if (!freerdp_settings_set_uint16(settings, FreeRDP_ProxyPort, port))
452
0
    goto fail;
453
454
0
  p = strchr(uri, '/');
455
0
  if (p)
456
0
    *p = '\0';
457
0
  if (!freerdp_settings_set_string(settings, FreeRDP_ProxyHostname, uri))
458
0
    goto fail;
459
460
0
  if (_stricmp("", uri) == 0)
461
0
  {
462
0
    WLog_ERR(TAG, "invalid syntax for proxy (hostname missing)");
463
0
    goto fail;
464
0
  }
465
466
0
  if (freerdp_settings_get_string(settings, FreeRDP_ProxyUsername))
467
0
  {
468
0
    WLog_INFO(TAG, "Parsed proxy configuration: %s://%s:%s@%s:%" PRIu16, protocol,
469
0
              freerdp_settings_get_string(settings, FreeRDP_ProxyUsername), "******",
470
0
              freerdp_settings_get_string(settings, FreeRDP_ProxyHostname),
471
0
              freerdp_settings_get_uint16(settings, FreeRDP_ProxyPort));
472
0
  }
473
0
  else
474
0
  {
475
0
    WLog_INFO(TAG, "Parsed proxy configuration: %s://%s:%" PRIu16, protocol,
476
0
              freerdp_settings_get_string(settings, FreeRDP_ProxyHostname),
477
0
              freerdp_settings_get_uint16(settings, FreeRDP_ProxyPort));
478
0
  }
479
0
  rc = TRUE;
480
481
0
fail:
482
0
  free(uri_copy);
483
0
  return rc;
484
0
}
485
486
BOOL proxy_connect(rdpSettings* settings, BIO* bufferedBio, const char* proxyUsername,
487
                   const char* proxyPassword, const char* hostname, UINT16 port)
488
0
{
489
0
  switch (freerdp_settings_get_uint32(settings, FreeRDP_ProxyType))
490
0
  {
491
0
    case PROXY_TYPE_NONE:
492
0
    case PROXY_TYPE_IGNORE:
493
0
      return TRUE;
494
495
0
    case PROXY_TYPE_HTTP:
496
0
      return http_proxy_connect(bufferedBio, proxyUsername, proxyPassword, hostname, port);
497
498
0
    case PROXY_TYPE_SOCKS:
499
0
      return socks_proxy_connect(bufferedBio, proxyUsername, proxyPassword, hostname, port);
500
501
0
    default:
502
0
      WLog_ERR(TAG, "Invalid internal proxy configuration");
503
0
      return FALSE;
504
0
  }
505
0
}
506
507
static const char* get_response_header(char* response)
508
0
{
509
0
  char* current_pos = strchr(response, '\r');
510
0
  if (!current_pos)
511
0
    current_pos = strchr(response, '\n');
512
513
0
  if (current_pos)
514
0
    *current_pos = '\0';
515
516
0
  return response;
517
0
}
518
519
static BOOL http_proxy_connect(BIO* bufferedBio, const char* proxyUsername,
520
                               const char* proxyPassword, const char* hostname, UINT16 port)
521
0
{
522
0
  BOOL rc = FALSE;
523
0
  int status = 0;
524
0
  wStream* s = NULL;
525
0
  char port_str[10] = { 0 };
526
0
  char recv_buf[256] = { 0 };
527
0
  char* eol = NULL;
528
0
  size_t resultsize = 0;
529
0
  size_t reserveSize = 0;
530
0
  size_t portLen = 0;
531
0
  size_t hostLen = 0;
532
0
  const char connect[] = "CONNECT ";
533
0
  const char httpheader[] = " HTTP/1.1" CRLF "Host: ";
534
535
0
  WINPR_ASSERT(bufferedBio);
536
0
  WINPR_ASSERT(hostname);
537
538
0
  _itoa_s(port, port_str, sizeof(port_str), 10);
539
540
0
  hostLen = strlen(hostname);
541
0
  portLen = strnlen(port_str, sizeof(port_str));
542
0
  reserveSize = strlen(connect) + (hostLen + 1 + portLen) * 2 + strlen(httpheader);
543
0
  s = Stream_New(NULL, reserveSize);
544
0
  if (!s)
545
0
    goto fail;
546
547
0
  Stream_Write(s, connect, strlen(connect));
548
0
  Stream_Write(s, hostname, hostLen);
549
0
  Stream_Write_UINT8(s, ':');
550
0
  Stream_Write(s, port_str, portLen);
551
0
  Stream_Write(s, httpheader, strlen(httpheader));
552
0
  Stream_Write(s, hostname, hostLen);
553
0
  Stream_Write_UINT8(s, ':');
554
0
  Stream_Write(s, port_str, portLen);
555
556
0
  if (proxyUsername && proxyPassword)
557
0
  {
558
0
    const int length = _scprintf("%s:%s", proxyUsername, proxyPassword);
559
0
    if (length > 0)
560
0
    {
561
0
      const size_t size = (size_t)length + 1ull;
562
0
      char* creds = (char*)malloc(size);
563
564
0
      if (!creds)
565
0
        goto fail;
566
0
      else
567
0
      {
568
0
        const char basic[] = CRLF "Proxy-Authorization: Basic ";
569
0
        char* base64 = NULL;
570
571
0
        (void)sprintf_s(creds, size, "%s:%s", proxyUsername, proxyPassword);
572
0
        base64 = crypto_base64_encode((const BYTE*)creds, size - 1);
573
574
0
        if (!base64 || !Stream_EnsureRemainingCapacity(s, strlen(basic) + strlen(base64)))
575
0
        {
576
0
          free(base64);
577
0
          free(creds);
578
0
          goto fail;
579
0
        }
580
0
        Stream_Write(s, basic, strlen(basic));
581
0
        Stream_Write(s, base64, strlen(base64));
582
583
0
        free(base64);
584
0
      }
585
0
      free(creds);
586
0
    }
587
0
  }
588
589
0
  if (!Stream_EnsureRemainingCapacity(s, 4))
590
0
    goto fail;
591
592
0
  Stream_Write(s, CRLF CRLF, 4);
593
0
  ERR_clear_error();
594
0
  status = BIO_write(bufferedBio, Stream_Buffer(s), Stream_GetPosition(s));
595
596
0
  if ((status < 0) || ((size_t)status != Stream_GetPosition(s)))
597
0
  {
598
0
    WLog_ERR(TAG, "HTTP proxy: failed to write CONNECT request");
599
0
    goto fail;
600
0
  }
601
602
  /* Read result until CR-LF-CR-LF.
603
   * Keep recv_buf a null-terminated string. */
604
0
  while (strstr(recv_buf, CRLF CRLF) == NULL)
605
0
  {
606
0
    if (resultsize >= sizeof(recv_buf) - 1)
607
0
    {
608
0
      WLog_ERR(TAG, "HTTP Reply headers too long: %s", get_response_header(recv_buf));
609
0
      goto fail;
610
0
    }
611
612
0
    ERR_clear_error();
613
0
    status =
614
0
        BIO_read(bufferedBio, (BYTE*)recv_buf + resultsize, sizeof(recv_buf) - resultsize - 1);
615
616
0
    if (status < 0)
617
0
    {
618
      /* Error? */
619
0
      if (BIO_should_retry(bufferedBio))
620
0
      {
621
0
        USleep(100);
622
0
        continue;
623
0
      }
624
625
0
      WLog_ERR(TAG, "Failed reading reply from HTTP proxy (Status %d)", status);
626
0
      goto fail;
627
0
    }
628
0
    else if (status == 0)
629
0
    {
630
      /* Error? */
631
0
      WLog_ERR(TAG, "Failed reading reply from HTTP proxy (BIO_read returned zero)");
632
0
      goto fail;
633
0
    }
634
635
0
    resultsize += status;
636
0
  }
637
638
  /* Extract HTTP status line */
639
0
  eol = strchr(recv_buf, '\r');
640
641
0
  if (!eol)
642
0
  {
643
    /* should never happen */
644
0
    goto fail;
645
0
  }
646
647
0
  *eol = '\0';
648
0
  WLog_INFO(TAG, "HTTP Proxy: %s", recv_buf);
649
650
0
  if (strnlen(recv_buf, sizeof(recv_buf)) < 12)
651
0
    goto fail;
652
653
0
  recv_buf[7] = 'X';
654
655
0
  if (strncmp(recv_buf, "HTTP/1.X 200", 12) != 0)
656
0
    goto fail;
657
658
0
  rc = TRUE;
659
0
fail:
660
0
  Stream_Free(s, TRUE);
661
0
  return rc;
662
0
}
663
664
static int recv_socks_reply(BIO* bufferedBio, BYTE* buf, int len, char* reason, BYTE checkVer)
665
0
{
666
0
  int status = 0;
667
668
0
  for (;;)
669
0
  {
670
0
    ERR_clear_error();
671
0
    status = BIO_read(bufferedBio, buf, len);
672
673
0
    if (status > 0)
674
0
    {
675
0
      break;
676
0
    }
677
0
    else if (status < 0)
678
0
    {
679
      /* Error? */
680
0
      if (BIO_should_retry(bufferedBio))
681
0
      {
682
0
        USleep(100);
683
0
        continue;
684
0
      }
685
686
0
      WLog_ERR(TAG, "Failed reading %s reply from SOCKS proxy (Status %d)", reason, status);
687
0
      return -1;
688
0
    }
689
0
    else // if (status == 0)
690
0
    {
691
      /* Error? */
692
0
      WLog_ERR(TAG, "Failed reading %s reply from SOCKS proxy (BIO_read returned zero)",
693
0
               reason);
694
0
      return -1;
695
0
    }
696
0
  }
697
698
0
  if (status < 2)
699
0
  {
700
0
    WLog_ERR(TAG, "SOCKS Proxy reply packet too short (%s)", reason);
701
0
    return -1;
702
0
  }
703
704
0
  if (buf[0] != checkVer)
705
0
  {
706
0
    WLog_ERR(TAG, "SOCKS Proxy version is not 5 (%s)", reason);
707
0
    return -1;
708
0
  }
709
710
0
  return status;
711
0
}
712
713
static BOOL socks_proxy_connect(BIO* bufferedBio, const char* proxyUsername,
714
                                const char* proxyPassword, const char* hostname, UINT16 port)
715
0
{
716
0
  int status = 0;
717
0
  int nauthMethods = 1;
718
0
  int writeLen = 3;
719
0
  BYTE buf[3 + 255 + 255]; /* biggest packet is user/pass auth */
720
0
  size_t hostnlen = strnlen(hostname, 255);
721
722
0
  if (proxyUsername && proxyPassword)
723
0
  {
724
0
    nauthMethods++;
725
0
    writeLen++;
726
0
  }
727
728
  /* select auth. method */
729
0
  buf[0] = 5;            /* SOCKS version */
730
0
  buf[1] = nauthMethods; /* #of methods offered */
731
0
  buf[2] = AUTH_M_NO_AUTH;
732
733
0
  if (nauthMethods > 1)
734
0
    buf[3] = AUTH_M_USR_PASS;
735
736
0
  ERR_clear_error();
737
0
  status = BIO_write(bufferedBio, buf, writeLen);
738
739
0
  if (status != writeLen)
740
0
  {
741
0
    WLog_ERR(TAG, "SOCKS proxy: failed to write AUTH METHOD request");
742
0
    return FALSE;
743
0
  }
744
745
0
  status = recv_socks_reply(bufferedBio, buf, 2, "AUTH REQ", 5);
746
747
0
  if (status <= 0)
748
0
    return FALSE;
749
750
0
  switch (buf[1])
751
0
  {
752
0
    case AUTH_M_NO_AUTH:
753
0
      WLog_DBG(TAG, "SOCKS Proxy: (NO AUTH) method was selected");
754
0
      break;
755
756
0
    case AUTH_M_USR_PASS:
757
0
      if (!proxyUsername || !proxyPassword)
758
0
        return FALSE;
759
0
      else
760
0
      {
761
0
        int usernameLen = strnlen(proxyUsername, 255);
762
0
        int userpassLen = strnlen(proxyPassword, 255);
763
0
        BYTE* ptr = NULL;
764
765
0
        if (nauthMethods < 2)
766
0
        {
767
0
          WLog_ERR(TAG, "SOCKS Proxy: USER/PASS method was not proposed to server");
768
0
          return FALSE;
769
0
        }
770
771
        /* user/password v1 method */
772
0
        ptr = buf + 2;
773
0
        buf[0] = 1;
774
0
        buf[1] = usernameLen;
775
0
        memcpy(ptr, proxyUsername, usernameLen);
776
0
        ptr += usernameLen;
777
0
        *ptr = userpassLen;
778
0
        ptr++;
779
0
        memcpy(ptr, proxyPassword, userpassLen);
780
0
        ERR_clear_error();
781
0
        status = BIO_write(bufferedBio, buf, 3 + usernameLen + userpassLen);
782
783
0
        if (status != 3 + usernameLen + userpassLen)
784
0
        {
785
0
          WLog_ERR(TAG, "SOCKS Proxy: error writing user/password request");
786
0
          return FALSE;
787
0
        }
788
789
0
        status = recv_socks_reply(bufferedBio, buf, 2, "AUTH REQ", 1);
790
791
0
        if (status < 2)
792
0
          return FALSE;
793
794
0
        if (buf[1] != 0x00)
795
0
        {
796
0
          WLog_ERR(TAG, "SOCKS Proxy: invalid user/password");
797
0
          return FALSE;
798
0
        }
799
0
      }
800
801
0
      break;
802
803
0
    default:
804
0
      WLog_ERR(TAG, "SOCKS Proxy: unknown method 0x%x was selected by proxy", buf[1]);
805
0
      return FALSE;
806
0
  }
807
808
  /* CONN request */
809
0
  buf[0] = 5;                 /* SOCKS version */
810
0
  buf[1] = SOCKS_CMD_CONNECT; /* command */
811
0
  buf[2] = 0;                 /* 3rd octet is reserved x00 */
812
0
  buf[3] = SOCKS_ADDR_FQDN;   /* addr.type */
813
0
  buf[4] = hostnlen;          /* DST.ADDR */
814
0
  memcpy(buf + 5, hostname, hostnlen);
815
  /* follows DST.PORT in netw. format */
816
0
  buf[hostnlen + 5] = (port >> 8) & 0xff;
817
0
  buf[hostnlen + 6] = port & 0xff;
818
0
  ERR_clear_error();
819
0
  status = BIO_write(bufferedBio, buf, hostnlen + 7U);
820
821
0
  if ((status < 0) || ((size_t)status != (hostnlen + 7U)))
822
0
  {
823
0
    WLog_ERR(TAG, "SOCKS proxy: failed to write CONN REQ");
824
0
    return FALSE;
825
0
  }
826
827
0
  status = recv_socks_reply(bufferedBio, buf, sizeof(buf), "CONN REQ", 5);
828
829
0
  if (status < 4)
830
0
    return FALSE;
831
832
0
  if (buf[1] == 0)
833
0
  {
834
0
    WLog_INFO(TAG, "Successfully connected to %s:%" PRIu16, hostname, port);
835
0
    return TRUE;
836
0
  }
837
838
0
  if (buf[1] > 0 && buf[1] < 9)
839
0
    WLog_INFO(TAG, "SOCKS Proxy replied: %s", rplstat[buf[1]]);
840
0
  else
841
0
    WLog_INFO(TAG, "SOCKS Proxy replied: %" PRIu8 " status not listed in rfc1928", buf[1]);
842
843
0
  return FALSE;
844
0
}