Coverage Report

Created: 2024-05-20 06:11

/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
        freerdp_settings_set_uint32(settings, FreeRDP_ProxyType, PROXY_TYPE_NONE);
310
0
      }
311
0
    }
312
0
    else
313
0
    {
314
0
      if (!proxy_parse_uri(settings, env))
315
0
      {
316
0
        WLog_WARN(
317
0
            TAG, "Error while parsing proxy URI from environment variable; ignoring proxy");
318
0
      }
319
0
    }
320
0
  }
321
322
0
  free(env);
323
0
}
324
325
BOOL proxy_parse_uri(rdpSettings* settings, const char* uri_in)
326
0
{
327
0
  BOOL rc = FALSE;
328
0
  const char* protocol = "";
329
0
  UINT16 port = 0;
330
0
  char* p = NULL;
331
0
  char* atPtr = NULL;
332
0
  char* uri_copy = _strdup(uri_in);
333
0
  char* uri = uri_copy;
334
0
  if (!uri)
335
0
    goto fail;
336
337
0
  p = strstr(uri, "://");
338
339
0
  if (p)
340
0
  {
341
0
    *p = '\0';
342
343
0
    if (_stricmp("no_proxy", uri) == 0)
344
0
    {
345
0
      if (!freerdp_settings_set_uint32(settings, FreeRDP_ProxyType, PROXY_TYPE_IGNORE))
346
0
        goto fail;
347
0
    }
348
0
    if (_stricmp("http", uri) == 0)
349
0
    {
350
0
      if (!freerdp_settings_set_uint32(settings, FreeRDP_ProxyType, PROXY_TYPE_HTTP))
351
0
        goto fail;
352
0
      protocol = "http";
353
0
    }
354
0
    else if (_stricmp("socks5", uri) == 0)
355
0
    {
356
0
      if (!freerdp_settings_set_uint32(settings, FreeRDP_ProxyType, PROXY_TYPE_SOCKS))
357
0
        goto fail;
358
0
      protocol = "socks5";
359
0
    }
360
0
    else
361
0
    {
362
0
      WLog_ERR(TAG, "Only HTTP and SOCKS5 proxies supported by now");
363
0
      goto fail;
364
0
    }
365
366
0
    uri = p + 3;
367
0
  }
368
0
  else
369
0
  {
370
    /* default proxy protocol is http */
371
0
    if (!freerdp_settings_set_uint32(settings, FreeRDP_ProxyType, PROXY_TYPE_HTTP))
372
0
      goto fail;
373
0
    protocol = "http";
374
0
  }
375
376
  /* uri is now [user:password@]hostname:port */
377
0
  atPtr = strrchr(uri, '@');
378
379
0
  if (atPtr)
380
0
  {
381
    /* got a login / password,
382
     *         atPtr
383
     *         v
384
     * [user:password@]hostname:port
385
     *    ^
386
     *    colonPtr
387
     */
388
0
    char* colonPtr = strchr(uri, ':');
389
390
0
    if (!colonPtr || (colonPtr > atPtr))
391
0
    {
392
0
      WLog_ERR(TAG, "invalid syntax for proxy (contains no password)");
393
0
      goto fail;
394
0
    }
395
396
0
    *colonPtr = '\0';
397
0
    if (!freerdp_settings_set_string(settings, FreeRDP_ProxyUsername, uri))
398
0
    {
399
0
      WLog_ERR(TAG, "unable to allocate proxy username");
400
0
      goto fail;
401
0
    }
402
403
0
    *atPtr = '\0';
404
405
0
    if (!freerdp_settings_set_string(settings, FreeRDP_ProxyPassword, colonPtr + 1))
406
0
    {
407
0
      WLog_ERR(TAG, "unable to allocate proxy password");
408
0
      goto fail;
409
0
    }
410
411
0
    uri = atPtr + 1;
412
0
  }
413
414
0
  p = strchr(uri, ':');
415
416
0
  if (p)
417
0
  {
418
0
    LONGLONG val = 0;
419
420
0
    if (!value_to_int(&p[1], &val, 0, UINT16_MAX))
421
0
    {
422
0
      WLog_ERR(TAG, "invalid syntax for proxy (invalid port)");
423
0
      goto fail;
424
0
    }
425
426
0
    if (val == 0)
427
0
    {
428
0
      WLog_ERR(TAG, "invalid syntax for proxy (port missing)");
429
0
      goto fail;
430
0
    }
431
432
0
    port = (UINT16)val;
433
0
    *p = '\0';
434
0
  }
435
0
  else
436
0
  {
437
0
    if (_stricmp("http", protocol) == 0)
438
0
    {
439
      /* The default is 80. Also for Proxys. */
440
0
      port = 80;
441
0
    }
442
0
    else
443
0
    {
444
0
      port = 1080;
445
0
    }
446
447
0
    WLog_DBG(TAG, "setting default proxy port: %" PRIu16, port);
448
0
  }
449
450
0
  if (!freerdp_settings_set_uint16(settings, FreeRDP_ProxyPort, port))
451
0
    goto fail;
452
453
0
  p = strchr(uri, '/');
454
0
  if (p)
455
0
    *p = '\0';
456
0
  if (!freerdp_settings_set_string(settings, FreeRDP_ProxyHostname, uri))
457
0
    goto fail;
458
459
0
  if (_stricmp("", uri) == 0)
460
0
  {
461
0
    WLog_ERR(TAG, "invalid syntax for proxy (hostname missing)");
462
0
    goto fail;
463
0
  }
464
465
0
  if (freerdp_settings_get_string(settings, FreeRDP_ProxyUsername))
466
0
  {
467
0
    WLog_INFO(TAG, "Parsed proxy configuration: %s://%s:%s@%s:%" PRIu16, protocol,
468
0
              freerdp_settings_get_string(settings, FreeRDP_ProxyUsername), "******",
469
0
              freerdp_settings_get_string(settings, FreeRDP_ProxyHostname),
470
0
              freerdp_settings_get_uint16(settings, FreeRDP_ProxyPort));
471
0
  }
472
0
  else
473
0
  {
474
0
    WLog_INFO(TAG, "Parsed proxy configuration: %s://%s:%" PRIu16, protocol,
475
0
              freerdp_settings_get_string(settings, FreeRDP_ProxyHostname),
476
0
              freerdp_settings_get_uint16(settings, FreeRDP_ProxyPort));
477
0
  }
478
0
  rc = TRUE;
479
480
0
fail:
481
0
  free(uri_copy);
482
0
  return rc;
483
0
}
484
485
BOOL proxy_connect(rdpSettings* settings, BIO* bufferedBio, const char* proxyUsername,
486
                   const char* proxyPassword, const char* hostname, UINT16 port)
487
0
{
488
0
  switch (freerdp_settings_get_uint32(settings, FreeRDP_ProxyType))
489
0
  {
490
0
    case PROXY_TYPE_NONE:
491
0
    case PROXY_TYPE_IGNORE:
492
0
      return TRUE;
493
494
0
    case PROXY_TYPE_HTTP:
495
0
      return http_proxy_connect(bufferedBio, proxyUsername, proxyPassword, hostname, port);
496
497
0
    case PROXY_TYPE_SOCKS:
498
0
      return socks_proxy_connect(bufferedBio, proxyUsername, proxyPassword, hostname, port);
499
500
0
    default:
501
0
      WLog_ERR(TAG, "Invalid internal proxy configuration");
502
0
      return FALSE;
503
0
  }
504
0
}
505
506
static const char* get_response_header(char* response)
507
0
{
508
0
  char* current_pos = strchr(response, '\r');
509
0
  if (!current_pos)
510
0
    current_pos = strchr(response, '\n');
511
512
0
  if (current_pos)
513
0
    *current_pos = '\0';
514
515
0
  return response;
516
0
}
517
518
static BOOL http_proxy_connect(BIO* bufferedBio, const char* proxyUsername,
519
                               const char* proxyPassword, const char* hostname, UINT16 port)
520
0
{
521
0
  BOOL rc = FALSE;
522
0
  int status = 0;
523
0
  wStream* s = NULL;
524
0
  char port_str[10] = { 0 };
525
0
  char recv_buf[256] = { 0 };
526
0
  char* eol = NULL;
527
0
  size_t resultsize = 0;
528
0
  size_t reserveSize = 0;
529
0
  size_t portLen = 0;
530
0
  size_t hostLen = 0;
531
0
  const char connect[] = "CONNECT ";
532
0
  const char httpheader[] = " HTTP/1.1" CRLF "Host: ";
533
534
0
  WINPR_ASSERT(bufferedBio);
535
0
  WINPR_ASSERT(hostname);
536
537
0
  _itoa_s(port, port_str, sizeof(port_str), 10);
538
539
0
  hostLen = strlen(hostname);
540
0
  portLen = strnlen(port_str, sizeof(port_str));
541
0
  reserveSize = strlen(connect) + (hostLen + 1 + portLen) * 2 + strlen(httpheader);
542
0
  s = Stream_New(NULL, reserveSize);
543
0
  if (!s)
544
0
    goto fail;
545
546
0
  Stream_Write(s, connect, strlen(connect));
547
0
  Stream_Write(s, hostname, hostLen);
548
0
  Stream_Write_UINT8(s, ':');
549
0
  Stream_Write(s, port_str, portLen);
550
0
  Stream_Write(s, httpheader, strlen(httpheader));
551
0
  Stream_Write(s, hostname, hostLen);
552
0
  Stream_Write_UINT8(s, ':');
553
0
  Stream_Write(s, port_str, portLen);
554
555
0
  if (proxyUsername && proxyPassword)
556
0
  {
557
0
    const int length = _scprintf("%s:%s", proxyUsername, proxyPassword);
558
0
    if (length > 0)
559
0
    {
560
0
      const size_t size = (size_t)length + 1ull;
561
0
      char* creds = (char*)malloc(size);
562
563
0
      if (!creds)
564
0
        goto fail;
565
0
      else
566
0
      {
567
0
        const char basic[] = CRLF "Proxy-Authorization: Basic ";
568
0
        char* base64 = NULL;
569
570
0
        sprintf_s(creds, size, "%s:%s", proxyUsername, proxyPassword);
571
0
        base64 = crypto_base64_encode((const BYTE*)creds, size - 1);
572
573
0
        if (!base64 || !Stream_EnsureRemainingCapacity(s, strlen(basic) + strlen(base64)))
574
0
        {
575
0
          free(base64);
576
0
          free(creds);
577
0
          goto fail;
578
0
        }
579
0
        Stream_Write(s, basic, strlen(basic));
580
0
        Stream_Write(s, base64, strlen(base64));
581
582
0
        free(base64);
583
0
      }
584
0
      free(creds);
585
0
    }
586
0
  }
587
588
0
  if (!Stream_EnsureRemainingCapacity(s, 4))
589
0
    goto fail;
590
591
0
  Stream_Write(s, CRLF CRLF, 4);
592
0
  ERR_clear_error();
593
0
  status = BIO_write(bufferedBio, Stream_Buffer(s), Stream_GetPosition(s));
594
595
0
  if ((status < 0) || ((size_t)status != Stream_GetPosition(s)))
596
0
  {
597
0
    WLog_ERR(TAG, "HTTP proxy: failed to write CONNECT request");
598
0
    goto fail;
599
0
  }
600
601
  /* Read result until CR-LF-CR-LF.
602
   * Keep recv_buf a null-terminated string. */
603
0
  while (strstr(recv_buf, CRLF CRLF) == NULL)
604
0
  {
605
0
    if (resultsize >= sizeof(recv_buf) - 1)
606
0
    {
607
0
      WLog_ERR(TAG, "HTTP Reply headers too long: %s", get_response_header(recv_buf));
608
0
      goto fail;
609
0
    }
610
611
0
    ERR_clear_error();
612
0
    status =
613
0
        BIO_read(bufferedBio, (BYTE*)recv_buf + resultsize, sizeof(recv_buf) - resultsize - 1);
614
615
0
    if (status < 0)
616
0
    {
617
      /* Error? */
618
0
      if (BIO_should_retry(bufferedBio))
619
0
      {
620
0
        USleep(100);
621
0
        continue;
622
0
      }
623
624
0
      WLog_ERR(TAG, "Failed reading reply from HTTP proxy (Status %d)", status);
625
0
      goto fail;
626
0
    }
627
0
    else if (status == 0)
628
0
    {
629
      /* Error? */
630
0
      WLog_ERR(TAG, "Failed reading reply from HTTP proxy (BIO_read returned zero)");
631
0
      goto fail;
632
0
    }
633
634
0
    resultsize += status;
635
0
  }
636
637
  /* Extract HTTP status line */
638
0
  eol = strchr(recv_buf, '\r');
639
640
0
  if (!eol)
641
0
  {
642
    /* should never happen */
643
0
    goto fail;
644
0
  }
645
646
0
  *eol = '\0';
647
0
  WLog_INFO(TAG, "HTTP Proxy: %s", recv_buf);
648
649
0
  if (strnlen(recv_buf, sizeof(recv_buf)) < 12)
650
0
    goto fail;
651
652
0
  recv_buf[7] = 'X';
653
654
0
  if (strncmp(recv_buf, "HTTP/1.X 200", 12))
655
0
    goto fail;
656
657
0
  rc = TRUE;
658
0
fail:
659
0
  Stream_Free(s, TRUE);
660
0
  return rc;
661
0
}
662
663
static int recv_socks_reply(BIO* bufferedBio, BYTE* buf, int len, char* reason, BYTE checkVer)
664
0
{
665
0
  int status = 0;
666
667
0
  for (;;)
668
0
  {
669
0
    ERR_clear_error();
670
0
    status = BIO_read(bufferedBio, buf, len);
671
672
0
    if (status > 0)
673
0
    {
674
0
      break;
675
0
    }
676
0
    else if (status < 0)
677
0
    {
678
      /* Error? */
679
0
      if (BIO_should_retry(bufferedBio))
680
0
      {
681
0
        USleep(100);
682
0
        continue;
683
0
      }
684
685
0
      WLog_ERR(TAG, "Failed reading %s reply from SOCKS proxy (Status %d)", reason, status);
686
0
      return -1;
687
0
    }
688
0
    else // if (status == 0)
689
0
    {
690
      /* Error? */
691
0
      WLog_ERR(TAG, "Failed reading %s reply from SOCKS proxy (BIO_read returned zero)",
692
0
               reason);
693
0
      return -1;
694
0
    }
695
0
  }
696
697
0
  if (status < 2)
698
0
  {
699
0
    WLog_ERR(TAG, "SOCKS Proxy reply packet too short (%s)", reason);
700
0
    return -1;
701
0
  }
702
703
0
  if (buf[0] != checkVer)
704
0
  {
705
0
    WLog_ERR(TAG, "SOCKS Proxy version is not 5 (%s)", reason);
706
0
    return -1;
707
0
  }
708
709
0
  return status;
710
0
}
711
712
static BOOL socks_proxy_connect(BIO* bufferedBio, const char* proxyUsername,
713
                                const char* proxyPassword, const char* hostname, UINT16 port)
714
0
{
715
0
  int status = 0;
716
0
  int nauthMethods = 1;
717
0
  int writeLen = 3;
718
0
  BYTE buf[3 + 255 + 255]; /* biggest packet is user/pass auth */
719
0
  size_t hostnlen = strnlen(hostname, 255);
720
721
0
  if (proxyUsername && proxyPassword)
722
0
  {
723
0
    nauthMethods++;
724
0
    writeLen++;
725
0
  }
726
727
  /* select auth. method */
728
0
  buf[0] = 5;            /* SOCKS version */
729
0
  buf[1] = nauthMethods; /* #of methods offered */
730
0
  buf[2] = AUTH_M_NO_AUTH;
731
732
0
  if (nauthMethods > 1)
733
0
    buf[3] = AUTH_M_USR_PASS;
734
735
0
  ERR_clear_error();
736
0
  status = BIO_write(bufferedBio, buf, writeLen);
737
738
0
  if (status != writeLen)
739
0
  {
740
0
    WLog_ERR(TAG, "SOCKS proxy: failed to write AUTH METHOD request");
741
0
    return FALSE;
742
0
  }
743
744
0
  status = recv_socks_reply(bufferedBio, buf, 2, "AUTH REQ", 5);
745
746
0
  if (status <= 0)
747
0
    return FALSE;
748
749
0
  switch (buf[1])
750
0
  {
751
0
    case AUTH_M_NO_AUTH:
752
0
      WLog_DBG(TAG, "SOCKS Proxy: (NO AUTH) method was selected");
753
0
      break;
754
755
0
    case AUTH_M_USR_PASS:
756
0
      if (!proxyUsername || !proxyPassword)
757
0
        return FALSE;
758
0
      else
759
0
      {
760
0
        int usernameLen = strnlen(proxyUsername, 255);
761
0
        int userpassLen = strnlen(proxyPassword, 255);
762
0
        BYTE* ptr = NULL;
763
764
0
        if (nauthMethods < 2)
765
0
        {
766
0
          WLog_ERR(TAG, "SOCKS Proxy: USER/PASS method was not proposed to server");
767
0
          return FALSE;
768
0
        }
769
770
        /* user/password v1 method */
771
0
        ptr = buf + 2;
772
0
        buf[0] = 1;
773
0
        buf[1] = usernameLen;
774
0
        memcpy(ptr, proxyUsername, usernameLen);
775
0
        ptr += usernameLen;
776
0
        *ptr = userpassLen;
777
0
        ptr++;
778
0
        memcpy(ptr, proxyPassword, userpassLen);
779
0
        ERR_clear_error();
780
0
        status = BIO_write(bufferedBio, buf, 3 + usernameLen + userpassLen);
781
782
0
        if (status != 3 + usernameLen + userpassLen)
783
0
        {
784
0
          WLog_ERR(TAG, "SOCKS Proxy: error writing user/password request");
785
0
          return FALSE;
786
0
        }
787
788
0
        status = recv_socks_reply(bufferedBio, buf, 2, "AUTH REQ", 1);
789
790
0
        if (status < 2)
791
0
          return FALSE;
792
793
0
        if (buf[1] != 0x00)
794
0
        {
795
0
          WLog_ERR(TAG, "SOCKS Proxy: invalid user/password");
796
0
          return FALSE;
797
0
        }
798
0
      }
799
800
0
      break;
801
802
0
    default:
803
0
      WLog_ERR(TAG, "SOCKS Proxy: unknown method 0x%x was selected by proxy", buf[1]);
804
0
      return FALSE;
805
0
  }
806
807
  /* CONN request */
808
0
  buf[0] = 5;                 /* SOCKS version */
809
0
  buf[1] = SOCKS_CMD_CONNECT; /* command */
810
0
  buf[2] = 0;                 /* 3rd octet is reserved x00 */
811
0
  buf[3] = SOCKS_ADDR_FQDN;   /* addr.type */
812
0
  buf[4] = hostnlen;          /* DST.ADDR */
813
0
  memcpy(buf + 5, hostname, hostnlen);
814
  /* follows DST.PORT in netw. format */
815
0
  buf[hostnlen + 5] = (port >> 8) & 0xff;
816
0
  buf[hostnlen + 6] = port & 0xff;
817
0
  ERR_clear_error();
818
0
  status = BIO_write(bufferedBio, buf, hostnlen + 7U);
819
820
0
  if ((status < 0) || ((size_t)status != (hostnlen + 7U)))
821
0
  {
822
0
    WLog_ERR(TAG, "SOCKS proxy: failed to write CONN REQ");
823
0
    return FALSE;
824
0
  }
825
826
0
  status = recv_socks_reply(bufferedBio, buf, sizeof(buf), "CONN REQ", 5);
827
828
0
  if (status < 4)
829
0
    return FALSE;
830
831
0
  if (buf[1] == 0)
832
0
  {
833
0
    WLog_INFO(TAG, "Successfully connected to %s:%" PRIu16, hostname, port);
834
0
    return TRUE;
835
0
  }
836
837
0
  if (buf[1] > 0 && buf[1] < 9)
838
0
    WLog_INFO(TAG, "SOCKS Proxy replied: %s", rplstat[buf[1]]);
839
0
  else
840
0
    WLog_INFO(TAG, "SOCKS Proxy replied: %" PRIu8 " status not listed in rfc1928", buf[1]);
841
842
0
  return FALSE;
843
0
}