Coverage Report

Created: 2026-10-15 06:19

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