Coverage Report

Created: 2025-08-24 06:12

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