113498266Sopenharmony_ci/***************************************************************************
213498266Sopenharmony_ci *                                  _   _ ____  _
313498266Sopenharmony_ci *  Project                     ___| | | |  _ \| |
413498266Sopenharmony_ci *                             / __| | | | |_) | |
513498266Sopenharmony_ci *                            | (__| |_| |  _ <| |___
613498266Sopenharmony_ci *                             \___|\___/|_| \_\_____|
713498266Sopenharmony_ci *
813498266Sopenharmony_ci * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
913498266Sopenharmony_ci *
1013498266Sopenharmony_ci * This software is licensed as described in the file COPYING, which
1113498266Sopenharmony_ci * you should have received as part of this distribution. The terms
1213498266Sopenharmony_ci * are also available at https://curl.se/docs/copyright.html.
1313498266Sopenharmony_ci *
1413498266Sopenharmony_ci * You may opt to use, copy, modify, merge, publish, distribute and/or sell
1513498266Sopenharmony_ci * copies of the Software, and permit persons to whom the Software is
1613498266Sopenharmony_ci * furnished to do so, under the terms of the COPYING file.
1713498266Sopenharmony_ci *
1813498266Sopenharmony_ci * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
1913498266Sopenharmony_ci * KIND, either express or implied.
2013498266Sopenharmony_ci *
2113498266Sopenharmony_ci * SPDX-License-Identifier: curl
2213498266Sopenharmony_ci *
2313498266Sopenharmony_ci ***************************************************************************/
2413498266Sopenharmony_ci
2513498266Sopenharmony_ci#include "curl_setup.h"
2613498266Sopenharmony_ci
2713498266Sopenharmony_ci#if !defined(CURL_DISABLE_PROXY)
2813498266Sopenharmony_ci
2913498266Sopenharmony_ci#ifdef HAVE_NETINET_IN_H
3013498266Sopenharmony_ci#include <netinet/in.h>
3113498266Sopenharmony_ci#endif
3213498266Sopenharmony_ci#ifdef HAVE_ARPA_INET_H
3313498266Sopenharmony_ci#include <arpa/inet.h>
3413498266Sopenharmony_ci#endif
3513498266Sopenharmony_ci
3613498266Sopenharmony_ci#include "urldata.h"
3713498266Sopenharmony_ci#include "sendf.h"
3813498266Sopenharmony_ci#include "select.h"
3913498266Sopenharmony_ci#include "cfilters.h"
4013498266Sopenharmony_ci#include "connect.h"
4113498266Sopenharmony_ci#include "timeval.h"
4213498266Sopenharmony_ci#include "socks.h"
4313498266Sopenharmony_ci#include "multiif.h" /* for getsock macros */
4413498266Sopenharmony_ci#include "inet_pton.h"
4513498266Sopenharmony_ci#include "url.h"
4613498266Sopenharmony_ci
4713498266Sopenharmony_ci/* The last 3 #include files should be in this order */
4813498266Sopenharmony_ci#include "curl_printf.h"
4913498266Sopenharmony_ci#include "curl_memory.h"
5013498266Sopenharmony_ci#include "memdebug.h"
5113498266Sopenharmony_ci
5213498266Sopenharmony_ci/* for the (SOCKS) connect state machine */
5313498266Sopenharmony_cienum connect_t {
5413498266Sopenharmony_ci  CONNECT_INIT,
5513498266Sopenharmony_ci  CONNECT_SOCKS_INIT, /* 1 */
5613498266Sopenharmony_ci  CONNECT_SOCKS_SEND, /* 2 waiting to send more first data */
5713498266Sopenharmony_ci  CONNECT_SOCKS_READ_INIT, /* 3 set up read */
5813498266Sopenharmony_ci  CONNECT_SOCKS_READ, /* 4 read server response */
5913498266Sopenharmony_ci  CONNECT_GSSAPI_INIT, /* 5 */
6013498266Sopenharmony_ci  CONNECT_AUTH_INIT, /* 6 setup outgoing auth buffer */
6113498266Sopenharmony_ci  CONNECT_AUTH_SEND, /* 7 send auth */
6213498266Sopenharmony_ci  CONNECT_AUTH_READ, /* 8 read auth response */
6313498266Sopenharmony_ci  CONNECT_REQ_INIT,  /* 9 init SOCKS "request" */
6413498266Sopenharmony_ci  CONNECT_RESOLVING, /* 10 */
6513498266Sopenharmony_ci  CONNECT_RESOLVED,  /* 11 */
6613498266Sopenharmony_ci  CONNECT_RESOLVE_REMOTE, /* 12 */
6713498266Sopenharmony_ci  CONNECT_REQ_SEND,  /* 13 */
6813498266Sopenharmony_ci  CONNECT_REQ_SENDING, /* 14 */
6913498266Sopenharmony_ci  CONNECT_REQ_READ,  /* 15 */
7013498266Sopenharmony_ci  CONNECT_REQ_READ_MORE, /* 16 */
7113498266Sopenharmony_ci  CONNECT_DONE /* 17 connected fine to the remote or the SOCKS proxy */
7213498266Sopenharmony_ci};
7313498266Sopenharmony_ci
7413498266Sopenharmony_ci#define CURL_SOCKS_BUF_SIZE 600
7513498266Sopenharmony_ci
7613498266Sopenharmony_ci/* make sure we configure it not too low */
7713498266Sopenharmony_ci#if CURL_SOCKS_BUF_SIZE < 600
7813498266Sopenharmony_ci#error CURL_SOCKS_BUF_SIZE must be at least 600
7913498266Sopenharmony_ci#endif
8013498266Sopenharmony_ci
8113498266Sopenharmony_ci
8213498266Sopenharmony_cistruct socks_state {
8313498266Sopenharmony_ci  enum connect_t state;
8413498266Sopenharmony_ci  ssize_t outstanding;  /* send this many bytes more */
8513498266Sopenharmony_ci  unsigned char buffer[CURL_SOCKS_BUF_SIZE];
8613498266Sopenharmony_ci  unsigned char *outp; /* send from this pointer */
8713498266Sopenharmony_ci
8813498266Sopenharmony_ci  const char *hostname;
8913498266Sopenharmony_ci  int remote_port;
9013498266Sopenharmony_ci  const char *proxy_user;
9113498266Sopenharmony_ci  const char *proxy_password;
9213498266Sopenharmony_ci};
9313498266Sopenharmony_ci
9413498266Sopenharmony_ci#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
9513498266Sopenharmony_ci/*
9613498266Sopenharmony_ci * Helper read-from-socket functions. Does the same as Curl_read() but it
9713498266Sopenharmony_ci * blocks until all bytes amount of buffersize will be read. No more, no less.
9813498266Sopenharmony_ci *
9913498266Sopenharmony_ci * This is STUPID BLOCKING behavior. Only used by the SOCKS GSSAPI functions.
10013498266Sopenharmony_ci */
10113498266Sopenharmony_ciint Curl_blockread_all(struct Curl_cfilter *cf,
10213498266Sopenharmony_ci                       struct Curl_easy *data,   /* transfer */
10313498266Sopenharmony_ci                       char *buf,                /* store read data here */
10413498266Sopenharmony_ci                       ssize_t buffersize,       /* max amount to read */
10513498266Sopenharmony_ci                       ssize_t *n)               /* amount bytes read */
10613498266Sopenharmony_ci{
10713498266Sopenharmony_ci  ssize_t nread = 0;
10813498266Sopenharmony_ci  ssize_t allread = 0;
10913498266Sopenharmony_ci  int result;
11013498266Sopenharmony_ci  CURLcode err = CURLE_OK;
11113498266Sopenharmony_ci
11213498266Sopenharmony_ci  *n = 0;
11313498266Sopenharmony_ci  for(;;) {
11413498266Sopenharmony_ci    timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
11513498266Sopenharmony_ci    if(timeout_ms < 0) {
11613498266Sopenharmony_ci      /* we already got the timeout */
11713498266Sopenharmony_ci      result = CURLE_OPERATION_TIMEDOUT;
11813498266Sopenharmony_ci      break;
11913498266Sopenharmony_ci    }
12013498266Sopenharmony_ci    if(!timeout_ms)
12113498266Sopenharmony_ci      timeout_ms = TIMEDIFF_T_MAX;
12213498266Sopenharmony_ci    if(SOCKET_READABLE(cf->conn->sock[cf->sockindex], timeout_ms) <= 0) {
12313498266Sopenharmony_ci      result = ~CURLE_OK;
12413498266Sopenharmony_ci      break;
12513498266Sopenharmony_ci    }
12613498266Sopenharmony_ci    nread = Curl_conn_cf_recv(cf->next, data, buf, buffersize, &err);
12713498266Sopenharmony_ci    if(nread <= 0) {
12813498266Sopenharmony_ci      result = err;
12913498266Sopenharmony_ci      if(CURLE_AGAIN == err)
13013498266Sopenharmony_ci        continue;
13113498266Sopenharmony_ci      if(err) {
13213498266Sopenharmony_ci        break;
13313498266Sopenharmony_ci      }
13413498266Sopenharmony_ci    }
13513498266Sopenharmony_ci
13613498266Sopenharmony_ci    if(buffersize == nread) {
13713498266Sopenharmony_ci      allread += nread;
13813498266Sopenharmony_ci      *n = allread;
13913498266Sopenharmony_ci      result = CURLE_OK;
14013498266Sopenharmony_ci      break;
14113498266Sopenharmony_ci    }
14213498266Sopenharmony_ci    if(!nread) {
14313498266Sopenharmony_ci      result = ~CURLE_OK;
14413498266Sopenharmony_ci      break;
14513498266Sopenharmony_ci    }
14613498266Sopenharmony_ci
14713498266Sopenharmony_ci    buffersize -= nread;
14813498266Sopenharmony_ci    buf += nread;
14913498266Sopenharmony_ci    allread += nread;
15013498266Sopenharmony_ci  }
15113498266Sopenharmony_ci  return result;
15213498266Sopenharmony_ci}
15313498266Sopenharmony_ci#endif
15413498266Sopenharmony_ci
15513498266Sopenharmony_ci#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
15613498266Sopenharmony_ci#define DEBUG_AND_VERBOSE
15713498266Sopenharmony_ci#define sxstate(x,d,y) socksstate(x,d,y, __LINE__)
15813498266Sopenharmony_ci#else
15913498266Sopenharmony_ci#define sxstate(x,d,y) socksstate(x,d,y)
16013498266Sopenharmony_ci#endif
16113498266Sopenharmony_ci
16213498266Sopenharmony_ci/* always use this function to change state, to make debugging easier */
16313498266Sopenharmony_cistatic void socksstate(struct socks_state *sx, struct Curl_easy *data,
16413498266Sopenharmony_ci                       enum connect_t state
16513498266Sopenharmony_ci#ifdef DEBUG_AND_VERBOSE
16613498266Sopenharmony_ci                       , int lineno
16713498266Sopenharmony_ci#endif
16813498266Sopenharmony_ci)
16913498266Sopenharmony_ci{
17013498266Sopenharmony_ci  enum connect_t oldstate = sx->state;
17113498266Sopenharmony_ci#ifdef DEBUG_AND_VERBOSE
17213498266Sopenharmony_ci  /* synced with the state list in urldata.h */
17313498266Sopenharmony_ci  static const char * const socks_statename[] = {
17413498266Sopenharmony_ci    "INIT",
17513498266Sopenharmony_ci    "SOCKS_INIT",
17613498266Sopenharmony_ci    "SOCKS_SEND",
17713498266Sopenharmony_ci    "SOCKS_READ_INIT",
17813498266Sopenharmony_ci    "SOCKS_READ",
17913498266Sopenharmony_ci    "GSSAPI_INIT",
18013498266Sopenharmony_ci    "AUTH_INIT",
18113498266Sopenharmony_ci    "AUTH_SEND",
18213498266Sopenharmony_ci    "AUTH_READ",
18313498266Sopenharmony_ci    "REQ_INIT",
18413498266Sopenharmony_ci    "RESOLVING",
18513498266Sopenharmony_ci    "RESOLVED",
18613498266Sopenharmony_ci    "RESOLVE_REMOTE",
18713498266Sopenharmony_ci    "REQ_SEND",
18813498266Sopenharmony_ci    "REQ_SENDING",
18913498266Sopenharmony_ci    "REQ_READ",
19013498266Sopenharmony_ci    "REQ_READ_MORE",
19113498266Sopenharmony_ci    "DONE"
19213498266Sopenharmony_ci  };
19313498266Sopenharmony_ci#endif
19413498266Sopenharmony_ci
19513498266Sopenharmony_ci  (void)data;
19613498266Sopenharmony_ci  if(oldstate == state)
19713498266Sopenharmony_ci    /* don't bother when the new state is the same as the old state */
19813498266Sopenharmony_ci    return;
19913498266Sopenharmony_ci
20013498266Sopenharmony_ci  sx->state = state;
20113498266Sopenharmony_ci
20213498266Sopenharmony_ci#ifdef DEBUG_AND_VERBOSE
20313498266Sopenharmony_ci  infof(data,
20413498266Sopenharmony_ci        "SXSTATE: %s => %s; line %d",
20513498266Sopenharmony_ci        socks_statename[oldstate], socks_statename[sx->state],
20613498266Sopenharmony_ci        lineno);
20713498266Sopenharmony_ci#endif
20813498266Sopenharmony_ci}
20913498266Sopenharmony_ci
21013498266Sopenharmony_cistatic CURLproxycode socks_state_send(struct Curl_cfilter *cf,
21113498266Sopenharmony_ci                                      struct socks_state *sx,
21213498266Sopenharmony_ci                                      struct Curl_easy *data,
21313498266Sopenharmony_ci                                      CURLproxycode failcode,
21413498266Sopenharmony_ci                                      const char *description)
21513498266Sopenharmony_ci{
21613498266Sopenharmony_ci  ssize_t nwritten;
21713498266Sopenharmony_ci  CURLcode result;
21813498266Sopenharmony_ci
21913498266Sopenharmony_ci  nwritten = Curl_conn_cf_send(cf->next, data, (char *)sx->outp,
22013498266Sopenharmony_ci                               sx->outstanding, &result);
22113498266Sopenharmony_ci  if(nwritten <= 0) {
22213498266Sopenharmony_ci    if(CURLE_AGAIN == result) {
22313498266Sopenharmony_ci      return CURLPX_OK;
22413498266Sopenharmony_ci    }
22513498266Sopenharmony_ci    else if(CURLE_OK == result) {
22613498266Sopenharmony_ci      /* connection closed */
22713498266Sopenharmony_ci      failf(data, "connection to proxy closed");
22813498266Sopenharmony_ci      return CURLPX_CLOSED;
22913498266Sopenharmony_ci    }
23013498266Sopenharmony_ci    failf(data, "Failed to send %s: %s", description,
23113498266Sopenharmony_ci          curl_easy_strerror(result));
23213498266Sopenharmony_ci    return failcode;
23313498266Sopenharmony_ci  }
23413498266Sopenharmony_ci  DEBUGASSERT(sx->outstanding >= nwritten);
23513498266Sopenharmony_ci  /* not done, remain in state */
23613498266Sopenharmony_ci  sx->outstanding -= nwritten;
23713498266Sopenharmony_ci  sx->outp += nwritten;
23813498266Sopenharmony_ci  return CURLPX_OK;
23913498266Sopenharmony_ci}
24013498266Sopenharmony_ci
24113498266Sopenharmony_cistatic CURLproxycode socks_state_recv(struct Curl_cfilter *cf,
24213498266Sopenharmony_ci                                      struct socks_state *sx,
24313498266Sopenharmony_ci                                      struct Curl_easy *data,
24413498266Sopenharmony_ci                                      CURLproxycode failcode,
24513498266Sopenharmony_ci                                      const char *description)
24613498266Sopenharmony_ci{
24713498266Sopenharmony_ci  ssize_t nread;
24813498266Sopenharmony_ci  CURLcode result;
24913498266Sopenharmony_ci
25013498266Sopenharmony_ci  nread = Curl_conn_cf_recv(cf->next, data, (char *)sx->outp,
25113498266Sopenharmony_ci                            sx->outstanding, &result);
25213498266Sopenharmony_ci  if(nread <= 0) {
25313498266Sopenharmony_ci    if(CURLE_AGAIN == result) {
25413498266Sopenharmony_ci      return CURLPX_OK;
25513498266Sopenharmony_ci    }
25613498266Sopenharmony_ci    else if(CURLE_OK == result) {
25713498266Sopenharmony_ci      /* connection closed */
25813498266Sopenharmony_ci      failf(data, "connection to proxy closed");
25913498266Sopenharmony_ci      return CURLPX_CLOSED;
26013498266Sopenharmony_ci    }
26113498266Sopenharmony_ci    failf(data, "SOCKS: Failed receiving %s: %s", description,
26213498266Sopenharmony_ci          curl_easy_strerror(result));
26313498266Sopenharmony_ci    return failcode;
26413498266Sopenharmony_ci  }
26513498266Sopenharmony_ci  /* remain in reading state */
26613498266Sopenharmony_ci  DEBUGASSERT(sx->outstanding >= nread);
26713498266Sopenharmony_ci  sx->outstanding -= nread;
26813498266Sopenharmony_ci  sx->outp += nread;
26913498266Sopenharmony_ci  return CURLPX_OK;
27013498266Sopenharmony_ci}
27113498266Sopenharmony_ci
27213498266Sopenharmony_ci/*
27313498266Sopenharmony_ci* This function logs in to a SOCKS4 proxy and sends the specifics to the final
27413498266Sopenharmony_ci* destination server.
27513498266Sopenharmony_ci*
27613498266Sopenharmony_ci* Reference :
27713498266Sopenharmony_ci*   https://www.openssh.com/txt/socks4.protocol
27813498266Sopenharmony_ci*
27913498266Sopenharmony_ci* Note :
28013498266Sopenharmony_ci*   Set protocol4a=true for  "SOCKS 4A (Simple Extension to SOCKS 4 Protocol)"
28113498266Sopenharmony_ci*   Nonsupport "Identification Protocol (RFC1413)"
28213498266Sopenharmony_ci*/
28313498266Sopenharmony_cistatic CURLproxycode do_SOCKS4(struct Curl_cfilter *cf,
28413498266Sopenharmony_ci                               struct socks_state *sx,
28513498266Sopenharmony_ci                               struct Curl_easy *data)
28613498266Sopenharmony_ci{
28713498266Sopenharmony_ci  struct connectdata *conn = cf->conn;
28813498266Sopenharmony_ci  const bool protocol4a =
28913498266Sopenharmony_ci    (conn->socks_proxy.proxytype == CURLPROXY_SOCKS4A) ? TRUE : FALSE;
29013498266Sopenharmony_ci  unsigned char *socksreq = sx->buffer;
29113498266Sopenharmony_ci  CURLcode result;
29213498266Sopenharmony_ci  CURLproxycode presult;
29313498266Sopenharmony_ci  struct Curl_dns_entry *dns = NULL;
29413498266Sopenharmony_ci
29513498266Sopenharmony_ci  switch(sx->state) {
29613498266Sopenharmony_ci  case CONNECT_SOCKS_INIT:
29713498266Sopenharmony_ci    /* SOCKS4 can only do IPv4, insist! */
29813498266Sopenharmony_ci    conn->ip_version = CURL_IPRESOLVE_V4;
29913498266Sopenharmony_ci    if(conn->bits.httpproxy)
30013498266Sopenharmony_ci      infof(data, "SOCKS4%s: connecting to HTTP proxy %s port %d",
30113498266Sopenharmony_ci            protocol4a ? "a" : "", sx->hostname, sx->remote_port);
30213498266Sopenharmony_ci
30313498266Sopenharmony_ci    infof(data, "SOCKS4 communication to %s:%d",
30413498266Sopenharmony_ci          sx->hostname, sx->remote_port);
30513498266Sopenharmony_ci
30613498266Sopenharmony_ci    /*
30713498266Sopenharmony_ci     * Compose socks4 request
30813498266Sopenharmony_ci     *
30913498266Sopenharmony_ci     * Request format
31013498266Sopenharmony_ci     *
31113498266Sopenharmony_ci     *     +----+----+----+----+----+----+----+----+----+----+....+----+
31213498266Sopenharmony_ci     *     | VN | CD | DSTPORT |      DSTIP        | USERID       |NULL|
31313498266Sopenharmony_ci     *     +----+----+----+----+----+----+----+----+----+----+....+----+
31413498266Sopenharmony_ci     * # of bytes:  1    1      2              4           variable       1
31513498266Sopenharmony_ci     */
31613498266Sopenharmony_ci
31713498266Sopenharmony_ci    socksreq[0] = 4; /* version (SOCKS4) */
31813498266Sopenharmony_ci    socksreq[1] = 1; /* connect */
31913498266Sopenharmony_ci    socksreq[2] = (unsigned char)((sx->remote_port >> 8) & 0xff); /* MSB */
32013498266Sopenharmony_ci    socksreq[3] = (unsigned char)(sx->remote_port & 0xff);        /* LSB */
32113498266Sopenharmony_ci
32213498266Sopenharmony_ci    /* DNS resolve only for SOCKS4, not SOCKS4a */
32313498266Sopenharmony_ci    if(!protocol4a) {
32413498266Sopenharmony_ci      enum resolve_t rc =
32513498266Sopenharmony_ci        Curl_resolv(data, sx->hostname, sx->remote_port, TRUE, &dns);
32613498266Sopenharmony_ci
32713498266Sopenharmony_ci      if(rc == CURLRESOLV_ERROR)
32813498266Sopenharmony_ci        return CURLPX_RESOLVE_HOST;
32913498266Sopenharmony_ci      else if(rc == CURLRESOLV_PENDING) {
33013498266Sopenharmony_ci        sxstate(sx, data, CONNECT_RESOLVING);
33113498266Sopenharmony_ci        infof(data, "SOCKS4 non-blocking resolve of %s", sx->hostname);
33213498266Sopenharmony_ci        return CURLPX_OK;
33313498266Sopenharmony_ci      }
33413498266Sopenharmony_ci      sxstate(sx, data, CONNECT_RESOLVED);
33513498266Sopenharmony_ci      goto CONNECT_RESOLVED;
33613498266Sopenharmony_ci    }
33713498266Sopenharmony_ci
33813498266Sopenharmony_ci    /* socks4a doesn't resolve anything locally */
33913498266Sopenharmony_ci    sxstate(sx, data, CONNECT_REQ_INIT);
34013498266Sopenharmony_ci    goto CONNECT_REQ_INIT;
34113498266Sopenharmony_ci
34213498266Sopenharmony_ci  case CONNECT_RESOLVING:
34313498266Sopenharmony_ci    /* check if we have the name resolved by now */
34413498266Sopenharmony_ci    dns = Curl_fetch_addr(data, sx->hostname, (int)conn->port);
34513498266Sopenharmony_ci
34613498266Sopenharmony_ci    if(dns) {
34713498266Sopenharmony_ci#ifdef CURLRES_ASYNCH
34813498266Sopenharmony_ci      data->state.async.dns = dns;
34913498266Sopenharmony_ci      data->state.async.done = TRUE;
35013498266Sopenharmony_ci#endif
35113498266Sopenharmony_ci      infof(data, "Hostname '%s' was found", sx->hostname);
35213498266Sopenharmony_ci      sxstate(sx, data, CONNECT_RESOLVED);
35313498266Sopenharmony_ci    }
35413498266Sopenharmony_ci    else {
35513498266Sopenharmony_ci      result = Curl_resolv_check(data, &dns);
35613498266Sopenharmony_ci      if(!dns) {
35713498266Sopenharmony_ci        if(result)
35813498266Sopenharmony_ci          return CURLPX_RESOLVE_HOST;
35913498266Sopenharmony_ci        return CURLPX_OK;
36013498266Sopenharmony_ci      }
36113498266Sopenharmony_ci    }
36213498266Sopenharmony_ci    FALLTHROUGH();
36313498266Sopenharmony_ci  case CONNECT_RESOLVED:
36413498266Sopenharmony_ciCONNECT_RESOLVED:
36513498266Sopenharmony_ci  {
36613498266Sopenharmony_ci    struct Curl_addrinfo *hp = NULL;
36713498266Sopenharmony_ci    /*
36813498266Sopenharmony_ci     * We cannot use 'hostent' as a struct that Curl_resolv() returns.  It
36913498266Sopenharmony_ci     * returns a Curl_addrinfo pointer that may not always look the same.
37013498266Sopenharmony_ci     */
37113498266Sopenharmony_ci    if(dns) {
37213498266Sopenharmony_ci      hp = dns->addr;
37313498266Sopenharmony_ci
37413498266Sopenharmony_ci      /* scan for the first IPv4 address */
37513498266Sopenharmony_ci      while(hp && (hp->ai_family != AF_INET))
37613498266Sopenharmony_ci        hp = hp->ai_next;
37713498266Sopenharmony_ci
37813498266Sopenharmony_ci      if(hp) {
37913498266Sopenharmony_ci        struct sockaddr_in *saddr_in;
38013498266Sopenharmony_ci        char buf[64];
38113498266Sopenharmony_ci        Curl_printable_address(hp, buf, sizeof(buf));
38213498266Sopenharmony_ci
38313498266Sopenharmony_ci        saddr_in = (struct sockaddr_in *)(void *)hp->ai_addr;
38413498266Sopenharmony_ci        socksreq[4] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[0];
38513498266Sopenharmony_ci        socksreq[5] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[1];
38613498266Sopenharmony_ci        socksreq[6] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[2];
38713498266Sopenharmony_ci        socksreq[7] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[3];
38813498266Sopenharmony_ci
38913498266Sopenharmony_ci        infof(data, "SOCKS4 connect to IPv4 %s (locally resolved)", buf);
39013498266Sopenharmony_ci
39113498266Sopenharmony_ci        Curl_resolv_unlock(data, dns); /* not used anymore from now on */
39213498266Sopenharmony_ci      }
39313498266Sopenharmony_ci      else
39413498266Sopenharmony_ci        failf(data, "SOCKS4 connection to %s not supported", sx->hostname);
39513498266Sopenharmony_ci    }
39613498266Sopenharmony_ci    else
39713498266Sopenharmony_ci      failf(data, "Failed to resolve \"%s\" for SOCKS4 connect.",
39813498266Sopenharmony_ci            sx->hostname);
39913498266Sopenharmony_ci
40013498266Sopenharmony_ci    if(!hp)
40113498266Sopenharmony_ci      return CURLPX_RESOLVE_HOST;
40213498266Sopenharmony_ci  }
40313498266Sopenharmony_ci    FALLTHROUGH();
40413498266Sopenharmony_ci  case CONNECT_REQ_INIT:
40513498266Sopenharmony_ciCONNECT_REQ_INIT:
40613498266Sopenharmony_ci    /*
40713498266Sopenharmony_ci     * This is currently not supporting "Identification Protocol (RFC1413)".
40813498266Sopenharmony_ci     */
40913498266Sopenharmony_ci    socksreq[8] = 0; /* ensure empty userid is NUL-terminated */
41013498266Sopenharmony_ci    if(sx->proxy_user) {
41113498266Sopenharmony_ci      size_t plen = strlen(sx->proxy_user);
41213498266Sopenharmony_ci      if(plen > 255) {
41313498266Sopenharmony_ci        /* there is no real size limit to this field in the protocol, but
41413498266Sopenharmony_ci           SOCKS5 limits the proxy user field to 255 bytes and it seems likely
41513498266Sopenharmony_ci           that a longer field is either a mistake or malicious input */
41613498266Sopenharmony_ci        failf(data, "Too long SOCKS proxy user name");
41713498266Sopenharmony_ci        return CURLPX_LONG_USER;
41813498266Sopenharmony_ci      }
41913498266Sopenharmony_ci      /* copy the proxy name WITH trailing zero */
42013498266Sopenharmony_ci      memcpy(socksreq + 8, sx->proxy_user, plen + 1);
42113498266Sopenharmony_ci    }
42213498266Sopenharmony_ci
42313498266Sopenharmony_ci    /*
42413498266Sopenharmony_ci     * Make connection
42513498266Sopenharmony_ci     */
42613498266Sopenharmony_ci    {
42713498266Sopenharmony_ci      size_t packetsize = 9 +
42813498266Sopenharmony_ci        strlen((char *)socksreq + 8); /* size including NUL */
42913498266Sopenharmony_ci
43013498266Sopenharmony_ci      /* If SOCKS4a, set special invalid IP address 0.0.0.x */
43113498266Sopenharmony_ci      if(protocol4a) {
43213498266Sopenharmony_ci        size_t hostnamelen = 0;
43313498266Sopenharmony_ci        socksreq[4] = 0;
43413498266Sopenharmony_ci        socksreq[5] = 0;
43513498266Sopenharmony_ci        socksreq[6] = 0;
43613498266Sopenharmony_ci        socksreq[7] = 1;
43713498266Sopenharmony_ci        /* append hostname */
43813498266Sopenharmony_ci        hostnamelen = strlen(sx->hostname) + 1; /* length including NUL */
43913498266Sopenharmony_ci        if((hostnamelen <= 255) &&
44013498266Sopenharmony_ci           (packetsize + hostnamelen < sizeof(sx->buffer)))
44113498266Sopenharmony_ci          strcpy((char *)socksreq + packetsize, sx->hostname);
44213498266Sopenharmony_ci        else {
44313498266Sopenharmony_ci          failf(data, "SOCKS4: too long host name");
44413498266Sopenharmony_ci          return CURLPX_LONG_HOSTNAME;
44513498266Sopenharmony_ci        }
44613498266Sopenharmony_ci        packetsize += hostnamelen;
44713498266Sopenharmony_ci      }
44813498266Sopenharmony_ci      sx->outp = socksreq;
44913498266Sopenharmony_ci      DEBUGASSERT(packetsize <= sizeof(sx->buffer));
45013498266Sopenharmony_ci      sx->outstanding = packetsize;
45113498266Sopenharmony_ci      sxstate(sx, data, CONNECT_REQ_SENDING);
45213498266Sopenharmony_ci    }
45313498266Sopenharmony_ci    FALLTHROUGH();
45413498266Sopenharmony_ci  case CONNECT_REQ_SENDING:
45513498266Sopenharmony_ci    /* Send request */
45613498266Sopenharmony_ci    presult = socks_state_send(cf, sx, data, CURLPX_SEND_CONNECT,
45713498266Sopenharmony_ci                               "SOCKS4 connect request");
45813498266Sopenharmony_ci    if(CURLPX_OK != presult)
45913498266Sopenharmony_ci      return presult;
46013498266Sopenharmony_ci    else if(sx->outstanding) {
46113498266Sopenharmony_ci      /* remain in sending state */
46213498266Sopenharmony_ci      return CURLPX_OK;
46313498266Sopenharmony_ci    }
46413498266Sopenharmony_ci    /* done sending! */
46513498266Sopenharmony_ci    sx->outstanding = 8; /* receive data size */
46613498266Sopenharmony_ci    sx->outp = socksreq;
46713498266Sopenharmony_ci    sxstate(sx, data, CONNECT_SOCKS_READ);
46813498266Sopenharmony_ci
46913498266Sopenharmony_ci    FALLTHROUGH();
47013498266Sopenharmony_ci  case CONNECT_SOCKS_READ:
47113498266Sopenharmony_ci    /* Receive response */
47213498266Sopenharmony_ci    presult = socks_state_recv(cf, sx, data, CURLPX_RECV_CONNECT,
47313498266Sopenharmony_ci                               "connect request ack");
47413498266Sopenharmony_ci    if(CURLPX_OK != presult)
47513498266Sopenharmony_ci      return presult;
47613498266Sopenharmony_ci    else if(sx->outstanding) {
47713498266Sopenharmony_ci      /* remain in reading state */
47813498266Sopenharmony_ci      return CURLPX_OK;
47913498266Sopenharmony_ci    }
48013498266Sopenharmony_ci    sxstate(sx, data, CONNECT_DONE);
48113498266Sopenharmony_ci    break;
48213498266Sopenharmony_ci  default: /* lots of unused states in SOCKS4 */
48313498266Sopenharmony_ci    break;
48413498266Sopenharmony_ci  }
48513498266Sopenharmony_ci
48613498266Sopenharmony_ci  /*
48713498266Sopenharmony_ci   * Response format
48813498266Sopenharmony_ci   *
48913498266Sopenharmony_ci   *     +----+----+----+----+----+----+----+----+
49013498266Sopenharmony_ci   *     | VN | CD | DSTPORT |      DSTIP        |
49113498266Sopenharmony_ci   *     +----+----+----+----+----+----+----+----+
49213498266Sopenharmony_ci   * # of bytes:  1    1      2              4
49313498266Sopenharmony_ci   *
49413498266Sopenharmony_ci   * VN is the version of the reply code and should be 0. CD is the result
49513498266Sopenharmony_ci   * code with one of the following values:
49613498266Sopenharmony_ci   *
49713498266Sopenharmony_ci   * 90: request granted
49813498266Sopenharmony_ci   * 91: request rejected or failed
49913498266Sopenharmony_ci   * 92: request rejected because SOCKS server cannot connect to
50013498266Sopenharmony_ci   *     identd on the client
50113498266Sopenharmony_ci   * 93: request rejected because the client program and identd
50213498266Sopenharmony_ci   *     report different user-ids
50313498266Sopenharmony_ci   */
50413498266Sopenharmony_ci
50513498266Sopenharmony_ci  /* wrong version ? */
50613498266Sopenharmony_ci  if(socksreq[0]) {
50713498266Sopenharmony_ci    failf(data,
50813498266Sopenharmony_ci          "SOCKS4 reply has wrong version, version should be 0.");
50913498266Sopenharmony_ci    return CURLPX_BAD_VERSION;
51013498266Sopenharmony_ci  }
51113498266Sopenharmony_ci
51213498266Sopenharmony_ci  /* Result */
51313498266Sopenharmony_ci  switch(socksreq[1]) {
51413498266Sopenharmony_ci  case 90:
51513498266Sopenharmony_ci    infof(data, "SOCKS4%s request granted.", protocol4a?"a":"");
51613498266Sopenharmony_ci    break;
51713498266Sopenharmony_ci  case 91:
51813498266Sopenharmony_ci    failf(data,
51913498266Sopenharmony_ci          "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
52013498266Sopenharmony_ci          ", request rejected or failed.",
52113498266Sopenharmony_ci          socksreq[4], socksreq[5], socksreq[6], socksreq[7],
52213498266Sopenharmony_ci          (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]),
52313498266Sopenharmony_ci          (unsigned char)socksreq[1]);
52413498266Sopenharmony_ci    return CURLPX_REQUEST_FAILED;
52513498266Sopenharmony_ci  case 92:
52613498266Sopenharmony_ci    failf(data,
52713498266Sopenharmony_ci          "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
52813498266Sopenharmony_ci          ", request rejected because SOCKS server cannot connect to "
52913498266Sopenharmony_ci          "identd on the client.",
53013498266Sopenharmony_ci          socksreq[4], socksreq[5], socksreq[6], socksreq[7],
53113498266Sopenharmony_ci          (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]),
53213498266Sopenharmony_ci          (unsigned char)socksreq[1]);
53313498266Sopenharmony_ci    return CURLPX_IDENTD;
53413498266Sopenharmony_ci  case 93:
53513498266Sopenharmony_ci    failf(data,
53613498266Sopenharmony_ci          "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
53713498266Sopenharmony_ci          ", request rejected because the client program and identd "
53813498266Sopenharmony_ci          "report different user-ids.",
53913498266Sopenharmony_ci          socksreq[4], socksreq[5], socksreq[6], socksreq[7],
54013498266Sopenharmony_ci          (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]),
54113498266Sopenharmony_ci          (unsigned char)socksreq[1]);
54213498266Sopenharmony_ci    return CURLPX_IDENTD_DIFFER;
54313498266Sopenharmony_ci  default:
54413498266Sopenharmony_ci    failf(data,
54513498266Sopenharmony_ci          "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
54613498266Sopenharmony_ci          ", Unknown.",
54713498266Sopenharmony_ci          socksreq[4], socksreq[5], socksreq[6], socksreq[7],
54813498266Sopenharmony_ci          (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]),
54913498266Sopenharmony_ci          (unsigned char)socksreq[1]);
55013498266Sopenharmony_ci    return CURLPX_UNKNOWN_FAIL;
55113498266Sopenharmony_ci  }
55213498266Sopenharmony_ci
55313498266Sopenharmony_ci  return CURLPX_OK; /* Proxy was successful! */
55413498266Sopenharmony_ci}
55513498266Sopenharmony_ci
55613498266Sopenharmony_ci/*
55713498266Sopenharmony_ci * This function logs in to a SOCKS5 proxy and sends the specifics to the final
55813498266Sopenharmony_ci * destination server.
55913498266Sopenharmony_ci */
56013498266Sopenharmony_cistatic CURLproxycode do_SOCKS5(struct Curl_cfilter *cf,
56113498266Sopenharmony_ci                               struct socks_state *sx,
56213498266Sopenharmony_ci                               struct Curl_easy *data)
56313498266Sopenharmony_ci{
56413498266Sopenharmony_ci  /*
56513498266Sopenharmony_ci    According to the RFC1928, section "6.  Replies". This is what a SOCK5
56613498266Sopenharmony_ci    replies:
56713498266Sopenharmony_ci
56813498266Sopenharmony_ci        +----+-----+-------+------+----------+----------+
56913498266Sopenharmony_ci        |VER | REP |  RSV  | ATYP | BND.ADDR | BND.PORT |
57013498266Sopenharmony_ci        +----+-----+-------+------+----------+----------+
57113498266Sopenharmony_ci        | 1  |  1  | X'00' |  1   | Variable |    2     |
57213498266Sopenharmony_ci        +----+-----+-------+------+----------+----------+
57313498266Sopenharmony_ci
57413498266Sopenharmony_ci    Where:
57513498266Sopenharmony_ci
57613498266Sopenharmony_ci    o  VER    protocol version: X'05'
57713498266Sopenharmony_ci    o  REP    Reply field:
57813498266Sopenharmony_ci    o  X'00' succeeded
57913498266Sopenharmony_ci  */
58013498266Sopenharmony_ci  struct connectdata *conn = cf->conn;
58113498266Sopenharmony_ci  unsigned char *socksreq = sx->buffer;
58213498266Sopenharmony_ci  size_t idx;
58313498266Sopenharmony_ci  CURLcode result;
58413498266Sopenharmony_ci  CURLproxycode presult;
58513498266Sopenharmony_ci  bool socks5_resolve_local =
58613498266Sopenharmony_ci    (conn->socks_proxy.proxytype == CURLPROXY_SOCKS5) ? TRUE : FALSE;
58713498266Sopenharmony_ci  const size_t hostname_len = strlen(sx->hostname);
58813498266Sopenharmony_ci  size_t len = 0;
58913498266Sopenharmony_ci  const unsigned char auth = data->set.socks5auth;
59013498266Sopenharmony_ci  bool allow_gssapi = FALSE;
59113498266Sopenharmony_ci  struct Curl_dns_entry *dns = NULL;
59213498266Sopenharmony_ci
59313498266Sopenharmony_ci  DEBUGASSERT(auth & (CURLAUTH_BASIC | CURLAUTH_GSSAPI));
59413498266Sopenharmony_ci  switch(sx->state) {
59513498266Sopenharmony_ci  case CONNECT_SOCKS_INIT:
59613498266Sopenharmony_ci    if(conn->bits.httpproxy)
59713498266Sopenharmony_ci      infof(data, "SOCKS5: connecting to HTTP proxy %s port %d",
59813498266Sopenharmony_ci            sx->hostname, sx->remote_port);
59913498266Sopenharmony_ci
60013498266Sopenharmony_ci    /* RFC1928 chapter 5 specifies max 255 chars for domain name in packet */
60113498266Sopenharmony_ci    if(!socks5_resolve_local && hostname_len > 255) {
60213498266Sopenharmony_ci      failf(data, "SOCKS5: the destination hostname is too long to be "
60313498266Sopenharmony_ci            "resolved remotely by the proxy.");
60413498266Sopenharmony_ci      return CURLPX_LONG_HOSTNAME;
60513498266Sopenharmony_ci    }
60613498266Sopenharmony_ci
60713498266Sopenharmony_ci    if(auth & ~(CURLAUTH_BASIC | CURLAUTH_GSSAPI))
60813498266Sopenharmony_ci      infof(data,
60913498266Sopenharmony_ci            "warning: unsupported value passed to CURLOPT_SOCKS5_AUTH: %u",
61013498266Sopenharmony_ci            auth);
61113498266Sopenharmony_ci    if(!(auth & CURLAUTH_BASIC))
61213498266Sopenharmony_ci      /* disable username/password auth */
61313498266Sopenharmony_ci      sx->proxy_user = NULL;
61413498266Sopenharmony_ci#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
61513498266Sopenharmony_ci    if(auth & CURLAUTH_GSSAPI)
61613498266Sopenharmony_ci      allow_gssapi = TRUE;
61713498266Sopenharmony_ci#endif
61813498266Sopenharmony_ci
61913498266Sopenharmony_ci    idx = 0;
62013498266Sopenharmony_ci    socksreq[idx++] = 5;   /* version */
62113498266Sopenharmony_ci    idx++;                 /* number of authentication methods */
62213498266Sopenharmony_ci    socksreq[idx++] = 0;   /* no authentication */
62313498266Sopenharmony_ci    if(allow_gssapi)
62413498266Sopenharmony_ci      socksreq[idx++] = 1; /* GSS-API */
62513498266Sopenharmony_ci    if(sx->proxy_user)
62613498266Sopenharmony_ci      socksreq[idx++] = 2; /* username/password */
62713498266Sopenharmony_ci    /* write the number of authentication methods */
62813498266Sopenharmony_ci    socksreq[1] = (unsigned char) (idx - 2);
62913498266Sopenharmony_ci
63013498266Sopenharmony_ci    sx->outp = socksreq;
63113498266Sopenharmony_ci    DEBUGASSERT(idx <= sizeof(sx->buffer));
63213498266Sopenharmony_ci    sx->outstanding = idx;
63313498266Sopenharmony_ci    presult = socks_state_send(cf, sx, data, CURLPX_SEND_CONNECT,
63413498266Sopenharmony_ci                               "initial SOCKS5 request");
63513498266Sopenharmony_ci    if(CURLPX_OK != presult)
63613498266Sopenharmony_ci      return presult;
63713498266Sopenharmony_ci    else if(sx->outstanding) {
63813498266Sopenharmony_ci      /* remain in sending state */
63913498266Sopenharmony_ci      return CURLPX_OK;
64013498266Sopenharmony_ci    }
64113498266Sopenharmony_ci    sxstate(sx, data, CONNECT_SOCKS_READ);
64213498266Sopenharmony_ci    goto CONNECT_SOCKS_READ_INIT;
64313498266Sopenharmony_ci  case CONNECT_SOCKS_SEND:
64413498266Sopenharmony_ci    presult = socks_state_send(cf, sx, data, CURLPX_SEND_CONNECT,
64513498266Sopenharmony_ci                               "initial SOCKS5 request");
64613498266Sopenharmony_ci    if(CURLPX_OK != presult)
64713498266Sopenharmony_ci      return presult;
64813498266Sopenharmony_ci    else if(sx->outstanding) {
64913498266Sopenharmony_ci      /* remain in sending state */
65013498266Sopenharmony_ci      return CURLPX_OK;
65113498266Sopenharmony_ci    }
65213498266Sopenharmony_ci    FALLTHROUGH();
65313498266Sopenharmony_ci  case CONNECT_SOCKS_READ_INIT:
65413498266Sopenharmony_ciCONNECT_SOCKS_READ_INIT:
65513498266Sopenharmony_ci    sx->outstanding = 2; /* expect two bytes */
65613498266Sopenharmony_ci    sx->outp = socksreq; /* store it here */
65713498266Sopenharmony_ci    FALLTHROUGH();
65813498266Sopenharmony_ci  case CONNECT_SOCKS_READ:
65913498266Sopenharmony_ci    presult = socks_state_recv(cf, sx, data, CURLPX_RECV_CONNECT,
66013498266Sopenharmony_ci                               "initial SOCKS5 response");
66113498266Sopenharmony_ci    if(CURLPX_OK != presult)
66213498266Sopenharmony_ci      return presult;
66313498266Sopenharmony_ci    else if(sx->outstanding) {
66413498266Sopenharmony_ci      /* remain in reading state */
66513498266Sopenharmony_ci      return CURLPX_OK;
66613498266Sopenharmony_ci    }
66713498266Sopenharmony_ci    else if(socksreq[0] != 5) {
66813498266Sopenharmony_ci      failf(data, "Received invalid version in initial SOCKS5 response.");
66913498266Sopenharmony_ci      return CURLPX_BAD_VERSION;
67013498266Sopenharmony_ci    }
67113498266Sopenharmony_ci    else if(socksreq[1] == 0) {
67213498266Sopenharmony_ci      /* DONE! No authentication needed. Send request. */
67313498266Sopenharmony_ci      sxstate(sx, data, CONNECT_REQ_INIT);
67413498266Sopenharmony_ci      goto CONNECT_REQ_INIT;
67513498266Sopenharmony_ci    }
67613498266Sopenharmony_ci    else if(socksreq[1] == 2) {
67713498266Sopenharmony_ci      /* regular name + password authentication */
67813498266Sopenharmony_ci      sxstate(sx, data, CONNECT_AUTH_INIT);
67913498266Sopenharmony_ci      goto CONNECT_AUTH_INIT;
68013498266Sopenharmony_ci    }
68113498266Sopenharmony_ci#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
68213498266Sopenharmony_ci    else if(allow_gssapi && (socksreq[1] == 1)) {
68313498266Sopenharmony_ci      sxstate(sx, data, CONNECT_GSSAPI_INIT);
68413498266Sopenharmony_ci      result = Curl_SOCKS5_gssapi_negotiate(cf, data);
68513498266Sopenharmony_ci      if(result) {
68613498266Sopenharmony_ci        failf(data, "Unable to negotiate SOCKS5 GSS-API context.");
68713498266Sopenharmony_ci        return CURLPX_GSSAPI;
68813498266Sopenharmony_ci      }
68913498266Sopenharmony_ci    }
69013498266Sopenharmony_ci#endif
69113498266Sopenharmony_ci    else {
69213498266Sopenharmony_ci      /* error */
69313498266Sopenharmony_ci      if(!allow_gssapi && (socksreq[1] == 1)) {
69413498266Sopenharmony_ci        failf(data,
69513498266Sopenharmony_ci              "SOCKS5 GSSAPI per-message authentication is not supported.");
69613498266Sopenharmony_ci        return CURLPX_GSSAPI_PERMSG;
69713498266Sopenharmony_ci      }
69813498266Sopenharmony_ci      else if(socksreq[1] == 255) {
69913498266Sopenharmony_ci        failf(data, "No authentication method was acceptable.");
70013498266Sopenharmony_ci        return CURLPX_NO_AUTH;
70113498266Sopenharmony_ci      }
70213498266Sopenharmony_ci    }
70313498266Sopenharmony_ci    failf(data,
70413498266Sopenharmony_ci          "Undocumented SOCKS5 mode attempted to be used by server.");
70513498266Sopenharmony_ci    return CURLPX_UNKNOWN_MODE;
70613498266Sopenharmony_ci#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
70713498266Sopenharmony_ci  case CONNECT_GSSAPI_INIT:
70813498266Sopenharmony_ci    /* GSSAPI stuff done non-blocking */
70913498266Sopenharmony_ci    break;
71013498266Sopenharmony_ci#endif
71113498266Sopenharmony_ci
71213498266Sopenharmony_ci  default: /* do nothing! */
71313498266Sopenharmony_ci    break;
71413498266Sopenharmony_ci
71513498266Sopenharmony_ciCONNECT_AUTH_INIT:
71613498266Sopenharmony_ci  case CONNECT_AUTH_INIT: {
71713498266Sopenharmony_ci    /* Needs user name and password */
71813498266Sopenharmony_ci    size_t proxy_user_len, proxy_password_len;
71913498266Sopenharmony_ci    if(sx->proxy_user && sx->proxy_password) {
72013498266Sopenharmony_ci      proxy_user_len = strlen(sx->proxy_user);
72113498266Sopenharmony_ci      proxy_password_len = strlen(sx->proxy_password);
72213498266Sopenharmony_ci    }
72313498266Sopenharmony_ci    else {
72413498266Sopenharmony_ci      proxy_user_len = 0;
72513498266Sopenharmony_ci      proxy_password_len = 0;
72613498266Sopenharmony_ci    }
72713498266Sopenharmony_ci
72813498266Sopenharmony_ci    /*   username/password request looks like
72913498266Sopenharmony_ci     * +----+------+----------+------+----------+
73013498266Sopenharmony_ci     * |VER | ULEN |  UNAME   | PLEN |  PASSWD  |
73113498266Sopenharmony_ci     * +----+------+----------+------+----------+
73213498266Sopenharmony_ci     * | 1  |  1   | 1 to 255 |  1   | 1 to 255 |
73313498266Sopenharmony_ci     * +----+------+----------+------+----------+
73413498266Sopenharmony_ci     */
73513498266Sopenharmony_ci    len = 0;
73613498266Sopenharmony_ci    socksreq[len++] = 1;    /* username/pw subnegotiation version */
73713498266Sopenharmony_ci    socksreq[len++] = (unsigned char) proxy_user_len;
73813498266Sopenharmony_ci    if(sx->proxy_user && proxy_user_len) {
73913498266Sopenharmony_ci      /* the length must fit in a single byte */
74013498266Sopenharmony_ci      if(proxy_user_len > 255) {
74113498266Sopenharmony_ci        failf(data, "Excessive user name length for proxy auth");
74213498266Sopenharmony_ci        return CURLPX_LONG_USER;
74313498266Sopenharmony_ci      }
74413498266Sopenharmony_ci      memcpy(socksreq + len, sx->proxy_user, proxy_user_len);
74513498266Sopenharmony_ci    }
74613498266Sopenharmony_ci    len += proxy_user_len;
74713498266Sopenharmony_ci    socksreq[len++] = (unsigned char) proxy_password_len;
74813498266Sopenharmony_ci    if(sx->proxy_password && proxy_password_len) {
74913498266Sopenharmony_ci      /* the length must fit in a single byte */
75013498266Sopenharmony_ci      if(proxy_password_len > 255) {
75113498266Sopenharmony_ci        failf(data, "Excessive password length for proxy auth");
75213498266Sopenharmony_ci        return CURLPX_LONG_PASSWD;
75313498266Sopenharmony_ci      }
75413498266Sopenharmony_ci      memcpy(socksreq + len, sx->proxy_password, proxy_password_len);
75513498266Sopenharmony_ci    }
75613498266Sopenharmony_ci    len += proxy_password_len;
75713498266Sopenharmony_ci    sxstate(sx, data, CONNECT_AUTH_SEND);
75813498266Sopenharmony_ci    DEBUGASSERT(len <= sizeof(sx->buffer));
75913498266Sopenharmony_ci    sx->outstanding = len;
76013498266Sopenharmony_ci    sx->outp = socksreq;
76113498266Sopenharmony_ci  }
76213498266Sopenharmony_ci    FALLTHROUGH();
76313498266Sopenharmony_ci  case CONNECT_AUTH_SEND:
76413498266Sopenharmony_ci    presult = socks_state_send(cf, sx, data, CURLPX_SEND_AUTH,
76513498266Sopenharmony_ci                               "SOCKS5 sub-negotiation request");
76613498266Sopenharmony_ci    if(CURLPX_OK != presult)
76713498266Sopenharmony_ci      return presult;
76813498266Sopenharmony_ci    else if(sx->outstanding) {
76913498266Sopenharmony_ci      /* remain in sending state */
77013498266Sopenharmony_ci      return CURLPX_OK;
77113498266Sopenharmony_ci    }
77213498266Sopenharmony_ci    sx->outp = socksreq;
77313498266Sopenharmony_ci    sx->outstanding = 2;
77413498266Sopenharmony_ci    sxstate(sx, data, CONNECT_AUTH_READ);
77513498266Sopenharmony_ci    FALLTHROUGH();
77613498266Sopenharmony_ci  case CONNECT_AUTH_READ:
77713498266Sopenharmony_ci    presult = socks_state_recv(cf, sx, data, CURLPX_RECV_AUTH,
77813498266Sopenharmony_ci                               "SOCKS5 sub-negotiation response");
77913498266Sopenharmony_ci    if(CURLPX_OK != presult)
78013498266Sopenharmony_ci      return presult;
78113498266Sopenharmony_ci    else if(sx->outstanding) {
78213498266Sopenharmony_ci      /* remain in reading state */
78313498266Sopenharmony_ci      return CURLPX_OK;
78413498266Sopenharmony_ci    }
78513498266Sopenharmony_ci    /* ignore the first (VER) byte */
78613498266Sopenharmony_ci    else if(socksreq[1]) { /* status */
78713498266Sopenharmony_ci      failf(data, "User was rejected by the SOCKS5 server (%d %d).",
78813498266Sopenharmony_ci            socksreq[0], socksreq[1]);
78913498266Sopenharmony_ci      return CURLPX_USER_REJECTED;
79013498266Sopenharmony_ci    }
79113498266Sopenharmony_ci
79213498266Sopenharmony_ci    /* Everything is good so far, user was authenticated! */
79313498266Sopenharmony_ci    sxstate(sx, data, CONNECT_REQ_INIT);
79413498266Sopenharmony_ci    FALLTHROUGH();
79513498266Sopenharmony_ci  case CONNECT_REQ_INIT:
79613498266Sopenharmony_ciCONNECT_REQ_INIT:
79713498266Sopenharmony_ci    if(socks5_resolve_local) {
79813498266Sopenharmony_ci      enum resolve_t rc = Curl_resolv(data, sx->hostname, sx->remote_port,
79913498266Sopenharmony_ci                                      TRUE, &dns);
80013498266Sopenharmony_ci
80113498266Sopenharmony_ci      if(rc == CURLRESOLV_ERROR)
80213498266Sopenharmony_ci        return CURLPX_RESOLVE_HOST;
80313498266Sopenharmony_ci
80413498266Sopenharmony_ci      if(rc == CURLRESOLV_PENDING) {
80513498266Sopenharmony_ci        sxstate(sx, data, CONNECT_RESOLVING);
80613498266Sopenharmony_ci        return CURLPX_OK;
80713498266Sopenharmony_ci      }
80813498266Sopenharmony_ci      sxstate(sx, data, CONNECT_RESOLVED);
80913498266Sopenharmony_ci      goto CONNECT_RESOLVED;
81013498266Sopenharmony_ci    }
81113498266Sopenharmony_ci    goto CONNECT_RESOLVE_REMOTE;
81213498266Sopenharmony_ci
81313498266Sopenharmony_ci  case CONNECT_RESOLVING:
81413498266Sopenharmony_ci    /* check if we have the name resolved by now */
81513498266Sopenharmony_ci    dns = Curl_fetch_addr(data, sx->hostname, sx->remote_port);
81613498266Sopenharmony_ci
81713498266Sopenharmony_ci    if(dns) {
81813498266Sopenharmony_ci#ifdef CURLRES_ASYNCH
81913498266Sopenharmony_ci      data->state.async.dns = dns;
82013498266Sopenharmony_ci      data->state.async.done = TRUE;
82113498266Sopenharmony_ci#endif
82213498266Sopenharmony_ci      infof(data, "SOCKS5: hostname '%s' found", sx->hostname);
82313498266Sopenharmony_ci    }
82413498266Sopenharmony_ci
82513498266Sopenharmony_ci    if(!dns) {
82613498266Sopenharmony_ci      result = Curl_resolv_check(data, &dns);
82713498266Sopenharmony_ci      if(!dns) {
82813498266Sopenharmony_ci        if(result)
82913498266Sopenharmony_ci          return CURLPX_RESOLVE_HOST;
83013498266Sopenharmony_ci        return CURLPX_OK;
83113498266Sopenharmony_ci      }
83213498266Sopenharmony_ci    }
83313498266Sopenharmony_ci    FALLTHROUGH();
83413498266Sopenharmony_ci  case CONNECT_RESOLVED:
83513498266Sopenharmony_ciCONNECT_RESOLVED:
83613498266Sopenharmony_ci  {
83713498266Sopenharmony_ci    char dest[MAX_IPADR_LEN];  /* printable address */
83813498266Sopenharmony_ci    struct Curl_addrinfo *hp = NULL;
83913498266Sopenharmony_ci    if(dns)
84013498266Sopenharmony_ci      hp = dns->addr;
84113498266Sopenharmony_ci#ifdef ENABLE_IPV6
84213498266Sopenharmony_ci    if(data->set.ipver != CURL_IPRESOLVE_WHATEVER) {
84313498266Sopenharmony_ci      int wanted_family = data->set.ipver == CURL_IPRESOLVE_V4 ?
84413498266Sopenharmony_ci        AF_INET : AF_INET6;
84513498266Sopenharmony_ci      /* scan for the first proper address */
84613498266Sopenharmony_ci      while(hp && (hp->ai_family != wanted_family))
84713498266Sopenharmony_ci        hp = hp->ai_next;
84813498266Sopenharmony_ci    }
84913498266Sopenharmony_ci#endif
85013498266Sopenharmony_ci    if(!hp) {
85113498266Sopenharmony_ci      failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.",
85213498266Sopenharmony_ci            sx->hostname);
85313498266Sopenharmony_ci      return CURLPX_RESOLVE_HOST;
85413498266Sopenharmony_ci    }
85513498266Sopenharmony_ci
85613498266Sopenharmony_ci    Curl_printable_address(hp, dest, sizeof(dest));
85713498266Sopenharmony_ci
85813498266Sopenharmony_ci    len = 0;
85913498266Sopenharmony_ci    socksreq[len++] = 5; /* version (SOCKS5) */
86013498266Sopenharmony_ci    socksreq[len++] = 1; /* connect */
86113498266Sopenharmony_ci    socksreq[len++] = 0; /* must be zero */
86213498266Sopenharmony_ci    if(hp->ai_family == AF_INET) {
86313498266Sopenharmony_ci      int i;
86413498266Sopenharmony_ci      struct sockaddr_in *saddr_in;
86513498266Sopenharmony_ci      socksreq[len++] = 1; /* ATYP: IPv4 = 1 */
86613498266Sopenharmony_ci
86713498266Sopenharmony_ci      saddr_in = (struct sockaddr_in *)(void *)hp->ai_addr;
86813498266Sopenharmony_ci      for(i = 0; i < 4; i++) {
86913498266Sopenharmony_ci        socksreq[len++] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[i];
87013498266Sopenharmony_ci      }
87113498266Sopenharmony_ci
87213498266Sopenharmony_ci      infof(data, "SOCKS5 connect to %s:%d (locally resolved)", dest,
87313498266Sopenharmony_ci            sx->remote_port);
87413498266Sopenharmony_ci    }
87513498266Sopenharmony_ci#ifdef ENABLE_IPV6
87613498266Sopenharmony_ci    else if(hp->ai_family == AF_INET6) {
87713498266Sopenharmony_ci      int i;
87813498266Sopenharmony_ci      struct sockaddr_in6 *saddr_in6;
87913498266Sopenharmony_ci      socksreq[len++] = 4; /* ATYP: IPv6 = 4 */
88013498266Sopenharmony_ci
88113498266Sopenharmony_ci      saddr_in6 = (struct sockaddr_in6 *)(void *)hp->ai_addr;
88213498266Sopenharmony_ci      for(i = 0; i < 16; i++) {
88313498266Sopenharmony_ci        socksreq[len++] =
88413498266Sopenharmony_ci          ((unsigned char *)&saddr_in6->sin6_addr.s6_addr)[i];
88513498266Sopenharmony_ci      }
88613498266Sopenharmony_ci
88713498266Sopenharmony_ci      infof(data, "SOCKS5 connect to [%s]:%d (locally resolved)", dest,
88813498266Sopenharmony_ci            sx->remote_port);
88913498266Sopenharmony_ci    }
89013498266Sopenharmony_ci#endif
89113498266Sopenharmony_ci    else {
89213498266Sopenharmony_ci      hp = NULL; /* fail! */
89313498266Sopenharmony_ci      failf(data, "SOCKS5 connection to %s not supported", dest);
89413498266Sopenharmony_ci    }
89513498266Sopenharmony_ci
89613498266Sopenharmony_ci    Curl_resolv_unlock(data, dns); /* not used anymore from now on */
89713498266Sopenharmony_ci    goto CONNECT_REQ_SEND;
89813498266Sopenharmony_ci  }
89913498266Sopenharmony_ciCONNECT_RESOLVE_REMOTE:
90013498266Sopenharmony_ci  case CONNECT_RESOLVE_REMOTE:
90113498266Sopenharmony_ci    /* Authentication is complete, now specify destination to the proxy */
90213498266Sopenharmony_ci    len = 0;
90313498266Sopenharmony_ci    socksreq[len++] = 5; /* version (SOCKS5) */
90413498266Sopenharmony_ci    socksreq[len++] = 1; /* connect */
90513498266Sopenharmony_ci    socksreq[len++] = 0; /* must be zero */
90613498266Sopenharmony_ci
90713498266Sopenharmony_ci    if(!socks5_resolve_local) {
90813498266Sopenharmony_ci      /* ATYP: domain name = 3,
90913498266Sopenharmony_ci         IPv6 == 4,
91013498266Sopenharmony_ci         IPv4 == 1 */
91113498266Sopenharmony_ci      unsigned char ip4[4];
91213498266Sopenharmony_ci#ifdef ENABLE_IPV6
91313498266Sopenharmony_ci      if(conn->bits.ipv6_ip) {
91413498266Sopenharmony_ci        char ip6[16];
91513498266Sopenharmony_ci        if(1 != Curl_inet_pton(AF_INET6, sx->hostname, ip6))
91613498266Sopenharmony_ci          return CURLPX_BAD_ADDRESS_TYPE;
91713498266Sopenharmony_ci        socksreq[len++] = 4;
91813498266Sopenharmony_ci        memcpy(&socksreq[len], ip6, sizeof(ip6));
91913498266Sopenharmony_ci        len += sizeof(ip6);
92013498266Sopenharmony_ci      }
92113498266Sopenharmony_ci      else
92213498266Sopenharmony_ci#endif
92313498266Sopenharmony_ci      if(1 == Curl_inet_pton(AF_INET, sx->hostname, ip4)) {
92413498266Sopenharmony_ci        socksreq[len++] = 1;
92513498266Sopenharmony_ci        memcpy(&socksreq[len], ip4, sizeof(ip4));
92613498266Sopenharmony_ci        len += sizeof(ip4);
92713498266Sopenharmony_ci      }
92813498266Sopenharmony_ci      else {
92913498266Sopenharmony_ci        socksreq[len++] = 3;
93013498266Sopenharmony_ci        socksreq[len++] = (unsigned char) hostname_len; /* one byte length */
93113498266Sopenharmony_ci        memcpy(&socksreq[len], sx->hostname, hostname_len); /* w/o NULL */
93213498266Sopenharmony_ci        len += hostname_len;
93313498266Sopenharmony_ci      }
93413498266Sopenharmony_ci      infof(data, "SOCKS5 connect to %s:%d (remotely resolved)",
93513498266Sopenharmony_ci            sx->hostname, sx->remote_port);
93613498266Sopenharmony_ci    }
93713498266Sopenharmony_ci    FALLTHROUGH();
93813498266Sopenharmony_ci
93913498266Sopenharmony_ci  case CONNECT_REQ_SEND:
94013498266Sopenharmony_ciCONNECT_REQ_SEND:
94113498266Sopenharmony_ci    /* PORT MSB */
94213498266Sopenharmony_ci    socksreq[len++] = (unsigned char)((sx->remote_port >> 8) & 0xff);
94313498266Sopenharmony_ci    /* PORT LSB */
94413498266Sopenharmony_ci    socksreq[len++] = (unsigned char)(sx->remote_port & 0xff);
94513498266Sopenharmony_ci
94613498266Sopenharmony_ci#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
94713498266Sopenharmony_ci    if(conn->socks5_gssapi_enctype) {
94813498266Sopenharmony_ci      failf(data, "SOCKS5 GSS-API protection not yet implemented.");
94913498266Sopenharmony_ci      return CURLPX_GSSAPI_PROTECTION;
95013498266Sopenharmony_ci    }
95113498266Sopenharmony_ci#endif
95213498266Sopenharmony_ci    sx->outp = socksreq;
95313498266Sopenharmony_ci    DEBUGASSERT(len <= sizeof(sx->buffer));
95413498266Sopenharmony_ci    sx->outstanding = len;
95513498266Sopenharmony_ci    sxstate(sx, data, CONNECT_REQ_SENDING);
95613498266Sopenharmony_ci    FALLTHROUGH();
95713498266Sopenharmony_ci  case CONNECT_REQ_SENDING:
95813498266Sopenharmony_ci    presult = socks_state_send(cf, sx, data, CURLPX_SEND_REQUEST,
95913498266Sopenharmony_ci                               "SOCKS5 connect request");
96013498266Sopenharmony_ci    if(CURLPX_OK != presult)
96113498266Sopenharmony_ci      return presult;
96213498266Sopenharmony_ci    else if(sx->outstanding) {
96313498266Sopenharmony_ci      /* remain in send state */
96413498266Sopenharmony_ci      return CURLPX_OK;
96513498266Sopenharmony_ci    }
96613498266Sopenharmony_ci#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
96713498266Sopenharmony_ci    if(conn->socks5_gssapi_enctype) {
96813498266Sopenharmony_ci      failf(data, "SOCKS5 GSS-API protection not yet implemented.");
96913498266Sopenharmony_ci      return CURLPX_GSSAPI_PROTECTION;
97013498266Sopenharmony_ci    }
97113498266Sopenharmony_ci#endif
97213498266Sopenharmony_ci    sx->outstanding = 10; /* minimum packet size is 10 */
97313498266Sopenharmony_ci    sx->outp = socksreq;
97413498266Sopenharmony_ci    sxstate(sx, data, CONNECT_REQ_READ);
97513498266Sopenharmony_ci    FALLTHROUGH();
97613498266Sopenharmony_ci  case CONNECT_REQ_READ:
97713498266Sopenharmony_ci    presult = socks_state_recv(cf, sx, data, CURLPX_RECV_REQACK,
97813498266Sopenharmony_ci                               "SOCKS5 connect request ack");
97913498266Sopenharmony_ci    if(CURLPX_OK != presult)
98013498266Sopenharmony_ci      return presult;
98113498266Sopenharmony_ci    else if(sx->outstanding) {
98213498266Sopenharmony_ci      /* remain in reading state */
98313498266Sopenharmony_ci      return CURLPX_OK;
98413498266Sopenharmony_ci    }
98513498266Sopenharmony_ci    else if(socksreq[0] != 5) { /* version */
98613498266Sopenharmony_ci      failf(data,
98713498266Sopenharmony_ci            "SOCKS5 reply has wrong version, version should be 5.");
98813498266Sopenharmony_ci      return CURLPX_BAD_VERSION;
98913498266Sopenharmony_ci    }
99013498266Sopenharmony_ci    else if(socksreq[1]) { /* Anything besides 0 is an error */
99113498266Sopenharmony_ci      CURLproxycode rc = CURLPX_REPLY_UNASSIGNED;
99213498266Sopenharmony_ci      int code = socksreq[1];
99313498266Sopenharmony_ci      failf(data, "Can't complete SOCKS5 connection to %s. (%d)",
99413498266Sopenharmony_ci            sx->hostname, (unsigned char)socksreq[1]);
99513498266Sopenharmony_ci      if(code < 9) {
99613498266Sopenharmony_ci        /* RFC 1928 section 6 lists: */
99713498266Sopenharmony_ci        static const CURLproxycode lookup[] = {
99813498266Sopenharmony_ci          CURLPX_OK,
99913498266Sopenharmony_ci          CURLPX_REPLY_GENERAL_SERVER_FAILURE,
100013498266Sopenharmony_ci          CURLPX_REPLY_NOT_ALLOWED,
100113498266Sopenharmony_ci          CURLPX_REPLY_NETWORK_UNREACHABLE,
100213498266Sopenharmony_ci          CURLPX_REPLY_HOST_UNREACHABLE,
100313498266Sopenharmony_ci          CURLPX_REPLY_CONNECTION_REFUSED,
100413498266Sopenharmony_ci          CURLPX_REPLY_TTL_EXPIRED,
100513498266Sopenharmony_ci          CURLPX_REPLY_COMMAND_NOT_SUPPORTED,
100613498266Sopenharmony_ci          CURLPX_REPLY_ADDRESS_TYPE_NOT_SUPPORTED,
100713498266Sopenharmony_ci        };
100813498266Sopenharmony_ci        rc = lookup[code];
100913498266Sopenharmony_ci      }
101013498266Sopenharmony_ci      return rc;
101113498266Sopenharmony_ci    }
101213498266Sopenharmony_ci
101313498266Sopenharmony_ci    /* Fix: in general, returned BND.ADDR is variable length parameter by RFC
101413498266Sopenharmony_ci       1928, so the reply packet should be read until the end to avoid errors
101513498266Sopenharmony_ci       at subsequent protocol level.
101613498266Sopenharmony_ci
101713498266Sopenharmony_ci       +----+-----+-------+------+----------+----------+
101813498266Sopenharmony_ci       |VER | REP |  RSV  | ATYP | BND.ADDR | BND.PORT |
101913498266Sopenharmony_ci       +----+-----+-------+------+----------+----------+
102013498266Sopenharmony_ci       | 1  |  1  | X'00' |  1   | Variable |    2     |
102113498266Sopenharmony_ci       +----+-----+-------+------+----------+----------+
102213498266Sopenharmony_ci
102313498266Sopenharmony_ci       ATYP:
102413498266Sopenharmony_ci       o  IP v4 address: X'01', BND.ADDR = 4 byte
102513498266Sopenharmony_ci       o  domain name:  X'03', BND.ADDR = [ 1 byte length, string ]
102613498266Sopenharmony_ci       o  IP v6 address: X'04', BND.ADDR = 16 byte
102713498266Sopenharmony_ci    */
102813498266Sopenharmony_ci
102913498266Sopenharmony_ci    /* Calculate real packet size */
103013498266Sopenharmony_ci    if(socksreq[3] == 3) {
103113498266Sopenharmony_ci      /* domain name */
103213498266Sopenharmony_ci      int addrlen = (int) socksreq[4];
103313498266Sopenharmony_ci      len = 5 + addrlen + 2;
103413498266Sopenharmony_ci    }
103513498266Sopenharmony_ci    else if(socksreq[3] == 4) {
103613498266Sopenharmony_ci      /* IPv6 */
103713498266Sopenharmony_ci      len = 4 + 16 + 2;
103813498266Sopenharmony_ci    }
103913498266Sopenharmony_ci    else if(socksreq[3] == 1) {
104013498266Sopenharmony_ci      len = 4 + 4 + 2;
104113498266Sopenharmony_ci    }
104213498266Sopenharmony_ci    else {
104313498266Sopenharmony_ci      failf(data, "SOCKS5 reply has wrong address type.");
104413498266Sopenharmony_ci      return CURLPX_BAD_ADDRESS_TYPE;
104513498266Sopenharmony_ci    }
104613498266Sopenharmony_ci
104713498266Sopenharmony_ci    /* At this point we already read first 10 bytes */
104813498266Sopenharmony_ci#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
104913498266Sopenharmony_ci    if(!conn->socks5_gssapi_enctype) {
105013498266Sopenharmony_ci      /* decrypt_gssapi_blockread already read the whole packet */
105113498266Sopenharmony_ci#endif
105213498266Sopenharmony_ci      if(len > 10) {
105313498266Sopenharmony_ci        DEBUGASSERT(len <= sizeof(sx->buffer));
105413498266Sopenharmony_ci        sx->outstanding = len - 10; /* get the rest */
105513498266Sopenharmony_ci        sx->outp = &socksreq[10];
105613498266Sopenharmony_ci        sxstate(sx, data, CONNECT_REQ_READ_MORE);
105713498266Sopenharmony_ci      }
105813498266Sopenharmony_ci      else {
105913498266Sopenharmony_ci        sxstate(sx, data, CONNECT_DONE);
106013498266Sopenharmony_ci        break;
106113498266Sopenharmony_ci      }
106213498266Sopenharmony_ci#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
106313498266Sopenharmony_ci    }
106413498266Sopenharmony_ci#endif
106513498266Sopenharmony_ci    FALLTHROUGH();
106613498266Sopenharmony_ci  case CONNECT_REQ_READ_MORE:
106713498266Sopenharmony_ci    presult = socks_state_recv(cf, sx, data, CURLPX_RECV_ADDRESS,
106813498266Sopenharmony_ci                               "SOCKS5 connect request address");
106913498266Sopenharmony_ci    if(CURLPX_OK != presult)
107013498266Sopenharmony_ci      return presult;
107113498266Sopenharmony_ci    else if(sx->outstanding) {
107213498266Sopenharmony_ci      /* remain in reading state */
107313498266Sopenharmony_ci      return CURLPX_OK;
107413498266Sopenharmony_ci    }
107513498266Sopenharmony_ci    sxstate(sx, data, CONNECT_DONE);
107613498266Sopenharmony_ci  }
107713498266Sopenharmony_ci  infof(data, "SOCKS5 request granted.");
107813498266Sopenharmony_ci
107913498266Sopenharmony_ci  return CURLPX_OK; /* Proxy was successful! */
108013498266Sopenharmony_ci}
108113498266Sopenharmony_ci
108213498266Sopenharmony_cistatic CURLcode connect_SOCKS(struct Curl_cfilter *cf,
108313498266Sopenharmony_ci                              struct socks_state *sxstate,
108413498266Sopenharmony_ci                              struct Curl_easy *data)
108513498266Sopenharmony_ci{
108613498266Sopenharmony_ci  CURLcode result = CURLE_OK;
108713498266Sopenharmony_ci  CURLproxycode pxresult = CURLPX_OK;
108813498266Sopenharmony_ci  struct connectdata *conn = cf->conn;
108913498266Sopenharmony_ci
109013498266Sopenharmony_ci  switch(conn->socks_proxy.proxytype) {
109113498266Sopenharmony_ci  case CURLPROXY_SOCKS5:
109213498266Sopenharmony_ci  case CURLPROXY_SOCKS5_HOSTNAME:
109313498266Sopenharmony_ci    pxresult = do_SOCKS5(cf, sxstate, data);
109413498266Sopenharmony_ci    break;
109513498266Sopenharmony_ci
109613498266Sopenharmony_ci  case CURLPROXY_SOCKS4:
109713498266Sopenharmony_ci  case CURLPROXY_SOCKS4A:
109813498266Sopenharmony_ci    pxresult = do_SOCKS4(cf, sxstate, data);
109913498266Sopenharmony_ci    break;
110013498266Sopenharmony_ci
110113498266Sopenharmony_ci  default:
110213498266Sopenharmony_ci    failf(data, "unknown proxytype option given");
110313498266Sopenharmony_ci    result = CURLE_COULDNT_CONNECT;
110413498266Sopenharmony_ci  } /* switch proxytype */
110513498266Sopenharmony_ci  if(pxresult) {
110613498266Sopenharmony_ci    result = CURLE_PROXY;
110713498266Sopenharmony_ci    data->info.pxcode = pxresult;
110813498266Sopenharmony_ci  }
110913498266Sopenharmony_ci
111013498266Sopenharmony_ci  return result;
111113498266Sopenharmony_ci}
111213498266Sopenharmony_ci
111313498266Sopenharmony_cistatic void socks_proxy_cf_free(struct Curl_cfilter *cf)
111413498266Sopenharmony_ci{
111513498266Sopenharmony_ci  struct socks_state *sxstate = cf->ctx;
111613498266Sopenharmony_ci  if(sxstate) {
111713498266Sopenharmony_ci    free(sxstate);
111813498266Sopenharmony_ci    cf->ctx = NULL;
111913498266Sopenharmony_ci  }
112013498266Sopenharmony_ci}
112113498266Sopenharmony_ci
112213498266Sopenharmony_ci/* After a TCP connection to the proxy has been verified, this function does
112313498266Sopenharmony_ci   the next magic steps. If 'done' isn't set TRUE, it is not done yet and
112413498266Sopenharmony_ci   must be called again.
112513498266Sopenharmony_ci
112613498266Sopenharmony_ci   Note: this function's sub-functions call failf()
112713498266Sopenharmony_ci
112813498266Sopenharmony_ci*/
112913498266Sopenharmony_cistatic CURLcode socks_proxy_cf_connect(struct Curl_cfilter *cf,
113013498266Sopenharmony_ci                                       struct Curl_easy *data,
113113498266Sopenharmony_ci                                       bool blocking, bool *done)
113213498266Sopenharmony_ci{
113313498266Sopenharmony_ci  CURLcode result;
113413498266Sopenharmony_ci  struct connectdata *conn = cf->conn;
113513498266Sopenharmony_ci  int sockindex = cf->sockindex;
113613498266Sopenharmony_ci  struct socks_state *sx = cf->ctx;
113713498266Sopenharmony_ci
113813498266Sopenharmony_ci  if(cf->connected) {
113913498266Sopenharmony_ci    *done = TRUE;
114013498266Sopenharmony_ci    return CURLE_OK;
114113498266Sopenharmony_ci  }
114213498266Sopenharmony_ci
114313498266Sopenharmony_ci  result = cf->next->cft->do_connect(cf->next, data, blocking, done);
114413498266Sopenharmony_ci  if(result || !*done)
114513498266Sopenharmony_ci    return result;
114613498266Sopenharmony_ci
114713498266Sopenharmony_ci  if(!sx) {
114813498266Sopenharmony_ci    sx = calloc(1, sizeof(*sx));
114913498266Sopenharmony_ci    if(!sx)
115013498266Sopenharmony_ci      return CURLE_OUT_OF_MEMORY;
115113498266Sopenharmony_ci    cf->ctx = sx;
115213498266Sopenharmony_ci  }
115313498266Sopenharmony_ci
115413498266Sopenharmony_ci  if(sx->state == CONNECT_INIT) {
115513498266Sopenharmony_ci    /* for the secondary socket (FTP), use the "connect to host"
115613498266Sopenharmony_ci     * but ignore the "connect to port" (use the secondary port)
115713498266Sopenharmony_ci     */
115813498266Sopenharmony_ci    sxstate(sx, data, CONNECT_SOCKS_INIT);
115913498266Sopenharmony_ci    sx->hostname =
116013498266Sopenharmony_ci      conn->bits.httpproxy ?
116113498266Sopenharmony_ci      conn->http_proxy.host.name :
116213498266Sopenharmony_ci      conn->bits.conn_to_host ?
116313498266Sopenharmony_ci      conn->conn_to_host.name :
116413498266Sopenharmony_ci      sockindex == SECONDARYSOCKET ?
116513498266Sopenharmony_ci      conn->secondaryhostname : conn->host.name;
116613498266Sopenharmony_ci    sx->remote_port =
116713498266Sopenharmony_ci      conn->bits.httpproxy ? (int)conn->http_proxy.port :
116813498266Sopenharmony_ci      sockindex == SECONDARYSOCKET ? conn->secondary_port :
116913498266Sopenharmony_ci      conn->bits.conn_to_port ? conn->conn_to_port :
117013498266Sopenharmony_ci      conn->remote_port;
117113498266Sopenharmony_ci    sx->proxy_user = conn->socks_proxy.user;
117213498266Sopenharmony_ci    sx->proxy_password = conn->socks_proxy.passwd;
117313498266Sopenharmony_ci  }
117413498266Sopenharmony_ci
117513498266Sopenharmony_ci  result = connect_SOCKS(cf, sx, data);
117613498266Sopenharmony_ci  if(!result && sx->state == CONNECT_DONE) {
117713498266Sopenharmony_ci    cf->connected = TRUE;
117813498266Sopenharmony_ci    Curl_verboseconnect(data, conn);
117913498266Sopenharmony_ci    socks_proxy_cf_free(cf);
118013498266Sopenharmony_ci  }
118113498266Sopenharmony_ci
118213498266Sopenharmony_ci  *done = cf->connected;
118313498266Sopenharmony_ci  return result;
118413498266Sopenharmony_ci}
118513498266Sopenharmony_ci
118613498266Sopenharmony_cistatic void socks_cf_adjust_pollset(struct Curl_cfilter *cf,
118713498266Sopenharmony_ci                                     struct Curl_easy *data,
118813498266Sopenharmony_ci                                     struct easy_pollset *ps)
118913498266Sopenharmony_ci{
119013498266Sopenharmony_ci  struct socks_state *sx = cf->ctx;
119113498266Sopenharmony_ci
119213498266Sopenharmony_ci  if(!cf->connected && sx) {
119313498266Sopenharmony_ci    /* If we are not connected, the filter below is and has nothing
119413498266Sopenharmony_ci     * to wait on, we determine what to wait for. */
119513498266Sopenharmony_ci    curl_socket_t sock = Curl_conn_cf_get_socket(cf, data);
119613498266Sopenharmony_ci    switch(sx->state) {
119713498266Sopenharmony_ci    case CONNECT_RESOLVING:
119813498266Sopenharmony_ci    case CONNECT_SOCKS_READ:
119913498266Sopenharmony_ci    case CONNECT_AUTH_READ:
120013498266Sopenharmony_ci    case CONNECT_REQ_READ:
120113498266Sopenharmony_ci    case CONNECT_REQ_READ_MORE:
120213498266Sopenharmony_ci      Curl_pollset_set_in_only(data, ps, sock);
120313498266Sopenharmony_ci      break;
120413498266Sopenharmony_ci    default:
120513498266Sopenharmony_ci      Curl_pollset_set_out_only(data, ps, sock);
120613498266Sopenharmony_ci      break;
120713498266Sopenharmony_ci    }
120813498266Sopenharmony_ci  }
120913498266Sopenharmony_ci}
121013498266Sopenharmony_ci
121113498266Sopenharmony_cistatic void socks_proxy_cf_close(struct Curl_cfilter *cf,
121213498266Sopenharmony_ci                                 struct Curl_easy *data)
121313498266Sopenharmony_ci{
121413498266Sopenharmony_ci
121513498266Sopenharmony_ci  DEBUGASSERT(cf->next);
121613498266Sopenharmony_ci  cf->connected = FALSE;
121713498266Sopenharmony_ci  socks_proxy_cf_free(cf);
121813498266Sopenharmony_ci  cf->next->cft->do_close(cf->next, data);
121913498266Sopenharmony_ci}
122013498266Sopenharmony_ci
122113498266Sopenharmony_cistatic void socks_proxy_cf_destroy(struct Curl_cfilter *cf,
122213498266Sopenharmony_ci                                   struct Curl_easy *data)
122313498266Sopenharmony_ci{
122413498266Sopenharmony_ci  (void)data;
122513498266Sopenharmony_ci  socks_proxy_cf_free(cf);
122613498266Sopenharmony_ci}
122713498266Sopenharmony_ci
122813498266Sopenharmony_cistatic void socks_cf_get_host(struct Curl_cfilter *cf,
122913498266Sopenharmony_ci                              struct Curl_easy *data,
123013498266Sopenharmony_ci                              const char **phost,
123113498266Sopenharmony_ci                              const char **pdisplay_host,
123213498266Sopenharmony_ci                              int *pport)
123313498266Sopenharmony_ci{
123413498266Sopenharmony_ci  (void)data;
123513498266Sopenharmony_ci  if(!cf->connected) {
123613498266Sopenharmony_ci    *phost = cf->conn->socks_proxy.host.name;
123713498266Sopenharmony_ci    *pdisplay_host = cf->conn->http_proxy.host.dispname;
123813498266Sopenharmony_ci    *pport = (int)cf->conn->socks_proxy.port;
123913498266Sopenharmony_ci  }
124013498266Sopenharmony_ci  else {
124113498266Sopenharmony_ci    cf->next->cft->get_host(cf->next, data, phost, pdisplay_host, pport);
124213498266Sopenharmony_ci  }
124313498266Sopenharmony_ci}
124413498266Sopenharmony_ci
124513498266Sopenharmony_cistruct Curl_cftype Curl_cft_socks_proxy = {
124613498266Sopenharmony_ci  "SOCKS-PROXYY",
124713498266Sopenharmony_ci  CF_TYPE_IP_CONNECT,
124813498266Sopenharmony_ci  0,
124913498266Sopenharmony_ci  socks_proxy_cf_destroy,
125013498266Sopenharmony_ci  socks_proxy_cf_connect,
125113498266Sopenharmony_ci  socks_proxy_cf_close,
125213498266Sopenharmony_ci  socks_cf_get_host,
125313498266Sopenharmony_ci  socks_cf_adjust_pollset,
125413498266Sopenharmony_ci  Curl_cf_def_data_pending,
125513498266Sopenharmony_ci  Curl_cf_def_send,
125613498266Sopenharmony_ci  Curl_cf_def_recv,
125713498266Sopenharmony_ci  Curl_cf_def_cntrl,
125813498266Sopenharmony_ci  Curl_cf_def_conn_is_alive,
125913498266Sopenharmony_ci  Curl_cf_def_conn_keep_alive,
126013498266Sopenharmony_ci  Curl_cf_def_query,
126113498266Sopenharmony_ci};
126213498266Sopenharmony_ci
126313498266Sopenharmony_ciCURLcode Curl_cf_socks_proxy_insert_after(struct Curl_cfilter *cf_at,
126413498266Sopenharmony_ci                                          struct Curl_easy *data)
126513498266Sopenharmony_ci{
126613498266Sopenharmony_ci  struct Curl_cfilter *cf;
126713498266Sopenharmony_ci  CURLcode result;
126813498266Sopenharmony_ci
126913498266Sopenharmony_ci  (void)data;
127013498266Sopenharmony_ci  result = Curl_cf_create(&cf, &Curl_cft_socks_proxy, NULL);
127113498266Sopenharmony_ci  if(!result)
127213498266Sopenharmony_ci    Curl_conn_cf_insert_after(cf_at, cf);
127313498266Sopenharmony_ci  return result;
127413498266Sopenharmony_ci}
127513498266Sopenharmony_ci
127613498266Sopenharmony_ci#endif /* CURL_DISABLE_PROXY */
1277