Coverage Report

Created: 2025-10-30 06:17

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