Coverage Report

Created: 2026-02-26 06:33

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