Coverage Report

Created: 2026-03-12 06:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Utilities/cmcurl/lib/socks.c
Line
Count
Source
1
/***************************************************************************
2
 *                                  _   _ ____  _
3
 *  Project                     ___| | | |  _ \| |
4
 *                             / __| | | | |_) | |
5
 *                            | (__| |_| |  _ <| |___
6
 *                             \___|\___/|_| \_\_____|
7
 *
8
 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9
 *
10
 * This software is licensed as described in the file COPYING, which
11
 * you should have received as part of this distribution. The terms
12
 * are also available at https://curl.se/docs/copyright.html.
13
 *
14
 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15
 * copies of the Software, and permit persons to whom the Software is
16
 * furnished to do so, under the terms of the COPYING file.
17
 *
18
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19
 * KIND, either express or implied.
20
 *
21
 * SPDX-License-Identifier: curl
22
 *
23
 ***************************************************************************/
24
#include "curl_setup.h"
25
26
#ifndef CURL_DISABLE_PROXY
27
28
#ifdef HAVE_NETINET_IN_H
29
#include <netinet/in.h>
30
#endif
31
#ifdef HAVE_ARPA_INET_H
32
#include <arpa/inet.h>
33
#endif
34
35
#include "urldata.h"
36
#include "bufq.h"
37
#include "curl_trc.h"
38
#include "select.h"
39
#include "cfilters.h"
40
#include "connect.h"
41
#include "socks.h"
42
#include "curlx/inet_pton.h"
43
#include "url.h"
44
45
#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
46
#define DEBUG_AND_VERBOSE
47
#endif
48
49
/* for the (SOCKS) connect state machine */
50
enum socks_state_t {
51
  SOCKS_ST_INIT,
52
  /* SOCKS Version 4 states */
53
  SOCKS4_ST_START,
54
  SOCKS4_ST_RESOLVING,
55
  SOCKS4_ST_SEND,
56
  SOCKS4_ST_RECV,
57
  /* SOCKS Version 5 states */
58
  SOCKS5_ST_START,
59
  SOCKS5_ST_REQ0_SEND,
60
  SOCKS5_ST_RESP0_RECV, /* set up read */
61
  SOCKS5_ST_GSSAPI_INIT,
62
  SOCKS5_ST_AUTH_INIT, /* setup outgoing auth buffer */
63
  SOCKS5_ST_AUTH_SEND, /* send auth */
64
  SOCKS5_ST_AUTH_RECV, /* read auth response */
65
  SOCKS5_ST_REQ1_INIT,  /* init SOCKS "request" */
66
  SOCKS5_ST_RESOLVING,
67
  SOCKS5_ST_REQ1_SEND,
68
  SOCKS5_ST_RESP1_RECV,
69
  /* Terminal states, all SOCKS versions */
70
  SOCKS_ST_SUCCESS,
71
  SOCKS_ST_FAILED
72
};
73
74
#ifdef DEBUG_AND_VERBOSE
75
static const char * const cf_socks_statename[] = {
76
  "SOCKS_INIT",
77
  "SOCKS4_START",
78
  "SOCKS4_RESOLVING",
79
  "SOCKS4_SEND",
80
  "SOCKS4_RECV",
81
  "SOCKS5_START",
82
  "SOCKS5_REQ0_SEND",
83
  "SOCKS5_RESP0_RECV",
84
  "SOCKS5_GSSAPI_INIT",
85
  "SOCKS5_AUTH_INIT",
86
  "SOCKS5_AUTH_SEND",
87
  "SOCKS5_AUTH_RECV",
88
  "SOCKS5_REQ1_INIT",
89
  "SOCKS5_RESOLVING",
90
  "SOCKS5_REQ1_SEND",
91
  "SOCKS5_RESP1_RECV",
92
  "SOCKS_SUCCESS",
93
  "SOCKS_FAILED"
94
};
95
#endif
96
97
0
#define SOCKS_CHUNK_SIZE    1024
98
0
#define SOCKS_CHUNKS        1
99
100
101
struct socks_state {
102
  enum socks_state_t state;
103
  struct bufq iobuf;
104
  const char *hostname;
105
  int remote_port;
106
  const char *proxy_user;
107
  const char *proxy_password;
108
  CURLproxycode presult;
109
  unsigned char version;
110
  BIT(resolve_local);
111
  BIT(start_resolving);
112
  BIT(socks4a);
113
};
114
115
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
116
/*
117
 * Helper read-from-socket functions. Does the same as Curl_read() but it
118
 * blocks until all bytes amount of buffersize will be read. No more, no less.
119
 *
120
 * This is STUPID BLOCKING behavior. Only used by the SOCKS GSSAPI functions.
121
 */
122
CURLcode Curl_blockread_all(struct Curl_cfilter *cf,
123
                            struct Curl_easy *data,
124
                            char *buf,             /* store read data here */
125
                            size_t blen,           /* space in buf */
126
                            size_t *pnread)        /* amount bytes read */
127
{
128
  size_t nread = 0;
129
  CURLcode result;
130
131
  *pnread = 0;
132
  for(;;) {
133
    timediff_t timeout_ms = Curl_timeleft_ms(data, TRUE);
134
    if(timeout_ms < 0) {
135
      /* we already got the timeout */
136
      return CURLE_OPERATION_TIMEDOUT;
137
    }
138
    if(!timeout_ms)
139
      timeout_ms = TIMEDIFF_T_MAX;
140
    if(SOCKET_READABLE(cf->conn->sock[cf->sockindex], timeout_ms) <= 0)
141
      return CURLE_OPERATION_TIMEDOUT;
142
    result = Curl_conn_cf_recv(cf->next, data, buf, blen, &nread);
143
    if(CURLE_AGAIN == result)
144
      continue;
145
    else if(result)
146
      return result;
147
148
    if(blen == nread) {
149
      *pnread += nread;
150
      return CURLE_OK;
151
    }
152
    if(!nread) /* EOF */
153
      return CURLE_RECV_ERROR;
154
155
    buf += nread;
156
    blen -= nread;
157
    *pnread += nread;
158
  }
159
}
160
#endif
161
162
#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
163
#define DEBUG_AND_VERBOSE
164
#define sxstate(x, c, d, y) socksstate(x, c, d, y, __LINE__)
165
#else
166
0
#define sxstate(x, c, d, y) socksstate(x, c, d, y)
167
#endif
168
169
/* always use this function to change state, to make debugging easier */
170
static void socksstate(struct socks_state *sx,
171
                       struct Curl_cfilter *cf,
172
                       struct Curl_easy *data,
173
                       enum socks_state_t state
174
#ifdef DEBUG_AND_VERBOSE
175
                       , int lineno
176
#endif
177
)
178
0
{
179
0
  enum socks_state_t oldstate = sx->state;
180
0
  (void)cf;
181
0
  (void)data;
182
0
  if(oldstate == state)
183
    /* do not bother when the new state is the same as the old state */
184
0
    return;
185
186
0
  sx->state = state;
187
188
#ifdef DEBUG_AND_VERBOSE
189
  CURL_TRC_CF(data, cf, "[%s] -> [%s] (line %d)",
190
              cf_socks_statename[oldstate],
191
              cf_socks_statename[sx->state], lineno);
192
#endif
193
0
}
194
195
static CURLproxycode socks_failed(struct socks_state *sx,
196
                                  struct Curl_cfilter *cf,
197
                                  struct Curl_easy *data,
198
                                  CURLproxycode presult)
199
0
{
200
0
  sxstate(sx, cf, data, SOCKS_ST_FAILED);
201
0
  sx->presult = presult;
202
0
  return presult;
203
0
}
204
205
static CURLproxycode socks_flush(struct socks_state *sx,
206
                                 struct Curl_cfilter *cf,
207
                                 struct Curl_easy *data,
208
                                 bool *done)
209
0
{
210
0
  CURLcode result;
211
0
  size_t nwritten;
212
213
0
  *done = FALSE;
214
0
  while(!Curl_bufq_is_empty(&sx->iobuf)) {
215
0
    result = Curl_cf_send_bufq(cf->next, data, &sx->iobuf, NULL, 0,
216
0
                               &nwritten);
217
0
    if(result == CURLE_AGAIN)
218
0
      return CURLPX_OK;
219
0
    else if(result) {
220
0
      failf(data, "Failed to send SOCKS request: %s",
221
0
            curl_easy_strerror(result));
222
0
      return socks_failed(sx, cf, data, CURLPX_SEND_CONNECT);
223
0
    }
224
0
  }
225
0
  *done = TRUE;
226
0
  return CURLPX_OK;
227
0
}
228
229
static CURLproxycode socks_recv(struct socks_state *sx,
230
                                struct Curl_cfilter *cf,
231
                                struct Curl_easy *data,
232
                                size_t min_bytes,
233
                                bool *done)
234
0
{
235
0
  CURLcode result;
236
0
  size_t nread;
237
238
0
  *done = FALSE;
239
0
  while(Curl_bufq_len(&sx->iobuf) < min_bytes) {
240
0
    result = Curl_cf_recv_bufq(cf->next, data, &sx->iobuf,
241
0
                               min_bytes - Curl_bufq_len(&sx->iobuf),
242
0
                               &nread);
243
0
    if(result == CURLE_AGAIN)
244
0
      return CURLPX_OK;
245
0
    else if(result) {
246
0
      failf(data, "Failed to receive SOCKS response: %s",
247
0
            curl_easy_strerror(result));
248
0
      return CURLPX_RECV_CONNECT;
249
0
    }
250
0
    else if(!nread) { /* EOF */
251
0
      if(Curl_bufq_len(&sx->iobuf) < min_bytes) {
252
0
        failf(data, "Failed to receive SOCKS response, "
253
0
              "proxy closed connection");
254
0
        return CURLPX_RECV_CONNECT;
255
0
      }
256
0
      break;
257
0
    }
258
0
  }
259
0
  *done = TRUE;
260
0
  return CURLPX_OK;
261
0
}
262
263
static CURLproxycode socks4_req_add_hd(struct socks_state *sx,
264
                                       struct Curl_easy *data)
265
0
{
266
0
  unsigned char buf[4];
267
0
  size_t nwritten;
268
0
  CURLcode result;
269
270
0
  (void)data;
271
0
  buf[0] = 4; /* version (SOCKS4) */
272
0
  buf[1] = 1; /* connect */
273
0
  buf[2] = (unsigned char)((sx->remote_port >> 8) & 0xffu); /* MSB */
274
0
  buf[3] = (unsigned char)(sx->remote_port & 0xffu);        /* LSB */
275
276
0
  result = Curl_bufq_write(&sx->iobuf, buf, 4, &nwritten);
277
0
  if(result || (nwritten != 4))
278
0
    return CURLPX_SEND_REQUEST;
279
0
  return CURLPX_OK;
280
0
}
281
282
static CURLproxycode socks4_req_add_user(struct socks_state *sx,
283
                                         struct Curl_easy *data)
284
0
{
285
0
  CURLcode result;
286
0
  size_t nwritten;
287
288
0
  if(sx->proxy_user) {
289
0
    size_t plen = strlen(sx->proxy_user);
290
0
    if(plen > 255) {
291
      /* there is no real size limit to this field in the protocol, but
292
         SOCKS5 limits the proxy user field to 255 bytes and it seems likely
293
         that a longer field is either a mistake or malicious input */
294
0
      failf(data, "Too long SOCKS proxy username");
295
0
      return CURLPX_LONG_USER;
296
0
    }
297
    /* add proxy name WITH trailing zero */
298
0
    result = Curl_bufq_cwrite(&sx->iobuf, sx->proxy_user, plen + 1,
299
0
                              &nwritten);
300
0
    if(result || (nwritten != (plen + 1)))
301
0
      return CURLPX_SEND_REQUEST;
302
0
  }
303
0
  else {
304
    /* empty username */
305
0
    unsigned char b = 0;
306
0
    result = Curl_bufq_write(&sx->iobuf, &b, 1, &nwritten);
307
0
    if(result || (nwritten != 1))
308
0
      return CURLPX_SEND_REQUEST;
309
0
  }
310
0
  return CURLPX_OK;
311
0
}
312
313
static CURLproxycode socks4_resolving(struct socks_state *sx,
314
                                      struct Curl_cfilter *cf,
315
                                      struct Curl_easy *data,
316
                                      bool *done)
317
0
{
318
0
  struct Curl_dns_entry *dns = NULL;
319
0
  struct Curl_addrinfo *hp = NULL;
320
0
  CURLcode result;
321
0
  size_t nwritten;
322
323
0
  *done = FALSE;
324
0
  if(sx->start_resolving) {
325
    /* need to resolve hostname to add destination address */
326
0
    sx->start_resolving = FALSE;
327
0
    DEBUGASSERT(sx->hostname && *sx->hostname);
328
329
0
    result = Curl_resolv(data, sx->hostname, sx->remote_port,
330
0
                         cf->conn->ip_version, TRUE, &dns);
331
0
    if(result == CURLE_AGAIN) {
332
0
      CURL_TRC_CF(data, cf, "SOCKS4 non-blocking resolve of %s", sx->hostname);
333
0
      return CURLPX_OK;
334
0
    }
335
0
    else if(result)
336
0
      return CURLPX_RESOLVE_HOST;
337
0
  }
338
0
  else {
339
    /* check if we have the name resolved by now */
340
0
    result = Curl_resolv_check(data, &dns);
341
0
    if(!result && !dns)
342
0
      return CURLPX_OK;
343
0
  }
344
345
0
  if(result || !dns) {
346
0
    failf(data, "Failed to resolve \"%s\" for SOCKS4 connect.", sx->hostname);
347
0
    return CURLPX_RESOLVE_HOST;
348
0
  }
349
350
  /*
351
   * We cannot use 'hostent' as a struct that Curl_resolv() returns. It
352
   * returns a Curl_addrinfo pointer that may not always look the same.
353
   */
354
  /* scan for the first IPv4 address */
355
0
  hp = dns->addr;
356
0
  while(hp && (hp->ai_family != AF_INET))
357
0
    hp = hp->ai_next;
358
359
0
  if(hp) {
360
0
    struct sockaddr_in *saddr_in;
361
0
    char ipbuf[64];
362
363
0
    Curl_printable_address(hp, ipbuf, sizeof(ipbuf));
364
0
    CURL_TRC_CF(data, cf, "SOCKS4 connect to IPv4 %s (locally resolved)",
365
0
                ipbuf);
366
367
0
    saddr_in = (struct sockaddr_in *)(void *)hp->ai_addr;
368
0
    result = Curl_bufq_write(&sx->iobuf,
369
0
                             (unsigned char *)&saddr_in->sin_addr.s_addr, 4,
370
0
                             &nwritten);
371
372
0
    Curl_resolv_unlink(data, &dns); /* not used anymore from now on */
373
0
    if(result || (nwritten != 4))
374
0
      return CURLPX_SEND_REQUEST;
375
0
  }
376
0
  else {
377
0
    failf(data, "SOCKS4 connection to %s not supported", sx->hostname);
378
0
    return CURLPX_RESOLVE_HOST;
379
0
  }
380
381
0
  *done = TRUE;
382
0
  return CURLPX_OK;
383
0
}
384
385
static CURLproxycode socks4_check_resp(struct socks_state *sx,
386
                                       struct Curl_cfilter *cf,
387
                                       struct Curl_easy *data)
388
0
{
389
0
  const unsigned char *resp;
390
0
  size_t rlen;
391
392
0
  if(!Curl_bufq_peek(&sx->iobuf, &resp, &rlen) || rlen < 8) {
393
0
    failf(data, "SOCKS4 reply is incomplete.");
394
0
    return CURLPX_RECV_CONNECT;
395
0
  }
396
397
0
  DEBUGASSERT(rlen == 8);
398
  /*
399
   * Response format
400
   *
401
   *     +----+----+----+----+----+----+----+----+
402
   *     | VN | CD | DSTPORT |      DSTIP        |
403
   *     +----+----+----+----+----+----+----+----+
404
   * # of bytes:  1    1      2              4
405
   *
406
   * VN is the version of the reply code and should be 0. CD is the result
407
   * code with one of the following values:
408
   *
409
   * 90: request granted
410
   * 91: request rejected or failed
411
   * 92: request rejected because SOCKS server cannot connect to
412
   *     identd on the client
413
   * 93: request rejected because the client program and identd
414
   *     report different user-ids
415
   */
416
417
  /* wrong version ? */
418
0
  if(resp[0]) {
419
0
    failf(data, "SOCKS4 reply has wrong version, version should be 0.");
420
0
    return CURLPX_BAD_VERSION;
421
0
  }
422
423
  /* Result */
424
0
  switch(resp[1]) {
425
0
  case 90:
426
0
    CURL_TRC_CF(data, cf, "SOCKS4%s request granted.", sx->socks4a ? "a" : "");
427
0
    Curl_bufq_skip(&sx->iobuf, 8);
428
0
    return CURLPX_OK;
429
0
  case 91:
430
0
    failf(data,
431
0
          "[SOCKS] cannot complete SOCKS4 connection to %u.%u.%u.%u:%u. (%u)"
432
0
          ", request rejected or failed.",
433
0
          resp[4], resp[5], resp[6], resp[7],
434
0
          ((resp[2] << 8) | resp[3]), resp[1]);
435
0
    return CURLPX_REQUEST_FAILED;
436
0
  case 92:
437
0
    failf(data,
438
0
          "[SOCKS] cannot complete SOCKS4 connection to %u.%u.%u.%u:%u. (%u)"
439
0
          ", request rejected because SOCKS server cannot connect to "
440
0
          "identd on the client.",
441
0
          resp[4], resp[5], resp[6], resp[7],
442
0
          ((resp[2] << 8) | resp[3]), resp[1]);
443
0
    return CURLPX_IDENTD;
444
0
  case 93:
445
0
    failf(data,
446
0
          "[SOCKS] cannot complete SOCKS4 connection to %u.%u.%u.%u:%u. (%u)"
447
0
          ", request rejected because the client program and identd "
448
0
          "report different user-ids.",
449
0
          resp[4], resp[5], resp[6], resp[7],
450
0
          ((resp[2] << 8) | resp[3]), resp[1]);
451
0
    return CURLPX_IDENTD_DIFFER;
452
0
  default:
453
0
    failf(data,
454
0
          "[SOCKS] cannot complete SOCKS4 connection to %u.%u.%u.%u:%u. (%u)"
455
0
          ", Unknown.",
456
0
          resp[4], resp[5], resp[6], resp[7],
457
0
          ((resp[2] << 8) | resp[3]), resp[1]);
458
0
    return CURLPX_UNKNOWN_FAIL;
459
0
  }
460
0
}
461
462
/*
463
 * This function logs in to a SOCKS4 proxy and sends the specifics to the final
464
 * destination server.
465
 *
466
 * Reference :
467
 *   https://www.openssh.com/txt/socks4.protocol
468
 *
469
 * Note :
470
 *   Set protocol4a=true for  "SOCKS 4A (Simple Extension to SOCKS 4 Protocol)"
471
 *   Nonsupport "Identification Protocol (RFC1413)"
472
 */
473
static CURLproxycode socks4_connect(struct Curl_cfilter *cf,
474
                                    struct socks_state *sx,
475
                                    struct Curl_easy *data)
476
0
{
477
0
  size_t nwritten;
478
0
  CURLproxycode presult;
479
0
  CURLcode result;
480
0
  bool done;
481
482
0
process_state:
483
0
  switch(sx->state) {
484
0
  case SOCKS_ST_INIT:
485
0
    sx->version = 4;
486
0
    sxstate(sx, cf, data, SOCKS4_ST_START);
487
0
    FALLTHROUGH();
488
489
0
  case SOCKS4_ST_START:
490
0
    Curl_bufq_reset(&sx->iobuf);
491
0
    sx->start_resolving = FALSE;
492
0
    sx->socks4a = (cf->conn->socks_proxy.proxytype == CURLPROXY_SOCKS4A);
493
0
    sx->resolve_local = !sx->socks4a;
494
0
    sx->presult = CURLPX_OK;
495
496
    /* SOCKS4 can only do IPv4, insist! */
497
0
    cf->conn->ip_version = CURL_IPRESOLVE_V4;
498
0
    CURL_TRC_CF(data, cf, "SOCKS4%s communication to%s %s:%d",
499
0
                sx->socks4a ? "a" : "",
500
0
                cf->conn->bits.httpproxy ? " HTTP proxy" : "",
501
0
                sx->hostname, sx->remote_port);
502
503
    /*
504
     * Compose socks4 request
505
     *
506
     * Request format
507
     *
508
     *     +----+----+----+----+----+----+----+----+----+----+....+----+
509
     *     | VN | CD | DSTPORT |      DSTIP        | USERID       |NULL|
510
     *     +----+----+----+----+----+----+----+----+----+----+....+----+
511
     * # of bytes:  1    1      2              4           variable       1
512
     */
513
0
    presult = socks4_req_add_hd(sx, data);
514
0
    if(presult)
515
0
      return socks_failed(sx, cf, data, presult);
516
517
    /* DNS resolve only for SOCKS4, not SOCKS4a */
518
0
    if(!sx->resolve_local) {
519
      /* socks4a, not resolving locally, sends the hostname.
520
       * add an invalid address + user + hostname */
521
0
      unsigned char buf[4] = { 0, 0, 0, 1 };
522
0
      size_t hlen = strlen(sx->hostname) + 1; /* including NUL */
523
524
0
      if(hlen > 255) {
525
0
        failf(data, "SOCKS4: too long hostname");
526
0
        return socks_failed(sx, cf, data, CURLPX_LONG_HOSTNAME);
527
0
      }
528
0
      result = Curl_bufq_write(&sx->iobuf, buf, 4, &nwritten);
529
0
      if(result || (nwritten != 4))
530
0
        return socks_failed(sx, cf, data, CURLPX_SEND_REQUEST);
531
0
      presult = socks4_req_add_user(sx, data);
532
0
      if(presult)
533
0
        return socks_failed(sx, cf, data, presult);
534
0
      result = Curl_bufq_cwrite(&sx->iobuf, sx->hostname, hlen, &nwritten);
535
0
      if(result || (nwritten != hlen))
536
0
        return socks_failed(sx, cf, data, CURLPX_SEND_REQUEST);
537
      /* request complete */
538
0
      sxstate(sx, cf, data, SOCKS4_ST_SEND);
539
0
      goto process_state;
540
0
    }
541
0
    sx->start_resolving = TRUE;
542
0
    sxstate(sx, cf, data, SOCKS4_ST_RESOLVING);
543
0
    FALLTHROUGH();
544
545
0
  case SOCKS4_ST_RESOLVING:
546
0
    presult = socks4_resolving(sx, cf, data, &done);
547
0
    if(presult)
548
0
      return socks_failed(sx, cf, data, presult);
549
0
    if(!done)
550
0
      return CURLPX_OK;
551
    /* append user */
552
0
    presult = socks4_req_add_user(sx, data);
553
0
    if(presult)
554
0
      return socks_failed(sx, cf, data, presult);
555
0
    sxstate(sx, cf, data, SOCKS4_ST_SEND);
556
0
    FALLTHROUGH();
557
558
0
  case SOCKS4_ST_SEND:
559
0
    presult = socks_flush(sx, cf, data, &done);
560
0
    if(presult)
561
0
      return socks_failed(sx, cf, data, presult);
562
0
    else if(!done)
563
0
      return CURLPX_OK;
564
0
    sxstate(sx, cf, data, SOCKS4_ST_RECV);
565
0
    FALLTHROUGH();
566
567
0
  case SOCKS4_ST_RECV:
568
    /* Receive 8 byte response */
569
0
    presult = socks_recv(sx, cf, data, 8, &done);
570
0
    if(presult)
571
0
      return socks_failed(sx, cf, data, presult);
572
0
    else if(!done)
573
0
      return CURLPX_OK;
574
0
    presult = socks4_check_resp(sx, cf, data);
575
0
    if(presult)
576
0
      return socks_failed(sx, cf, data, presult);
577
0
    sxstate(sx, cf, data, SOCKS_ST_SUCCESS);
578
0
    FALLTHROUGH();
579
580
0
  case SOCKS_ST_SUCCESS:
581
0
    return CURLPX_OK;
582
583
0
  case SOCKS_ST_FAILED:
584
0
    DEBUGASSERT(sx->presult);
585
0
    return sx->presult;
586
587
0
  default:
588
0
    DEBUGASSERT(0);
589
0
    return socks_failed(sx, cf, data, CURLPX_SEND_REQUEST);
590
0
  }
591
0
}
592
593
static CURLproxycode socks5_req0_init(struct Curl_cfilter *cf,
594
                                      struct socks_state *sx,
595
                                      struct Curl_easy *data)
596
0
{
597
0
  const unsigned char auth = data->set.socks5auth;
598
0
  unsigned char req[5]; /* version + len + 3 possible auth methods */
599
0
  unsigned char nauths;
600
0
  size_t req_len, nwritten;
601
0
  CURLcode result;
602
603
0
  (void)cf;
604
  /* RFC1928 chapter 5 specifies max 255 chars for domain name in packet */
605
0
  if(!sx->resolve_local && strlen(sx->hostname) > 255) {
606
0
    failf(data, "SOCKS5: the destination hostname is too long to be "
607
0
          "resolved remotely by the proxy.");
608
0
    return CURLPX_LONG_HOSTNAME;
609
0
  }
610
611
0
  if(auth & ~(CURLAUTH_BASIC | CURLAUTH_GSSAPI))
612
0
    infof(data, "warning: unsupported value passed to "
613
0
          "CURLOPT_SOCKS5_AUTH: %u", auth);
614
0
  if(!(auth & CURLAUTH_BASIC))
615
    /* disable username/password auth */
616
0
    sx->proxy_user = NULL;
617
618
0
  req[0] = 5;   /* version */
619
0
  nauths = 1;
620
0
  req[1 + nauths] = 0;   /* 1. no authentication */
621
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
622
  if(auth & CURLAUTH_GSSAPI) {
623
    ++nauths;
624
    req[1 + nauths] = 1; /* GSS-API */
625
  }
626
#endif
627
0
  if(sx->proxy_user) {
628
0
    ++nauths;
629
0
    req[1 + nauths] = 2; /* username/password */
630
0
  }
631
0
  req[1] = nauths;
632
0
  req_len = 2 + nauths;
633
634
0
  result = Curl_bufq_write(&sx->iobuf, req, req_len, &nwritten);
635
0
  if(result || (nwritten != req_len))
636
0
    return CURLPX_SEND_REQUEST;
637
0
  return CURLPX_OK;
638
0
}
639
640
static CURLproxycode socks5_check_resp0(struct socks_state *sx,
641
                                        struct Curl_cfilter *cf,
642
                                        struct Curl_easy *data)
643
0
{
644
0
  const unsigned char *resp;
645
0
  unsigned char auth_mode;
646
0
  size_t rlen;
647
648
0
  if(!Curl_bufq_peek(&sx->iobuf, &resp, &rlen) || rlen < 2) {
649
0
    failf(data, "SOCKS5 initial reply is incomplete.");
650
0
    return CURLPX_RECV_CONNECT;
651
0
  }
652
653
0
  if(resp[0] != 5) {
654
0
    failf(data, "Received invalid version in initial SOCKS5 response.");
655
0
    return CURLPX_BAD_VERSION;
656
0
  }
657
658
0
  auth_mode = resp[1];
659
0
  Curl_bufq_skip(&sx->iobuf, 2);
660
661
0
  switch(auth_mode) {
662
0
  case 0:
663
    /* DONE! No authentication needed. Send request. */
664
0
    sxstate(sx, cf, data, SOCKS5_ST_REQ1_INIT);
665
0
    return CURLPX_OK;
666
0
  case 1:
667
0
    if(data->set.socks5auth & CURLAUTH_GSSAPI) {
668
0
      sxstate(sx, cf, data, SOCKS5_ST_GSSAPI_INIT);
669
0
      return CURLPX_OK;
670
0
    }
671
0
    failf(data,
672
0
          "SOCKS5 GSSAPI per-message authentication is not enabled.");
673
0
    return CURLPX_GSSAPI_PERMSG;
674
0
  case 2:
675
    /* regular name + password authentication */
676
0
    if(data->set.socks5auth & CURLAUTH_BASIC) {
677
0
      sxstate(sx, cf, data, SOCKS5_ST_AUTH_INIT);
678
0
      return CURLPX_OK;
679
0
    }
680
0
    failf(data, "BASIC authentication proposed but not enabled.");
681
0
    return CURLPX_NO_AUTH;
682
0
  case 255:
683
0
    failf(data, "No authentication method was acceptable.");
684
0
    return CURLPX_NO_AUTH;
685
0
  default:
686
0
    failf(data, "Unknown SOCKS5 mode attempted to be used by server.");
687
0
    return CURLPX_UNKNOWN_MODE;
688
0
  }
689
0
}
690
691
static CURLproxycode socks5_auth_init(struct Curl_cfilter *cf,
692
                                      struct socks_state *sx,
693
                                      struct Curl_easy *data)
694
0
{
695
  /* Needs username and password */
696
0
  size_t ulen = 0, plen = 0, nwritten;
697
0
  unsigned char buf[2];
698
0
  CURLcode result;
699
700
0
  if(sx->proxy_user && sx->proxy_password) {
701
0
    ulen = strlen(sx->proxy_user);
702
0
    plen = strlen(sx->proxy_password);
703
    /* the lengths must fit in a single byte */
704
0
    if(ulen > 255) {
705
0
      failf(data, "Excessive username length for proxy auth");
706
0
      return CURLPX_LONG_USER;
707
0
    }
708
0
    if(plen > 255) {
709
0
      failf(data, "Excessive password length for proxy auth");
710
0
      return CURLPX_LONG_PASSWD;
711
0
    }
712
0
  }
713
714
  /*   username/password request looks like
715
   * +----+------+----------+------+----------+
716
   * |VER | ULEN |  UNAME   | PLEN |  PASSWD  |
717
   * +----+------+----------+------+----------+
718
   * | 1  |  1   | 1 to 255 |  1   | 1 to 255 |
719
   * +----+------+----------+------+----------+
720
   */
721
0
  buf[0] = 1;    /* username/pw subnegotiation version */
722
0
  buf[1] = (unsigned char)ulen;
723
0
  result = Curl_bufq_write(&sx->iobuf, buf, 2, &nwritten);
724
0
  if(result || (nwritten != 2))
725
0
    return CURLPX_SEND_REQUEST;
726
0
  if(ulen) {
727
0
    result = Curl_bufq_cwrite(&sx->iobuf, sx->proxy_user, ulen, &nwritten);
728
0
    if(result || (nwritten != ulen))
729
0
      return CURLPX_SEND_REQUEST;
730
0
  }
731
0
  buf[0] = (unsigned char)plen;
732
0
  result = Curl_bufq_write(&sx->iobuf, buf, 1, &nwritten);
733
0
  if(result || (nwritten != 1))
734
0
    return CURLPX_SEND_REQUEST;
735
0
  if(plen) {
736
0
    result = Curl_bufq_cwrite(&sx->iobuf, sx->proxy_password, plen, &nwritten);
737
0
    if(result || (nwritten != plen))
738
0
      return CURLPX_SEND_REQUEST;
739
0
  }
740
0
  sxstate(sx, cf, data, SOCKS5_ST_AUTH_SEND);
741
0
  return CURLPX_OK;
742
0
}
743
744
static CURLproxycode socks5_check_auth_resp(struct socks_state *sx,
745
                                            struct Curl_cfilter *cf,
746
                                            struct Curl_easy *data)
747
0
{
748
0
  const unsigned char *resp;
749
0
  unsigned char auth_status;
750
0
  size_t rlen;
751
752
0
  (void)cf;
753
0
  if(!Curl_bufq_peek(&sx->iobuf, &resp, &rlen) || rlen < 2) {
754
0
    failf(data, "SOCKS5 sub-negotiation response incomplete.");
755
0
    return CURLPX_RECV_CONNECT;
756
0
  }
757
758
  /* ignore the first (VER) byte */
759
0
  auth_status = resp[1];
760
0
  if(auth_status) {
761
0
    failf(data, "User was rejected by the SOCKS5 server (%d %d).",
762
0
          resp[0], resp[1]);
763
0
    return CURLPX_USER_REJECTED;
764
0
  }
765
0
  Curl_bufq_skip(&sx->iobuf, 2);
766
0
  return CURLPX_OK;
767
0
}
768
769
static CURLproxycode socks5_req1_init(struct socks_state *sx,
770
                                      struct Curl_cfilter *cf,
771
                                      struct Curl_easy *data)
772
0
{
773
0
  unsigned char req[5];
774
0
  unsigned char ipbuf[16];
775
0
  const unsigned char *destination;
776
0
  unsigned char desttype, destlen, hdlen;
777
0
  size_t nwritten;
778
0
  CURLcode result;
779
780
0
  req[0] = 5; /* version (SOCKS5) */
781
0
  req[1] = 1; /* connect */
782
0
  req[2] = 0; /* must be zero */
783
0
  if(sx->resolve_local) {
784
    /* rest of request is added after resolving */
785
0
    result = Curl_bufq_write(&sx->iobuf, req, 3, &nwritten);
786
0
    if(result || (nwritten != 3))
787
0
      return CURLPX_SEND_REQUEST;
788
0
    return CURLPX_OK;
789
0
  }
790
791
  /* remote resolving, send what type+addr/string to resolve */
792
0
#ifdef USE_IPV6
793
0
  if(cf->conn->bits.ipv6_ip) {
794
0
    desttype = 4;
795
0
    destination = ipbuf;
796
0
    destlen = 16;
797
0
    if(curlx_inet_pton(AF_INET6, sx->hostname, ipbuf) != 1)
798
0
      return CURLPX_BAD_ADDRESS_TYPE;
799
0
  }
800
0
  else
801
0
#endif
802
0
  if(curlx_inet_pton(AF_INET, sx->hostname, ipbuf) == 1) {
803
0
    desttype = 1;
804
0
    destination = ipbuf;
805
0
    destlen = 4;
806
0
  }
807
0
  else {
808
0
    const size_t hostname_len = strlen(sx->hostname);
809
0
    desttype = 3;
810
0
    destination = (const unsigned char *)sx->hostname;
811
0
    destlen = (unsigned char)hostname_len; /* one byte length */
812
0
  }
813
814
0
  req[3] = desttype;
815
0
  req[4] = destlen;
816
0
  hdlen = (desttype == 3) ? 5 : 4; /* no length byte for ip addresses */
817
0
  result = Curl_bufq_write(&sx->iobuf, req, hdlen, &nwritten);
818
0
  if(result || (nwritten != hdlen))
819
0
    return CURLPX_SEND_REQUEST;
820
0
  result = Curl_bufq_write(&sx->iobuf, destination, destlen, &nwritten);
821
0
  if(result || (nwritten != destlen))
822
0
    return CURLPX_SEND_REQUEST;
823
  /* PORT MSB+LSB */
824
0
  req[0] = (unsigned char)((sx->remote_port >> 8) & 0xff);
825
0
  req[1] = (unsigned char)(sx->remote_port & 0xff);
826
0
  result = Curl_bufq_write(&sx->iobuf, req, 2, &nwritten);
827
0
  if(result || (nwritten != 2))
828
0
    return CURLPX_SEND_REQUEST;
829
0
  CURL_TRC_CF(data, cf, "SOCKS5 connect to %s:%d (remotely resolved)",
830
0
              sx->hostname, sx->remote_port);
831
0
  return CURLPX_OK;
832
0
}
833
834
static CURLproxycode socks5_resolving(struct socks_state *sx,
835
                                      struct Curl_cfilter *cf,
836
                                      struct Curl_easy *data,
837
                                      bool *done)
838
0
{
839
0
  struct Curl_dns_entry *dns = NULL;
840
0
  struct Curl_addrinfo *hp = NULL;
841
0
  char dest[MAX_IPADR_LEN];  /* printable address */
842
0
  unsigned char *destination = NULL;
843
0
  unsigned char desttype = 1, destlen = 4;
844
0
  unsigned char req[2];
845
0
  CURLcode result;
846
0
  CURLproxycode presult = CURLPX_OK;
847
0
  size_t nwritten;
848
849
0
  *done = FALSE;
850
0
  if(sx->start_resolving) {
851
    /* need to resolve hostname to add destination address */
852
0
    sx->start_resolving = FALSE;
853
0
    DEBUGASSERT(sx->hostname && *sx->hostname);
854
855
0
    result = Curl_resolv(data, sx->hostname, sx->remote_port,
856
0
                         cf->conn->ip_version, TRUE, &dns);
857
0
    if(result == CURLE_AGAIN) {
858
0
      CURL_TRC_CF(data, cf, "SOCKS5 non-blocking resolve of %s", sx->hostname);
859
0
      return CURLPX_OK;
860
0
    }
861
0
    else if(result)
862
0
      return CURLPX_RESOLVE_HOST;
863
0
  }
864
0
  else {
865
    /* check if we have the name resolved by now */
866
0
    result = Curl_resolv_check(data, &dns);
867
0
    if(!result && !dns)
868
0
      return CURLPX_OK;
869
0
  }
870
871
0
  if(result || !dns) {
872
0
    failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.", sx->hostname);
873
0
    presult = CURLPX_RESOLVE_HOST;
874
0
    goto out;
875
0
  }
876
877
0
  if(dns)
878
0
    hp = dns->addr;
879
0
#ifdef USE_IPV6
880
0
  if(data->set.ipver != CURL_IPRESOLVE_WHATEVER) {
881
0
    int wanted_family = data->set.ipver == CURL_IPRESOLVE_V4 ?
882
0
      AF_INET : AF_INET6;
883
    /* scan for the first proper address */
884
0
    while(hp && (hp->ai_family != wanted_family))
885
0
      hp = hp->ai_next;
886
0
  }
887
0
#endif
888
0
  if(!hp) {
889
0
    failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.", sx->hostname);
890
0
    presult = CURLPX_RESOLVE_HOST;
891
0
    goto out;
892
0
  }
893
894
0
  Curl_printable_address(hp, dest, sizeof(dest));
895
896
0
  if(hp->ai_family == AF_INET) {
897
0
    struct sockaddr_in *saddr_in;
898
0
    desttype = 1; /* ATYP: IPv4 = 1 */
899
0
    destlen = 4;
900
0
    saddr_in = (struct sockaddr_in *)(void *)hp->ai_addr;
901
0
    destination = (unsigned char *)&saddr_in->sin_addr.s_addr;
902
0
    CURL_TRC_CF(data, cf, "SOCKS5 connect to %s:%d (locally resolved)",
903
0
                dest, sx->remote_port);
904
0
  }
905
0
#ifdef USE_IPV6
906
0
  else if(hp->ai_family == AF_INET6) {
907
0
    struct sockaddr_in6 *saddr_in6;
908
0
    desttype = 4; /* ATYP: IPv6 = 4 */
909
0
    destlen = 16;
910
0
    saddr_in6 = (struct sockaddr_in6 *)(void *)hp->ai_addr;
911
0
    destination = (unsigned char *)&saddr_in6->sin6_addr.s6_addr;
912
0
    CURL_TRC_CF(data, cf, "SOCKS5 connect to [%s]:%d (locally resolved)",
913
0
                dest, sx->remote_port);
914
0
  }
915
0
#endif
916
917
0
  if(!destination) {
918
0
    failf(data, "SOCKS5 connection to %s not supported", dest);
919
0
    presult = CURLPX_RESOLVE_HOST;
920
0
    goto out;
921
0
  }
922
923
0
  req[0] = desttype;
924
0
  result = Curl_bufq_write(&sx->iobuf, req, 1, &nwritten);
925
0
  if(result || (nwritten != 1)) {
926
0
    presult = CURLPX_SEND_REQUEST;
927
0
    goto out;
928
0
  }
929
0
  result = Curl_bufq_write(&sx->iobuf, destination, destlen, &nwritten);
930
0
  if(result || (nwritten != destlen)) {
931
0
    presult = CURLPX_SEND_REQUEST;
932
0
    goto out;
933
0
  }
934
  /* PORT MSB+LSB */
935
0
  req[0] = (unsigned char)((sx->remote_port >> 8) & 0xffu);
936
0
  req[1] = (unsigned char)(sx->remote_port & 0xffu);
937
0
  result = Curl_bufq_write(&sx->iobuf, req, 2, &nwritten);
938
0
  if(result || (nwritten != 2)) {
939
0
    presult = CURLPX_SEND_REQUEST;
940
0
    goto out;
941
0
  }
942
943
0
out:
944
0
  if(dns)
945
0
    Curl_resolv_unlink(data, &dns);
946
0
  *done = (presult == CURLPX_OK);
947
0
  return presult;
948
0
}
949
950
static CURLproxycode socks5_recv_resp1(struct socks_state *sx,
951
                                       struct Curl_cfilter *cf,
952
                                       struct Curl_easy *data,
953
                                       bool *done)
954
0
{
955
0
  const unsigned char *resp;
956
0
  size_t rlen, resp_len = 8; /* minimum response length */
957
0
  CURLproxycode presult;
958
959
0
  presult = socks_recv(sx, cf, data, resp_len, done);
960
0
  if(presult)
961
0
    return presult;
962
0
  else if(!*done)
963
0
    return CURLPX_OK;
964
965
0
  if(!Curl_bufq_peek(&sx->iobuf, &resp, &rlen) || rlen < resp_len) {
966
0
    failf(data, "SOCKS5 response is incomplete.");
967
0
    return CURLPX_RECV_CONNECT;
968
0
  }
969
970
  /* Response packet includes BND.ADDR is variable length parameter by RFC
971
     1928, so the response packet MUST be read until the end to avoid errors
972
     at subsequent protocol level.
973
974
     +----+-----+-------+------+----------+----------+
975
     |VER | REP |  RSV  | ATYP | BND.ADDR | BND.PORT |
976
     +----+-----+-------+------+----------+----------+
977
     | 1  |  1  | X'00' |  1   | Variable |    2     |
978
     +----+-----+-------+------+----------+----------+
979
980
     ATYP:
981
     o  IP v4 address: X'01', BND.ADDR = 4 byte
982
     o  domain name:   X'03', BND.ADDR = [ 1 byte length, string ]
983
     o  IP v6 address: X'04', BND.ADDR = 16 byte
984
  */
985
0
  if(resp[0] != 5) { /* version */
986
0
    failf(data, "SOCKS5 reply has wrong version, version should be 5.");
987
0
    return CURLPX_BAD_VERSION;
988
0
  }
989
0
  else if(resp[1]) { /* Anything besides 0 is an error */
990
0
    CURLproxycode rc = CURLPX_REPLY_UNASSIGNED;
991
0
    int code = resp[1];
992
0
    failf(data, "cannot complete SOCKS5 connection to %s. (%d)",
993
0
          sx->hostname, code);
994
0
    if(code < 9) {
995
      /* RFC 1928 section 6 lists: */
996
0
      static const CURLproxycode lookup[] = {
997
0
        CURLPX_OK,
998
0
        CURLPX_REPLY_GENERAL_SERVER_FAILURE,
999
0
        CURLPX_REPLY_NOT_ALLOWED,
1000
0
        CURLPX_REPLY_NETWORK_UNREACHABLE,
1001
0
        CURLPX_REPLY_HOST_UNREACHABLE,
1002
0
        CURLPX_REPLY_CONNECTION_REFUSED,
1003
0
        CURLPX_REPLY_TTL_EXPIRED,
1004
0
        CURLPX_REPLY_COMMAND_NOT_SUPPORTED,
1005
0
        CURLPX_REPLY_ADDRESS_TYPE_NOT_SUPPORTED,
1006
0
      };
1007
0
      rc = lookup[code];
1008
0
    }
1009
0
    return rc;
1010
0
  }
1011
1012
  /* Calculate real packet size */
1013
0
  switch(resp[3]) {
1014
0
  case 1: /* IPv4 */
1015
0
    resp_len = 4 + 4 + 2;
1016
0
    break;
1017
0
  case 3: /* domain name */
1018
0
    resp_len = 4 + 1 + resp[4] + 2; /* header, var length, var bytes, port */
1019
0
    break;
1020
0
  case 4: /* IPv6 */
1021
0
    resp_len = 4 + 16 + 2;
1022
0
    break;
1023
0
  default:
1024
0
    failf(data, "SOCKS5 reply has wrong address type.");
1025
0
    return CURLPX_BAD_ADDRESS_TYPE;
1026
0
  }
1027
1028
  /* receive the rest of the response */
1029
0
  presult = socks_recv(sx, cf, data, resp_len, done);
1030
0
  if(presult)
1031
0
    return presult;
1032
0
  else if(!*done)
1033
0
    return CURLPX_OK;
1034
1035
0
  if(!Curl_bufq_peek(&sx->iobuf, &resp, &rlen) || rlen < resp_len) {
1036
0
    failf(data, "SOCKS5 response is incomplete.");
1037
0
    return CURLPX_RECV_CONNECT;
1038
0
  }
1039
  /* got it all */
1040
0
  *done = TRUE;
1041
0
  return CURLPX_OK;
1042
0
}
1043
1044
/*
1045
 * This function logs in to a SOCKS5 proxy and sends the specifics to the final
1046
 * destination server.
1047
 */
1048
static CURLproxycode socks5_connect(struct Curl_cfilter *cf,
1049
                                    struct socks_state *sx,
1050
                                    struct Curl_easy *data)
1051
0
{
1052
0
  CURLproxycode presult;
1053
0
  bool done;
1054
1055
0
process_state:
1056
0
  switch(sx->state) {
1057
0
  case SOCKS_ST_INIT:
1058
0
    sx->version = 5;
1059
0
    sx->resolve_local = (cf->conn->socks_proxy.proxytype == CURLPROXY_SOCKS5);
1060
0
    sxstate(sx, cf, data, SOCKS5_ST_START);
1061
0
    FALLTHROUGH();
1062
1063
0
  case SOCKS5_ST_START:
1064
0
    if(cf->conn->bits.httpproxy)
1065
0
      CURL_TRC_CF(data, cf, "SOCKS5: connecting to HTTP proxy %s port %d",
1066
0
                  sx->hostname, sx->remote_port);
1067
0
    presult = socks5_req0_init(cf, sx, data);
1068
0
    if(presult)
1069
0
      return socks_failed(sx, cf, data, presult);
1070
0
    sxstate(sx, cf, data, SOCKS5_ST_REQ0_SEND);
1071
0
    FALLTHROUGH();
1072
1073
0
  case SOCKS5_ST_REQ0_SEND:
1074
0
    presult = socks_flush(sx, cf, data, &done);
1075
0
    if(presult)
1076
0
      return socks_failed(sx, cf, data, presult);
1077
0
    else if(!done)
1078
0
      return CURLPX_OK;
1079
    /* done sending! */
1080
0
    sxstate(sx, cf, data, SOCKS5_ST_RESP0_RECV);
1081
0
    FALLTHROUGH();
1082
1083
0
  case SOCKS5_ST_RESP0_RECV:
1084
0
    presult = socks_recv(sx, cf, data, 2, &done);
1085
0
    if(presult)
1086
0
      return socks_failed(sx, cf, data, presult);
1087
0
    else if(!done)
1088
0
      return CURLPX_OK;
1089
0
    presult = socks5_check_resp0(sx, cf, data);
1090
0
    if(presult)
1091
0
      return socks_failed(sx, cf, data, presult);
1092
    /* socks5_check_resp0() sets next socks state */
1093
0
    goto process_state;
1094
1095
0
  case SOCKS5_ST_GSSAPI_INIT: {
1096
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
1097
    /* GSSAPI stuff done non-blocking */
1098
    CURLcode result = Curl_SOCKS5_gssapi_negotiate(cf, data);
1099
    if(result) {
1100
      failf(data, "Unable to negotiate SOCKS5 GSS-API context.");
1101
      return CURLPX_GSSAPI;
1102
    }
1103
    sxstate(sx, cf, data, SOCKS5_ST_REQ1_INIT);
1104
    goto process_state;
1105
#else
1106
0
    failf(data,
1107
0
          "SOCKS5 GSSAPI per-message authentication is not supported.");
1108
0
    return socks_failed(sx, cf, data, CURLPX_GSSAPI_PERMSG);
1109
0
#endif
1110
0
  }
1111
1112
0
  case SOCKS5_ST_AUTH_INIT:
1113
0
    presult = socks5_auth_init(cf, sx, data);
1114
0
    if(presult)
1115
0
      return socks_failed(sx, cf, data, presult);
1116
0
    sxstate(sx, cf, data, SOCKS5_ST_AUTH_SEND);
1117
0
    FALLTHROUGH();
1118
1119
0
  case SOCKS5_ST_AUTH_SEND:
1120
0
    presult = socks_flush(sx, cf, data, &done);
1121
0
    if(presult)
1122
0
      return socks_failed(sx, cf, data, presult);
1123
0
    else if(!done)
1124
0
      return CURLPX_OK;
1125
0
    sxstate(sx, cf, data, SOCKS5_ST_AUTH_RECV);
1126
0
    FALLTHROUGH();
1127
1128
0
  case SOCKS5_ST_AUTH_RECV:
1129
0
    presult = socks_recv(sx, cf, data, 2, &done);
1130
0
    if(presult)
1131
0
      return socks_failed(sx, cf, data, presult);
1132
0
    else if(!done)
1133
0
      return CURLPX_OK;
1134
0
    presult = socks5_check_auth_resp(sx, cf, data);
1135
0
    if(presult)
1136
0
      return socks_failed(sx, cf, data, presult);
1137
    /* Everything is good so far, user was authenticated! */
1138
0
    sxstate(sx, cf, data, SOCKS5_ST_REQ1_INIT);
1139
0
    FALLTHROUGH();
1140
1141
0
  case SOCKS5_ST_REQ1_INIT:
1142
0
    presult = socks5_req1_init(sx, cf, data);
1143
0
    if(presult)
1144
0
      return socks_failed(sx, cf, data, presult);
1145
0
    if(!sx->resolve_local) {
1146
      /* we do not resolve, request is complete */
1147
0
      sxstate(sx, cf, data, SOCKS5_ST_REQ1_SEND);
1148
0
      goto process_state;
1149
0
    }
1150
0
    sx->start_resolving = TRUE;
1151
0
    sxstate(sx, cf, data, SOCKS5_ST_RESOLVING);
1152
0
    FALLTHROUGH();
1153
1154
0
  case SOCKS5_ST_RESOLVING:
1155
0
    presult = socks5_resolving(sx, cf, data, &done);
1156
0
    if(presult)
1157
0
      return socks_failed(sx, cf, data, presult);
1158
0
    if(!done)
1159
0
      return CURLPX_OK;
1160
0
    sxstate(sx, cf, data, SOCKS5_ST_REQ1_SEND);
1161
0
    FALLTHROUGH();
1162
1163
0
  case SOCKS5_ST_REQ1_SEND:
1164
0
    presult = socks_flush(sx, cf, data, &done);
1165
0
    if(presult)
1166
0
      return socks_failed(sx, cf, data, presult);
1167
0
    else if(!done)
1168
0
      return CURLPX_OK;
1169
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
1170
    if(cf->conn->socks5_gssapi_enctype) {
1171
      failf(data, "SOCKS5 GSS-API protection not yet implemented.");
1172
      return CURLPX_GSSAPI_PROTECTION;
1173
    }
1174
#endif
1175
0
    sxstate(sx, cf, data, SOCKS5_ST_RESP1_RECV);
1176
0
    FALLTHROUGH();
1177
1178
0
  case SOCKS5_ST_RESP1_RECV:
1179
0
    presult = socks5_recv_resp1(sx, cf, data, &done);
1180
0
    if(presult)
1181
0
      return socks_failed(sx, cf, data, presult);
1182
0
    if(!done)
1183
0
      return CURLPX_OK;
1184
0
    CURL_TRC_CF(data, cf, "SOCKS5 request granted.");
1185
0
    sxstate(sx, cf, data, SOCKS_ST_SUCCESS);
1186
0
    FALLTHROUGH();
1187
1188
0
  case SOCKS_ST_SUCCESS:
1189
0
    return CURLPX_OK;
1190
1191
0
  case SOCKS_ST_FAILED:
1192
0
    DEBUGASSERT(sx->presult);
1193
0
    return sx->presult;
1194
1195
0
  default:
1196
0
    DEBUGASSERT(0);
1197
0
    return socks_failed(sx, cf, data, CURLPX_SEND_REQUEST);
1198
0
  }
1199
0
}
1200
1201
static void socks_proxy_cf_free(struct Curl_cfilter *cf)
1202
0
{
1203
0
  struct socks_state *sxstate = cf->ctx;
1204
0
  if(sxstate) {
1205
0
    Curl_bufq_free(&sxstate->iobuf);
1206
0
    curlx_free(sxstate);
1207
0
    cf->ctx = NULL;
1208
0
  }
1209
0
}
1210
1211
/* After a TCP connection to the proxy has been verified, this function does
1212
   the next magic steps. If 'done' is not set TRUE, it is not done yet and
1213
   must be called again.
1214
1215
   Note: this function's sub-functions call failf()
1216
1217
*/
1218
static CURLcode socks_proxy_cf_connect(struct Curl_cfilter *cf,
1219
                                       struct Curl_easy *data,
1220
                                       bool *done)
1221
0
{
1222
0
  CURLcode result;
1223
0
  struct connectdata *conn = cf->conn;
1224
0
  int sockindex = cf->sockindex;
1225
0
  struct socks_state *sx = cf->ctx;
1226
0
  CURLproxycode pxresult = CURLPX_OK;
1227
1228
0
  if(cf->connected) {
1229
0
    *done = TRUE;
1230
0
    return CURLE_OK;
1231
0
  }
1232
1233
0
  result = cf->next->cft->do_connect(cf->next, data, done);
1234
0
  if(result || !*done)
1235
0
    return result;
1236
1237
0
  if(!sx) {
1238
0
    cf->ctx = sx = curlx_calloc(1, sizeof(*sx));
1239
0
    if(!sx) {
1240
0
      result = CURLE_OUT_OF_MEMORY;
1241
0
      goto out;
1242
0
    }
1243
1244
    /* for the secondary socket (FTP), use the "connect to host"
1245
     * but ignore the "connect to port" (use the secondary port)
1246
     */
1247
0
    sx->hostname =
1248
0
      conn->bits.httpproxy ?
1249
0
      conn->http_proxy.host.name :
1250
0
      conn->bits.conn_to_host ?
1251
0
      conn->conn_to_host.name :
1252
0
      sockindex == SECONDARYSOCKET ?
1253
0
      conn->secondaryhostname : conn->host.name;
1254
0
    sx->remote_port =
1255
0
      conn->bits.httpproxy ? (int)conn->http_proxy.port :
1256
0
      sockindex == SECONDARYSOCKET ? conn->secondary_port :
1257
0
      conn->bits.conn_to_port ? conn->conn_to_port :
1258
0
      conn->remote_port;
1259
0
    sx->proxy_user = conn->socks_proxy.user;
1260
0
    sx->proxy_password = conn->socks_proxy.passwd;
1261
0
    Curl_bufq_init2(&sx->iobuf, SOCKS_CHUNK_SIZE, SOCKS_CHUNKS,
1262
0
                    BUFQ_OPT_SOFT_LIMIT);
1263
0
  }
1264
1265
0
  switch(conn->socks_proxy.proxytype) {
1266
0
  case CURLPROXY_SOCKS5:
1267
0
  case CURLPROXY_SOCKS5_HOSTNAME:
1268
0
    pxresult = socks5_connect(cf, sx, data);
1269
0
    break;
1270
1271
0
  case CURLPROXY_SOCKS4:
1272
0
  case CURLPROXY_SOCKS4A:
1273
0
    pxresult = socks4_connect(cf, sx, data);
1274
0
    break;
1275
1276
0
  default:
1277
0
    failf(data, "unknown proxytype option given");
1278
0
    result = CURLE_COULDNT_CONNECT;
1279
0
    goto out;
1280
0
  }
1281
1282
0
  if(pxresult) {
1283
0
    result = CURLE_PROXY;
1284
0
    data->info.pxcode = pxresult;
1285
0
    goto out;
1286
0
  }
1287
0
  else if(sx->state != SOCKS_ST_SUCCESS)
1288
0
    goto out;
1289
1290
0
#ifndef CURL_DISABLE_VERBOSE_STRINGS
1291
0
  if(Curl_trc_is_verbose(data)) {
1292
0
    struct ip_quadruple ipquad;
1293
0
    bool is_ipv6;
1294
0
    if(!Curl_conn_cf_get_ip_info(cf->next, data, &is_ipv6, &ipquad))
1295
0
      infof(data, "Opened %sSOCKS connection from %s port %u to %s port %u "
1296
0
            "(via %s port %u)",
1297
0
            (sockindex == SECONDARYSOCKET) ? "2nd " : "",
1298
0
            ipquad.local_ip, ipquad.local_port,
1299
0
            sx->hostname, sx->remote_port,
1300
0
            ipquad.remote_ip, ipquad.remote_port);
1301
0
    else
1302
0
      infof(data, "Opened %sSOCKS connection",
1303
0
            (sockindex == SECONDARYSOCKET) ? "2nd " : "");
1304
0
  }
1305
0
#endif
1306
0
  socks_proxy_cf_free(cf);
1307
0
  cf->connected = TRUE;
1308
1309
0
out:
1310
0
  *done = cf->connected;
1311
0
  return result;
1312
0
}
1313
1314
static CURLcode socks_cf_adjust_pollset(struct Curl_cfilter *cf,
1315
                                        struct Curl_easy *data,
1316
                                        struct easy_pollset *ps)
1317
0
{
1318
0
  struct socks_state *sx = cf->ctx;
1319
0
  CURLcode result = CURLE_OK;
1320
1321
0
  if(!cf->connected && sx) {
1322
    /* If we are not connected, the filter below is and has nothing
1323
     * to wait on, we determine what to wait for. */
1324
0
    curl_socket_t sock = Curl_conn_cf_get_socket(cf, data);
1325
0
    switch(sx->state) {
1326
0
    case SOCKS4_ST_SEND:
1327
0
    case SOCKS5_ST_REQ0_SEND:
1328
0
    case SOCKS5_ST_AUTH_SEND:
1329
0
    case SOCKS5_ST_REQ1_SEND:
1330
0
      CURL_TRC_CF(data, cf, "adjust pollset out (%d)", sx->state);
1331
0
      result = Curl_pollset_set_out_only(data, ps, sock);
1332
0
      break;
1333
0
    default:
1334
0
      CURL_TRC_CF(data, cf, "adjust pollset in (%d)", sx->state);
1335
0
      result = Curl_pollset_set_in_only(data, ps, sock);
1336
0
      break;
1337
0
    }
1338
0
  }
1339
0
  return result;
1340
0
}
1341
1342
static void socks_proxy_cf_close(struct Curl_cfilter *cf,
1343
                                 struct Curl_easy *data)
1344
0
{
1345
1346
0
  DEBUGASSERT(cf->next);
1347
0
  cf->connected = FALSE;
1348
0
  socks_proxy_cf_free(cf);
1349
0
  cf->next->cft->do_close(cf->next, data);
1350
0
}
1351
1352
static void socks_proxy_cf_destroy(struct Curl_cfilter *cf,
1353
                                   struct Curl_easy *data)
1354
0
{
1355
0
  (void)data;
1356
0
  socks_proxy_cf_free(cf);
1357
0
}
1358
1359
static CURLcode socks_cf_query(struct Curl_cfilter *cf,
1360
                               struct Curl_easy *data,
1361
                               int query, int *pres1, void *pres2)
1362
0
{
1363
0
  struct socks_state *sx = cf->ctx;
1364
1365
0
  switch(query) {
1366
0
  case CF_QUERY_HOST_PORT:
1367
0
    if(sx) {
1368
0
      *pres1 = sx->remote_port;
1369
0
      *((const char **)pres2) = sx->hostname;
1370
0
      return CURLE_OK;
1371
0
    }
1372
0
    break;
1373
0
  case CF_QUERY_ALPN_NEGOTIATED: {
1374
0
    const char **palpn = pres2;
1375
0
    DEBUGASSERT(palpn);
1376
0
    *palpn = NULL;
1377
0
    return CURLE_OK;
1378
0
  }
1379
0
  default:
1380
0
    break;
1381
0
  }
1382
0
  return cf->next ?
1383
0
    cf->next->cft->query(cf->next, data, query, pres1, pres2) :
1384
0
    CURLE_UNKNOWN_OPTION;
1385
0
}
1386
1387
struct Curl_cftype Curl_cft_socks_proxy = {
1388
  "SOCKS",
1389
  CF_TYPE_IP_CONNECT | CF_TYPE_PROXY,
1390
  0,
1391
  socks_proxy_cf_destroy,
1392
  socks_proxy_cf_connect,
1393
  socks_proxy_cf_close,
1394
  Curl_cf_def_shutdown,
1395
  socks_cf_adjust_pollset,
1396
  Curl_cf_def_data_pending,
1397
  Curl_cf_def_send,
1398
  Curl_cf_def_recv,
1399
  Curl_cf_def_cntrl,
1400
  Curl_cf_def_conn_is_alive,
1401
  Curl_cf_def_conn_keep_alive,
1402
  socks_cf_query,
1403
};
1404
1405
CURLcode Curl_cf_socks_proxy_insert_after(struct Curl_cfilter *cf_at,
1406
                                          struct Curl_easy *data)
1407
0
{
1408
0
  struct Curl_cfilter *cf;
1409
0
  CURLcode result;
1410
1411
0
  (void)data;
1412
0
  result = Curl_cf_create(&cf, &Curl_cft_socks_proxy, NULL);
1413
0
  if(!result)
1414
0
    Curl_conn_cf_insert_after(cf_at, cf);
1415
0
  return result;
1416
0
}
1417
1418
#endif /* CURL_DISABLE_PROXY */