Coverage Report

Created: 2026-09-14 07:07

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
136
#define SOCKS_CHUNK_SIZE    1024
95
136
#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.21k
#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.21k
{
176
1.21k
  enum socks_state_t oldstate = sx->state;
177
178
1.21k
  if(oldstate == state)
179
    /* do not bother when the new state is the same as the old state */
180
84
    return;
181
182
1.13k
  sx->state = state;
183
184
1.13k
#if defined(DEBUGBUILD) && defined(CURLVERBOSE)
185
1.13k
  CURL_TRC_CF(data, cf, "[%s] -> [%s] (line %d)",
186
1.13k
              cf_socks_statename[oldstate],
187
1.13k
              cf_socks_statename[sx->state], lineno);
188
#else
189
  (void)cf;
190
  (void)data;
191
#endif
192
1.13k
}
193
194
static CURLproxycode socks_failed(struct socks_ctx *sx,
195
                                  struct Curl_cfilter *cf,
196
                                  struct Curl_easy *data,
197
                                  CURLproxycode presult)
198
68
{
199
68
  sxstate(sx, cf, data, SOCKS_ST_FAILED);
200
68
  sx->presult = presult;
201
68
  return presult;
202
68
}
203
204
static CURLproxycode socks_flush(struct socks_ctx *sx,
205
                                 struct Curl_cfilter *cf,
206
                                 struct Curl_easy *data,
207
                                 bool *done)
208
309
{
209
309
  CURLcode result;
210
309
  size_t nwritten;
211
212
309
  *done = FALSE;
213
618
  while(!Curl_bufq_is_empty(&sx->iobuf)) {
214
309
    result = Curl_cf_send_bufq(cf->next, data, &sx->iobuf, NULL, 0,
215
309
                               &nwritten);
216
309
    if(result == CURLE_AGAIN)
217
0
      return CURLPX_OK;
218
309
    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
309
  }
224
309
  *done = TRUE;
225
309
  return CURLPX_OK;
226
309
}
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
487
{
234
487
  CURLcode result;
235
487
  size_t nread;
236
237
487
  *done = FALSE;
238
852
  while(Curl_bufq_len(&sx->iobuf) < min_bytes) {
239
509
    result = Curl_cf_recv_bufq(cf->next, data, &sx->iobuf,
240
509
                               min_bytes - Curl_bufq_len(&sx->iobuf),
241
509
                               &nread);
242
509
    if(result == CURLE_AGAIN)
243
99
      return CURLPX_OK;
244
410
    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
410
    else if(!nread) { /* EOF */
250
45
      if(Curl_bufq_len(&sx->iobuf) < min_bytes) {
251
45
        failf(data, "Failed to receive SOCKS response, "
252
45
              "proxy closed connection");
253
45
        return CURLPX_RECV_CONNECT;
254
45
      }
255
0
      break;
256
45
    }
257
509
  }
258
343
  *done = TRUE;
259
343
  return CURLPX_OK;
260
487
}
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 trailing zero */
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
136
{
568
136
  const unsigned char auth = data->set.socks5auth;
569
136
  unsigned char req[5]; /* version + len + 3 possible auth methods */
570
136
  unsigned char nauths;
571
136
  size_t req_len, nwritten;
572
136
  CURLcode result;
573
574
136
  (void)cf;
575
  /* RFC1928 chapter 5 specifies max 255 chars for domain name in packet */
576
136
  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
136
  if(auth & ~(CURLAUTH_BASIC | CURLAUTH_GSSAPI))
583
0
    infof(data, "warning: unsupported value passed to "
584
136
          "CURLOPT_SOCKS5_AUTH: %u", auth);
585
136
  if(!(auth & CURLAUTH_BASIC))
586
    /* disable username/password auth */
587
3
    Curl_creds_unlink(&sx->creds);
588
589
136
  req[0] = 5;   /* version */
590
136
  nauths = 1;
591
136
  req[1 + nauths] = 0;   /* 1. no authentication */
592
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
593
  if(auth & CURLAUTH_GSSAPI) {
594
    ++nauths;
595
    req[1 + nauths] = 1; /* GSS-API */
596
  }
597
#endif
598
136
  if(sx->creds) {
599
0
    ++nauths;
600
0
    req[1 + nauths] = 2; /* username/password */
601
0
  }
602
136
  req[1] = nauths;
603
136
  req_len = 2 + nauths;
604
605
136
  result = Curl_bufq_write(&sx->iobuf, req, req_len, &nwritten);
606
136
  if(result || (nwritten != req_len))
607
0
    return CURLPX_SEND_REQUEST;
608
136
  return CURLPX_OK;
609
136
}
610
611
static CURLproxycode socks5_check_resp0(struct socks_ctx *sx,
612
                                        struct Curl_cfilter *cf,
613
                                        struct Curl_easy *data)
614
110
{
615
110
  const unsigned char *resp;
616
110
  unsigned char auth_mode;
617
110
  size_t rlen;
618
619
110
  if(!Curl_bufq_peek(&sx->iobuf, &resp, &rlen) || rlen < 2) {
620
0
    failf(data, "SOCKS5 initial reply is incomplete.");
621
0
    return CURLPX_RECV_CONNECT;
622
0
  }
623
624
110
  if(resp[0] != 5) {
625
9
    failf(data, "Received invalid version in initial SOCKS5 response.");
626
9
    return CURLPX_BAD_VERSION;
627
9
  }
628
629
101
  auth_mode = resp[1];
630
101
  Curl_bufq_skip(&sx->iobuf, 2);
631
632
101
  switch(auth_mode) {
633
12
  case 0:
634
    /* DONE! No authentication needed. Send request. */
635
12
    sxstate(sx, cf, data, SOCKS5_ST_REQ1_INIT);
636
12
    return CURLPX_OK;
637
2
  case 1:
638
2
    if(data->set.socks5auth & CURLAUTH_GSSAPI) {
639
1
      sxstate(sx, cf, data, SOCKS5_ST_GSSAPI_INIT);
640
1
      return CURLPX_OK;
641
1
    }
642
1
    failf(data, "SOCKS5 GSSAPI per-message authentication is not enabled.");
643
1
    return CURLPX_GSSAPI_PERMSG;
644
85
  case 2:
645
    /* regular name + password authentication */
646
85
    if(data->set.socks5auth & CURLAUTH_BASIC) {
647
84
      sxstate(sx, cf, data, SOCKS5_ST_AUTH_INIT);
648
84
      return CURLPX_OK;
649
84
    }
650
1
    failf(data, "BASIC authentication proposed but not enabled.");
651
1
    return CURLPX_NO_AUTH;
652
1
  case 255:
653
1
    failf(data, "No authentication method was acceptable.");
654
1
    return CURLPX_NO_AUTH;
655
1
  default:
656
1
    failf(data, "Unknown SOCKS5 mode attempted to be used by server.");
657
1
    return CURLPX_UNKNOWN_MODE;
658
101
  }
659
101
}
660
661
static CURLproxycode socks5_auth_init(struct Curl_cfilter *cf,
662
                                      struct socks_ctx *sx,
663
                                      struct Curl_easy *data)
664
84
{
665
  /* Needs username and password */
666
84
  size_t ulen = 0, plen = 0, nwritten;
667
84
  unsigned char buf[2];
668
84
  CURLcode result;
669
670
84
  if(sx->creds) {
671
0
    ulen = strlen(sx->creds->user);
672
0
    plen = strlen(sx->creds->passwd);
673
    /* the lengths must fit in a single byte */
674
0
    if(ulen > 255) {
675
0
      failf(data, "Excessive username length for proxy auth");
676
0
      return CURLPX_LONG_USER;
677
0
    }
678
0
    if(plen > 255) {
679
0
      failf(data, "Excessive password length for proxy auth");
680
0
      return CURLPX_LONG_PASSWD;
681
0
    }
682
0
  }
683
684
  /*   username/password request looks like
685
   * +----+------+----------+------+----------+
686
   * |VER | ULEN |  UNAME   | PLEN |  PASSWD  |
687
   * +----+------+----------+------+----------+
688
   * | 1  |  1   | 1 to 255 |  1   | 1 to 255 |
689
   * +----+------+----------+------+----------+
690
   */
691
84
  buf[0] = 1;    /* username/pw subnegotiation version */
692
84
  buf[1] = (unsigned char)ulen;
693
84
  result = Curl_bufq_write(&sx->iobuf, buf, 2, &nwritten);
694
84
  if(result || (nwritten != 2))
695
0
    return CURLPX_SEND_REQUEST;
696
84
  if(ulen) {
697
0
    result = Curl_bufq_cwrite(&sx->iobuf, sx->creds->user, ulen, &nwritten);
698
0
    if(result || (nwritten != ulen))
699
0
      return CURLPX_SEND_REQUEST;
700
0
  }
701
84
  buf[0] = (unsigned char)plen;
702
84
  result = Curl_bufq_write(&sx->iobuf, buf, 1, &nwritten);
703
84
  if(result || (nwritten != 1))
704
0
    return CURLPX_SEND_REQUEST;
705
84
  if(plen) {
706
0
    result = Curl_bufq_cwrite(&sx->iobuf, sx->creds->passwd, plen, &nwritten);
707
0
    if(result || (nwritten != plen))
708
0
      return CURLPX_SEND_REQUEST;
709
0
  }
710
84
  sxstate(sx, cf, data, SOCKS5_ST_AUTH_SEND);
711
84
  return CURLPX_OK;
712
84
}
713
714
static CURLproxycode socks5_check_auth_resp(struct socks_ctx *sx,
715
                                            struct Curl_cfilter *cf,
716
                                            struct Curl_easy *data)
717
78
{
718
78
  const unsigned char *resp;
719
78
  unsigned char auth_status;
720
78
  size_t rlen;
721
722
78
  (void)cf;
723
78
  if(!Curl_bufq_peek(&sx->iobuf, &resp, &rlen) || rlen < 2) {
724
0
    failf(data, "SOCKS5 sub-negotiation response incomplete.");
725
0
    return CURLPX_RECV_CONNECT;
726
0
  }
727
728
  /* ignore the first (VER) byte */
729
78
  auth_status = resp[1];
730
78
  if(auth_status) {
731
1
    failf(data, "User was rejected by the SOCKS5 server (%d %d).",
732
1
          resp[0], resp[1]);
733
1
    return CURLPX_USER_REJECTED;
734
1
  }
735
77
  Curl_bufq_skip(&sx->iobuf, 2);
736
77
  return CURLPX_OK;
737
78
}
738
739
static CURLproxycode socks5_req1_init(struct socks_ctx *sx,
740
                                      struct Curl_cfilter *cf,
741
                                      struct Curl_easy *data)
742
89
{
743
89
  unsigned char req[5];
744
89
  unsigned char ipbuf[16];
745
89
  const unsigned char *destination;
746
89
  unsigned char desttype, destlen, hdlen;
747
89
  size_t nwritten;
748
89
  CURLcode result;
749
750
89
  req[0] = 5; /* version (SOCKS5) */
751
89
  req[1] = 1; /* connect */
752
89
  req[2] = 0; /* must be zero */
753
89
  if(sx->resolve_local) {
754
    /* rest of request is added after resolving */
755
89
    result = Curl_bufq_write(&sx->iobuf, req, 3, &nwritten);
756
89
    if(result || (nwritten != 3))
757
0
      return CURLPX_SEND_REQUEST;
758
89
    return CURLPX_OK;
759
89
  }
760
761
  /* remote resolving, send what type+addr/string to resolve */
762
0
#ifdef USE_IPV6
763
0
  if(strchr(sx->dest->hostname, ':')) {
764
0
    desttype = 4;
765
0
    destination = ipbuf;
766
0
    destlen = 16;
767
0
    if(curlx_inet_pton(AF_INET6, sx->dest->hostname, ipbuf) != 1)
768
0
      return CURLPX_BAD_ADDRESS_TYPE;
769
0
  }
770
0
  else
771
0
#endif
772
0
  if(curlx_inet_pton(AF_INET, sx->dest->hostname, ipbuf) == 1) {
773
0
    desttype = 1;
774
0
    destination = ipbuf;
775
0
    destlen = 4;
776
0
  }
777
0
  else {
778
0
    const size_t hostname_len = strlen(sx->dest->hostname);
779
    /* socks5_req0_init() already rejects hostnames longer than 255 bytes, so
780
       this cast to unsigned char is safe. Assert to guard against future
781
       refactoring that might remove or reorder that earlier check. */
782
0
    DEBUGASSERT(hostname_len <= 255);
783
0
    desttype = 3;
784
0
    destination = (const unsigned char *)sx->dest->hostname;
785
0
    destlen = (unsigned char)hostname_len; /* 1-byte length */
786
0
  }
787
788
0
  req[3] = desttype;
789
0
  req[4] = destlen;
790
0
  hdlen = (desttype == 3) ? 5 : 4; /* no length byte for ip addresses */
791
0
  result = Curl_bufq_write(&sx->iobuf, req, hdlen, &nwritten);
792
0
  if(result || (nwritten != hdlen))
793
0
    return CURLPX_SEND_REQUEST;
794
0
  result = Curl_bufq_write(&sx->iobuf, destination, destlen, &nwritten);
795
0
  if(result || (nwritten != destlen))
796
0
    return CURLPX_SEND_REQUEST;
797
  /* PORT MSB+LSB */
798
0
  req[0] = (unsigned char)((sx->dest->port >> 8) & 0xff);
799
0
  req[1] = (unsigned char)(sx->dest->port & 0xff);
800
0
  result = Curl_bufq_write(&sx->iobuf, req, 2, &nwritten);
801
0
  if(result || (nwritten != 2))
802
0
    return CURLPX_SEND_REQUEST;
803
0
  CURL_TRC_CF(data, cf, "SOCKS5 connect to %s:%u (remotely resolved)",
804
0
              sx->dest->hostname, sx->dest->port);
805
0
  return CURLPX_OK;
806
0
}
807
808
static CURLproxycode socks5_resolving(struct socks_ctx *sx,
809
                                      struct Curl_cfilter *cf,
810
                                      struct Curl_easy *data,
811
                                      bool *done)
812
160
{
813
160
  const struct Curl_addrinfo *ai = NULL;
814
160
  char dest[MAX_IPADR_LEN];  /* printable address */
815
160
  const unsigned char *destination = NULL;
816
160
  unsigned char desttype = 1, destlen = 4;
817
160
  unsigned char req[2];
818
160
  CURLcode result;
819
160
  CURLproxycode presult = CURLPX_OK;
820
160
  size_t nwritten;
821
822
160
  *done = FALSE;
823
160
  result = Curl_conn_dns_addr_result(cf->conn, cf->sockindex, sx->dest);
824
160
  if(result) {
825
71
    if(result != CURLE_AGAIN) {
826
0
      failf(data, "error %d resolving SOCKS destination %s:%u",
827
0
            (int)result, sx->dest->hostname, sx->dest->port);
828
0
      return CURLPX_RESOLVE_HOST;
829
0
    }
830
71
    return CURLPX_OK;
831
71
  }
832
833
89
#ifdef USE_IPV6
834
89
  if(data->set.ipver != CURL_IPRESOLVE_V4)
835
87
    ai = Curl_conn_dns_get_ai(data, sx->dest, cf->sockindex, AF_INET6, 0);
836
89
#endif
837
89
  if(!ai)
838
89
    ai = Curl_conn_dns_get_ai(data, sx->dest, cf->sockindex, AF_INET, 0);
839
840
89
  if(!ai) {
841
0
    failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.",
842
0
          sx->dest->hostname);
843
0
    presult = CURLPX_RESOLVE_HOST;
844
0
    goto out;
845
0
  }
846
847
89
  Curl_printable_address(ai, dest, sizeof(dest));
848
849
89
  if(ai->ai_family == AF_INET) {
850
89
    struct sockaddr_in *saddr_in;
851
89
    desttype = 1; /* ATYP: IPv4 = 1 */
852
89
    destlen = 4;
853
89
    saddr_in = (struct sockaddr_in *)(void *)ai->ai_addr;
854
89
    destination = (const unsigned char *)&saddr_in->sin_addr.s_addr;
855
89
    CURL_TRC_CF(data, cf, "SOCKS5 connect to %s:%u (locally resolved)",
856
89
                dest, sx->dest->port);
857
89
  }
858
0
#ifdef USE_IPV6
859
0
  else if(ai->ai_family == AF_INET6) {
860
0
    struct sockaddr_in6 *saddr_in6;
861
0
    desttype = 4; /* ATYP: IPv6 = 4 */
862
0
    destlen = 16;
863
0
    saddr_in6 = (struct sockaddr_in6 *)(void *)ai->ai_addr;
864
0
    destination = (const unsigned char *)&saddr_in6->sin6_addr.s6_addr;
865
0
    CURL_TRC_CF(data, cf, "SOCKS5 connect to [%s]:%u (locally resolved)",
866
0
                dest, sx->dest->port);
867
0
  }
868
89
#endif
869
870
89
  if(!destination) {
871
0
    failf(data, "SOCKS5 connection to %s not supported", dest);
872
0
    presult = CURLPX_RESOLVE_HOST;
873
0
    goto out;
874
0
  }
875
876
89
  req[0] = desttype;
877
89
  result = Curl_bufq_write(&sx->iobuf, req, 1, &nwritten);
878
89
  if(result || (nwritten != 1)) {
879
0
    presult = CURLPX_SEND_REQUEST;
880
0
    goto out;
881
0
  }
882
89
  result = Curl_bufq_write(&sx->iobuf, destination, destlen, &nwritten);
883
89
  if(result || (nwritten != destlen)) {
884
0
    presult = CURLPX_SEND_REQUEST;
885
0
    goto out;
886
0
  }
887
  /* PORT MSB+LSB */
888
89
  req[0] = (unsigned char)((sx->dest->port >> 8) & 0xffU);
889
89
  req[1] = (unsigned char)(sx->dest->port & 0xffU);
890
89
  result = Curl_bufq_write(&sx->iobuf, req, 2, &nwritten);
891
89
  if(result || (nwritten != 2)) {
892
0
    presult = CURLPX_SEND_REQUEST;
893
0
    goto out;
894
0
  }
895
896
89
out:
897
89
  *done = (presult == CURLPX_OK);
898
89
  return presult;
899
89
}
900
901
static CURLproxycode socks5_recv_resp1(struct socks_ctx *sx,
902
                                       struct Curl_cfilter *cf,
903
                                       struct Curl_easy *data,
904
                                       bool *done)
905
130
{
906
130
  const unsigned char *resp;
907
130
  size_t rlen, resp_len = 8; /* minimum response length */
908
130
  CURLproxycode presult;
909
910
130
  presult = socks_recv(sx, cf, data, resp_len, done);
911
130
  if(presult)
912
13
    return presult;
913
117
  else if(!*done)
914
12
    return CURLPX_OK;
915
916
105
  if(!Curl_bufq_peek(&sx->iobuf, &resp, &rlen) || rlen < resp_len) {
917
0
    failf(data, "SOCKS5 response is incomplete.");
918
0
    return CURLPX_RECV_CONNECT;
919
0
  }
920
921
  /* Response packet includes BND.ADDR is variable length parameter by RFC
922
     1928, so the response packet MUST be read until the end to avoid errors
923
     at subsequent protocol level.
924
925
     +----+-----+-------+------+----------+----------+
926
     |VER | REP |  RSV  | ATYP | BND.ADDR | BND.PORT |
927
     +----+-----+-------+------+----------+----------+
928
     | 1  |  1  | 0x00  |  1   | Variable |    2     |
929
     +----+-----+-------+------+----------+----------+
930
931
     ATYP:
932
     o IPv4 address: 0x01, BND.ADDR = 4-byte
933
     o domain name:  0x03, BND.ADDR = [ 1-byte length, string ]
934
     o IPv6 address: 0x04, BND.ADDR = 16-byte
935
   */
936
105
  if(resp[0] != 5) { /* version */
937
3
    failf(data, "SOCKS5 reply has wrong version, version should be 5.");
938
3
    return CURLPX_BAD_VERSION;
939
3
  }
940
102
  else if(resp[1]) { /* Anything besides 0 is an error */
941
2
    CURLproxycode rc = CURLPX_REPLY_UNASSIGNED;
942
2
    int code = resp[1];
943
2
    failf(data, "cannot complete SOCKS5 connection to %s. (%d)",
944
2
          sx->dest->hostname, code);
945
2
    if(code < 9) {
946
      /* RFC 1928 section 6 lists: */
947
1
      static const CURLproxycode lookup[] = {
948
1
        CURLPX_OK,
949
1
        CURLPX_REPLY_GENERAL_SERVER_FAILURE,
950
1
        CURLPX_REPLY_NOT_ALLOWED,
951
1
        CURLPX_REPLY_NETWORK_UNREACHABLE,
952
1
        CURLPX_REPLY_HOST_UNREACHABLE,
953
1
        CURLPX_REPLY_CONNECTION_REFUSED,
954
1
        CURLPX_REPLY_TTL_EXPIRED,
955
1
        CURLPX_REPLY_COMMAND_NOT_SUPPORTED,
956
1
        CURLPX_REPLY_ADDRESS_TYPE_NOT_SUPPORTED,
957
1
      };
958
1
      rc = lookup[code];
959
1
    }
960
2
    return rc;
961
2
  }
962
963
  /* Calculate real packet size */
964
100
  switch(resp[3]) {
965
8
  case 1: /* IPv4 */
966
8
    resp_len = 4 + 4 + 2;
967
8
    break;
968
15
  case 3: /* domain name */
969
15
    resp_len = 4 + 1 + resp[4] + 2; /* header, var length, var bytes, port */
970
15
    break;
971
74
  case 4: /* IPv6 */
972
74
    resp_len = 4 + 16 + 2;
973
74
    break;
974
3
  default:
975
3
    failf(data, "SOCKS5 reply has wrong address type.");
976
3
    return CURLPX_BAD_ADDRESS_TYPE;
977
100
  }
978
979
  /* receive the rest of the response */
980
97
  presult = socks_recv(sx, cf, data, resp_len, done);
981
97
  if(presult)
982
8
    return presult;
983
89
  else if(!*done)
984
39
    return CURLPX_OK;
985
986
50
  if(!Curl_bufq_peek(&sx->iobuf, &resp, &rlen) || rlen < resp_len) {
987
0
    failf(data, "SOCKS5 response is incomplete.");
988
0
    return CURLPX_RECV_CONNECT;
989
0
  }
990
  /* got it all */
991
50
  *done = TRUE;
992
50
  return CURLPX_OK;
993
50
}
994
995
/*
996
 * This function logs in to a SOCKS5 proxy and sends the specifics to the final
997
 * destination server.
998
 */
999
static CURLproxycode socks5_connect(struct Curl_cfilter *cf,
1000
                                    struct socks_ctx *sx,
1001
                                    struct Curl_easy *data)
1002
288
{
1003
288
  CURLproxycode presult;
1004
288
  bool done;
1005
1006
385
process_state:
1007
385
  switch(sx->state) {
1008
136
  case SOCKS_ST_INIT:
1009
136
    sx->version = 5;
1010
136
    sxstate(sx, cf, data, SOCKS5_ST_START);
1011
136
    FALLTHROUGH();
1012
1013
136
  case SOCKS5_ST_START:
1014
136
    CURL_TRC_CF(data, cf, "SOCKS5: connecting to %s:%u",
1015
136
                sx->dest->hostname, sx->dest->port);
1016
136
    presult = socks5_req0_init(cf, sx, data);
1017
136
    if(presult)
1018
0
      return socks_failed(sx, cf, data, presult);
1019
136
    sxstate(sx, cf, data, SOCKS5_ST_REQ0_SEND);
1020
136
    FALLTHROUGH();
1021
1022
136
  case SOCKS5_ST_REQ0_SEND:
1023
136
    presult = socks_flush(sx, cf, data, &done);
1024
136
    if(presult)
1025
0
      return socks_failed(sx, cf, data, presult);
1026
136
    else if(!done)
1027
0
      return CURLPX_OK;
1028
    /* done sending! */
1029
136
    sxstate(sx, cf, data, SOCKS5_ST_RESP0_RECV);
1030
136
    FALLTHROUGH();
1031
1032
166
  case SOCKS5_ST_RESP0_RECV:
1033
166
    presult = socks_recv(sx, cf, data, 2, &done);
1034
166
    if(presult)
1035
22
      return socks_failed(sx, cf, data, presult);
1036
144
    else if(!done)
1037
34
      return CURLPX_OK;
1038
110
    presult = socks5_check_resp0(sx, cf, data);
1039
110
    if(presult)
1040
13
      return socks_failed(sx, cf, data, presult);
1041
    /* socks5_check_resp0() sets next socks state */
1042
97
    goto process_state;
1043
1044
97
  case SOCKS5_ST_GSSAPI_INIT: {
1045
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
1046
    /* GSSAPI stuff done non-blocking */
1047
    CURLcode result = Curl_SOCKS5_gssapi_negotiate(cf, data, sx->creds);
1048
    if(result) {
1049
      failf(data, "Unable to negotiate SOCKS5 GSS-API context.");
1050
      return CURLPX_GSSAPI;
1051
    }
1052
    sxstate(sx, cf, data, SOCKS5_ST_REQ1_INIT);
1053
    goto process_state;
1054
#else
1055
1
    failf(data, "SOCKS5 GSSAPI per-message authentication is not supported.");
1056
1
    return socks_failed(sx, cf, data, CURLPX_GSSAPI_PERMSG);
1057
110
#endif
1058
110
  }
1059
1060
84
  case SOCKS5_ST_AUTH_INIT:
1061
84
    presult = socks5_auth_init(cf, sx, data);
1062
84
    if(presult)
1063
0
      return socks_failed(sx, cf, data, presult);
1064
84
    sxstate(sx, cf, data, SOCKS5_ST_AUTH_SEND);
1065
84
    FALLTHROUGH();
1066
1067
84
  case SOCKS5_ST_AUTH_SEND:
1068
84
    presult = socks_flush(sx, cf, data, &done);
1069
84
    if(presult)
1070
0
      return socks_failed(sx, cf, data, presult);
1071
84
    else if(!done)
1072
0
      return CURLPX_OK;
1073
84
    sxstate(sx, cf, data, SOCKS5_ST_AUTH_RECV);
1074
84
    FALLTHROUGH();
1075
1076
94
  case SOCKS5_ST_AUTH_RECV:
1077
94
    presult = socks_recv(sx, cf, data, 2, &done);
1078
94
    if(presult)
1079
2
      return socks_failed(sx, cf, data, presult);
1080
92
    else if(!done)
1081
14
      return CURLPX_OK;
1082
78
    presult = socks5_check_auth_resp(sx, cf, data);
1083
78
    if(presult)
1084
1
      return socks_failed(sx, cf, data, presult);
1085
    /* Everything is good so far, user was authenticated! */
1086
77
    sxstate(sx, cf, data, SOCKS5_ST_REQ1_INIT);
1087
77
    FALLTHROUGH();
1088
1089
89
  case SOCKS5_ST_REQ1_INIT:
1090
89
    presult = socks5_req1_init(sx, cf, data);
1091
89
    if(presult)
1092
0
      return socks_failed(sx, cf, data, presult);
1093
89
    if(!sx->resolve_local) {
1094
      /* we do not resolve, request is complete */
1095
0
      sxstate(sx, cf, data, SOCKS5_ST_REQ1_SEND);
1096
0
      goto process_state;
1097
0
    }
1098
89
    sxstate(sx, cf, data, SOCKS5_ST_RESOLVING);
1099
89
    FALLTHROUGH();
1100
1101
160
  case SOCKS5_ST_RESOLVING:
1102
160
    presult = socks5_resolving(sx, cf, data, &done);
1103
160
    if(presult)
1104
0
      return socks_failed(sx, cf, data, presult);
1105
160
    if(!done)
1106
71
      return CURLPX_OK;
1107
89
    sxstate(sx, cf, data, SOCKS5_ST_REQ1_SEND);
1108
89
    FALLTHROUGH();
1109
1110
89
  case SOCKS5_ST_REQ1_SEND:
1111
89
    presult = socks_flush(sx, cf, data, &done);
1112
89
    if(presult)
1113
0
      return socks_failed(sx, cf, data, presult);
1114
89
    else if(!done)
1115
0
      return CURLPX_OK;
1116
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
1117
    if(cf->conn->socks5_gssapi_enctype) {
1118
      failf(data, "SOCKS5 GSS-API protection not yet implemented.");
1119
      return CURLPX_GSSAPI_PROTECTION;
1120
    }
1121
#endif
1122
89
    sxstate(sx, cf, data, SOCKS5_ST_RESP1_RECV);
1123
89
    FALLTHROUGH();
1124
1125
130
  case SOCKS5_ST_RESP1_RECV:
1126
130
    presult = socks5_recv_resp1(sx, cf, data, &done);
1127
130
    if(presult)
1128
29
      return socks_failed(sx, cf, data, presult);
1129
101
    if(!done)
1130
51
      return CURLPX_OK;
1131
50
    CURL_TRC_CF(data, cf, "SOCKS5 request granted.");
1132
50
    sxstate(sx, cf, data, SOCKS_ST_SUCCESS);
1133
50
    FALLTHROUGH();
1134
1135
50
  case SOCKS_ST_SUCCESS:
1136
50
    return CURLPX_OK;
1137
1138
0
  case SOCKS_ST_FAILED:
1139
0
    DEBUGASSERT(sx->presult);
1140
0
    return sx->presult;
1141
1142
0
  default:
1143
0
    DEBUGASSERT(0);
1144
0
    return socks_failed(sx, cf, data, CURLPX_SEND_REQUEST);
1145
385
  }
1146
385
}
1147
1148
static void socks_proxy_ctx_free(struct socks_ctx *ctx)
1149
136
{
1150
136
  if(ctx) {
1151
136
    Curl_peer_unlink(&ctx->dest);
1152
136
    Curl_creds_unlink(&ctx->creds);
1153
136
    Curl_bufq_free(&ctx->iobuf);
1154
136
    curlx_free(ctx);
1155
136
  }
1156
136
}
1157
1158
/* After a TCP connection to the proxy has been verified, this function does
1159
   the next magic steps. If 'done' is not set TRUE, it is not done yet and
1160
   must be called again.
1161
1162
   Note: this function's sub-functions call failf() */
1163
static CURLcode socks_proxy_cf_connect(struct Curl_cfilter *cf,
1164
                                       struct Curl_easy *data,
1165
                                       bool *done)
1166
416
{
1167
416
  struct socks_ctx *ctx = cf->ctx;
1168
416
  CURLproxycode pxresult = CURLPX_OK;
1169
416
  CURLcode result;
1170
1171
416
  if(cf->connected) {
1172
128
    *done = TRUE;
1173
128
    return CURLE_OK;
1174
128
  }
1175
1176
288
  result = cf->next->cft->do_connect(cf->next, data, done);
1177
288
  if(result || !*done)
1178
0
    return result;
1179
1180
288
  switch(ctx->proxy_type) {
1181
288
  case CURLPROXY_SOCKS5:
1182
288
  case CURLPROXY_SOCKS5_HOSTNAME:
1183
288
    pxresult = socks5_connect(cf, ctx, data);
1184
288
    break;
1185
1186
0
  case CURLPROXY_SOCKS4:
1187
0
  case CURLPROXY_SOCKS4A:
1188
0
    pxresult = socks4_connect(cf, ctx, data);
1189
0
    break;
1190
1191
0
  default:
1192
0
    DEBUGASSERT(0); /* should not come here, checked it at creation time */
1193
0
    result = CURLE_COULDNT_CONNECT;
1194
0
    goto out;
1195
288
  }
1196
1197
288
  if(pxresult) {
1198
68
    result = CURLE_PROXY;
1199
68
    data->info.pxcode = (uint8_t)pxresult;
1200
68
    goto out;
1201
68
  }
1202
220
  else if(ctx->state != SOCKS_ST_SUCCESS)
1203
170
    goto out;
1204
1205
50
#ifdef CURLVERBOSE
1206
50
  if(Curl_trc_is_verbose(data)) {
1207
0
    struct ip_quadruple ipquad;
1208
0
    bool is_ipv6;
1209
0
    if(!Curl_conn_cf_get_ip_info(cf->next, data, &is_ipv6, &ipquad))
1210
0
      infof(data, "Opened %sSOCKS connection from %s port %d to %s port %d "
1211
0
            "(via %s port %u)",
1212
0
            (cf->sockindex == SECONDARYSOCKET) ? "2nd " : "",
1213
0
            ipquad.local_ip, ipquad.local_port,
1214
0
            ctx->dest->hostname, ctx->dest->port,
1215
0
            ipquad.remote_ip, ipquad.remote_port);
1216
0
    else
1217
0
      infof(data, "Opened %sSOCKS connection",
1218
0
            (cf->sockindex == SECONDARYSOCKET) ? "2nd " : "");
1219
0
  }
1220
50
#endif
1221
50
  cf->connected = TRUE;
1222
1223
288
out:
1224
288
  *done = (bool)cf->connected;
1225
288
  if(*done || result)
1226
118
    Curl_creds_unlink(&ctx->creds);
1227
288
  return result;
1228
50
}
1229
1230
static CURLcode socks_cf_adjust_pollset(struct Curl_cfilter *cf,
1231
                                        struct Curl_easy *data,
1232
                                        struct easy_pollset *ps)
1233
273
{
1234
273
  struct socks_ctx *sx = cf->ctx;
1235
273
  CURLcode result = CURLE_OK;
1236
1237
273
  if(!cf->connected && sx) {
1238
    /* If we are not connected, the filter below is and has nothing
1239
     * to wait on, we determine what to wait for. */
1240
170
    curl_socket_t sock = Curl_conn_cf_get_socket(cf, data);
1241
170
    switch(sx->state) {
1242
0
    case SOCKS4_ST_SEND:
1243
0
    case SOCKS5_ST_REQ0_SEND:
1244
0
    case SOCKS5_ST_AUTH_SEND:
1245
0
    case SOCKS5_ST_REQ1_SEND:
1246
0
      CURL_TRC_CF(data, cf, "adjust pollset out (%d)", (int)sx->state);
1247
0
      result = Curl_pollset_set_out_only(data, ps, sock);
1248
0
      break;
1249
170
    default:
1250
170
      CURL_TRC_CF(data, cf, "adjust pollset in (%d)", (int)sx->state);
1251
170
      result = Curl_pollset_set_in_only(data, ps, sock);
1252
170
      break;
1253
170
    }
1254
170
  }
1255
273
  return result;
1256
273
}
1257
1258
static void socks_proxy_cf_destroy(struct Curl_cfilter *cf,
1259
                                   struct Curl_easy *data)
1260
136
{
1261
136
  (void)data;
1262
136
  socks_proxy_ctx_free(cf->ctx);
1263
136
  cf->ctx = NULL;
1264
136
}
1265
1266
static CURLcode socks_cf_query(struct Curl_cfilter *cf,
1267
                               struct Curl_easy *data,
1268
                               int query, int *pres1, void *pres2)
1269
746
{
1270
746
  struct socks_ctx *sx = cf->ctx;
1271
1272
746
  switch(query) {
1273
0
  case CF_QUERY_HOST_PORT:
1274
0
    if(sx) {
1275
0
      *pres1 = sx->dest->port;
1276
0
      *((const char **)pres2) = sx->dest->hostname;
1277
0
      return CURLE_OK;
1278
0
    }
1279
0
    break;
1280
37
  case CF_QUERY_ALPN_NEGOTIATED: {
1281
37
    const char **palpn = pres2;
1282
37
    DEBUGASSERT(palpn);
1283
37
    *palpn = NULL;
1284
37
    return CURLE_OK;
1285
37
  }
1286
709
  default:
1287
709
    break;
1288
746
  }
1289
709
  return cf->next ?
1290
709
    cf->next->cft->query(cf->next, data, query, pres1, pres2) :
1291
709
    CURLE_UNKNOWN_OPTION;
1292
746
}
1293
1294
struct Curl_cftype Curl_cft_socks_proxy = {
1295
  "SOCKS",
1296
  CF_TYPE_IP_CONNECT | CF_TYPE_PROXY,
1297
  0,
1298
  socks_proxy_cf_destroy,
1299
  socks_proxy_cf_connect,
1300
  Curl_cf_def_shutdown,
1301
  socks_cf_adjust_pollset,
1302
  Curl_cf_def_data_pending,
1303
  Curl_cf_def_send,
1304
  Curl_cf_def_recv,
1305
  Curl_cf_def_cntrl,
1306
  Curl_cf_def_conn_is_alive,
1307
  Curl_cf_def_conn_keep_alive,
1308
  socks_cf_query,
1309
};
1310
1311
CURLcode Curl_cf_socks_proxy_insert_after(struct Curl_cfilter *cf_at,
1312
                                          struct Curl_easy *data,
1313
                                          struct Curl_peer *dest,
1314
                                          uint8_t ip_version,
1315
                                          uint8_t proxy_type,
1316
                                          struct Curl_creds *creds)
1317
136
{
1318
136
  struct Curl_cfilter *cf;
1319
136
  struct socks_ctx *ctx;
1320
136
  bool resolve_local = FALSE;
1321
136
  uint8_t dns_queries = Curl_resolv_dns_queries(data, ip_version);
1322
136
  CURLcode result;
1323
1324
136
  if(!dest)
1325
0
    return CURLE_FAILED_INIT;
1326
1327
136
  switch(proxy_type) {
1328
136
  case CURLPROXY_SOCKS5:
1329
136
    resolve_local = TRUE;
1330
136
    break;
1331
0
  case CURLPROXY_SOCKS5_HOSTNAME:
1332
0
    break;
1333
0
  case CURLPROXY_SOCKS4:
1334
0
    resolve_local = TRUE;
1335
0
    dns_queries = (uint8_t)(dns_queries & ~CURL_DNSQ_AAAA);
1336
0
    break;
1337
0
  case CURLPROXY_SOCKS4A:
1338
0
    break;
1339
0
  default:
1340
0
    failf(data, "unknown proxytype %d option given", proxy_type);
1341
0
    return CURLE_COULDNT_CONNECT;
1342
136
  }
1343
1344
  /* NUL byte already part of struct size */
1345
136
  ctx = curlx_calloc(1, sizeof(*ctx));
1346
136
  if(!ctx) {
1347
0
    return CURLE_OUT_OF_MEMORY;
1348
0
  }
1349
1350
136
  Curl_peer_link(&ctx->dest, dest);
1351
136
  ctx->ip_version = ip_version;
1352
136
  ctx->proxy_type = proxy_type;
1353
136
  ctx->resolve_local = resolve_local;
1354
136
  Curl_creds_link(&ctx->creds, creds);
1355
136
  Curl_bufq_init2(&ctx->iobuf, SOCKS_CHUNK_SIZE, SOCKS_CHUNKS,
1356
136
                  BUFQ_OPT_SOFT_LIMIT);
1357
1358
136
  result = Curl_cf_create(&cf, &Curl_cft_socks_proxy, ctx);
1359
136
  if(!result) {
1360
136
    Curl_conn_cf_insert_after(cf_at, cf);
1361
136
    if(ctx->resolve_local) {
1362
136
      result = Curl_conn_dns_add_addr_resolve(data, cf_at->conn,
1363
136
                                              cf_at->sockindex,
1364
136
                                              ctx->dest, dns_queries,
1365
136
                                              TRNSPRT_TCP);
1366
136
    }
1367
136
  }
1368
0
  else
1369
0
    socks_proxy_ctx_free(ctx);
1370
136
  return result;
1371
136
}
1372
1373
#endif /* CURL_DISABLE_PROXY */