Coverage Report

Created: 2026-05-30 06:06

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