Coverage Report

Created: 2026-06-15 07:03

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