Coverage Report

Created: 2025-11-23 06:22

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