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