Skip to content

Commit 4a4b63d

Browse files
committed
socks: make the connect phase non-blocking
Removes two entries from KNOWN_BUGS. Closes #4907
1 parent d60b1b3 commit 4a4b63d

13 files changed

+817
-553
lines changed

docs/KNOWN_BUGS

-17
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,6 @@ problems may have been fixed or changed somewhat since this was written!
8787
9.1 SFTP doesn't do CURLOPT_POSTQUOTE correct
8888

8989
10. SOCKS
90-
10.1 SOCKS proxy connections are done blocking
91-
10.2 SOCKS don't support timeouts
9290
10.3 FTPS over SOCKS
9391
10.4 active FTP over a SOCKS
9492

@@ -621,21 +619,6 @@ problems may have been fixed or changed somewhat since this was written!
621619

622620
10. SOCKS
623621

624-
10.1 SOCKS proxy connections are done blocking
625-
626-
Both SOCKS5 and SOCKS4 proxy connections are done blocking, which is very bad
627-
when used with the multi interface.
628-
629-
10.2 SOCKS don't support timeouts
630-
631-
The SOCKS4 connection codes don't properly acknowledge (connect) timeouts.
632-
According to bug #1556528, even the SOCKS5 connect code does not do it right:
633-
https://curl.haxx.se/bug/view.cgi?id=604
634-
635-
When connecting to a SOCK proxy, the (connect) timeout is not properly
636-
acknowledged after the actual TCP connect (during the SOCKS "negotiate"
637-
phase).
638-
639622
10.3 FTPS over SOCKS
640623

641624
libcurl doesn't support FTPS over a SOCKS proxy.

docs/TODO

-1
Original file line numberDiff line numberDiff line change
@@ -405,7 +405,6 @@
405405
EWOULDBLOCK or similar. Blocking cases include:
406406

407407
- Name resolves on non-windows unless c-ares or the threaded resolver is used
408-
- SOCKS proxy handshakes
409408
- file:// transfers
410409
- TELNET transfers
411410
- The "DONE" operation (post transfer protocol-specific actions) for the

lib/connect.c

+54-27
Original file line numberDiff line numberDiff line change
@@ -745,58 +745,82 @@ void Curl_updateconninfo(struct connectdata *conn, curl_socket_t sockfd)
745745
Curl_persistconninfo(conn);
746746
}
747747

748-
/* after a TCP connection to the proxy has been verified, this function does
749-
the next magic step.
748+
/* After a TCP connection to the proxy has been verified, this function does
749+
the next magic steps. If 'done' isn't set TRUE, it is not done yet and
750+
must be called again.
750751
751752
Note: this function's sub-functions call failf()
752753
753754
*/
754-
static CURLcode connected_proxy(struct connectdata *conn, int sockindex)
755+
static CURLcode connect_SOCKS(struct connectdata *conn, int sockindex,
756+
bool *done)
755757
{
756758
CURLcode result = CURLE_OK;
757759

760+
infof(conn->data, "connect_SOCKS is called [socks state %d]\n",
761+
conn->cnnct.state);
762+
758763
if(conn->bits.socksproxy) {
759764
#ifndef CURL_DISABLE_PROXY
760765
/* for the secondary socket (FTP), use the "connect to host"
761766
* but ignore the "connect to port" (use the secondary port)
762767
*/
763-
const char * const host = conn->bits.httpproxy ?
764-
conn->http_proxy.host.name :
765-
conn->bits.conn_to_host ?
766-
conn->conn_to_host.name :
767-
sockindex == SECONDARYSOCKET ?
768-
conn->secondaryhostname : conn->host.name;
769-
const int port = conn->bits.httpproxy ? (int)conn->http_proxy.port :
770-
sockindex == SECONDARYSOCKET ? conn->secondary_port :
771-
conn->bits.conn_to_port ? conn->conn_to_port :
772-
conn->remote_port;
773-
conn->bits.socksproxy_connecting = TRUE;
768+
const char * const host =
769+
conn->bits.httpproxy ?
770+
conn->http_proxy.host.name :
771+
conn->bits.conn_to_host ?
772+
conn->conn_to_host.name :
773+
sockindex == SECONDARYSOCKET ?
774+
conn->secondaryhostname : conn->host.name;
775+
const int port =
776+
conn->bits.httpproxy ? (int)conn->http_proxy.port :
777+
sockindex == SECONDARYSOCKET ? conn->secondary_port :
778+
conn->bits.conn_to_port ? conn->conn_to_port :
779+
conn->remote_port;
774780
switch(conn->socks_proxy.proxytype) {
775781
case CURLPROXY_SOCKS5:
776782
case CURLPROXY_SOCKS5_HOSTNAME:
777783
result = Curl_SOCKS5(conn->socks_proxy.user, conn->socks_proxy.passwd,
778-
host, port, sockindex, conn);
784+
host, port, sockindex, conn, done);
779785
break;
780786

781787
case CURLPROXY_SOCKS4:
782788
case CURLPROXY_SOCKS4A:
783789
result = Curl_SOCKS4(conn->socks_proxy.user, host, port, sockindex,
784-
conn);
790+
conn, done);
785791
break;
786792

787793
default:
788794
failf(conn->data, "unknown proxytype option given");
789795
result = CURLE_COULDNT_CONNECT;
790796
} /* switch proxytype */
791-
conn->bits.socksproxy_connecting = FALSE;
792797
#else
793798
(void)sockindex;
794799
#endif /* CURL_DISABLE_PROXY */
795800
}
801+
else
802+
*done = TRUE; /* no SOCKS proxy, so consider us connected */
796803

797804
return result;
798805
}
799806

807+
/*
808+
* post_SOCKS() is called after a successful connect to the peer, which
809+
* *could* be a SOCKS proxy
810+
*/
811+
static void post_SOCKS(struct connectdata *conn,
812+
int sockindex,
813+
bool *connected)
814+
{
815+
conn->bits.tcpconnect[sockindex] = TRUE;
816+
817+
*connected = TRUE;
818+
if(sockindex == FIRSTSOCKET)
819+
Curl_pgrsTime(conn->data, TIMER_CONNECT); /* connect done */
820+
Curl_updateconninfo(conn, conn->sock[sockindex]);
821+
Curl_verboseconnect(conn);
822+
}
823+
800824
/*
801825
* Curl_is_connected() checks if the socket has connected.
802826
*/
@@ -834,6 +858,14 @@ CURLcode Curl_is_connected(struct connectdata *conn,
834858
return CURLE_OPERATION_TIMEDOUT;
835859
}
836860

861+
if(SOCKS_STATE(conn->cnnct.state)) {
862+
/* still doing SOCKS */
863+
result = connect_SOCKS(conn, sockindex, connected);
864+
if(!result && *connected)
865+
post_SOCKS(conn, sockindex, connected);
866+
return result;
867+
}
868+
837869
for(i = 0; i<2; i++) {
838870
const int other = i ^ 1;
839871
if(conn->tempsock[i] == CURL_SOCKET_BAD)
@@ -900,18 +932,13 @@ CURLcode Curl_is_connected(struct connectdata *conn,
900932
conn->tempsock[other] = CURL_SOCKET_BAD;
901933
}
902934

903-
/* see if we need to do any proxy magic first once we connected */
904-
result = connected_proxy(conn, sockindex);
905-
if(result)
935+
/* see if we need to kick off any SOCKS proxy magic once we
936+
connected */
937+
result = connect_SOCKS(conn, sockindex, connected);
938+
if(result || !*connected)
906939
return result;
907940

908-
conn->bits.tcpconnect[sockindex] = TRUE;
909-
910-
*connected = TRUE;
911-
if(sockindex == FIRSTSOCKET)
912-
Curl_pgrsTime(data, TIMER_CONNECT); /* connect done */
913-
Curl_updateconninfo(conn, conn->sock[sockindex]);
914-
Curl_verboseconnect(conn);
941+
post_SOCKS(conn, sockindex, connected);
915942

916943
return CURLE_OK;
917944
}

lib/ftp.c

+6-3
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@
5555
#include "transfer.h"
5656
#include "escape.h"
5757
#include "http.h" /* for HTTP proxy tunnel stuff */
58-
#include "socks.h"
5958
#include "ftp.h"
6059
#include "fileinfo.h"
6160
#include "ftplistparser.h"
@@ -78,6 +77,7 @@
7877
#include "warnless.h"
7978
#include "http_proxy.h"
8079
#include "non-ascii.h"
80+
#include "socks.h"
8181
/* The last 3 #include files should be in this order */
8282
#include "curl_printf.h"
8383
#include "curl_memory.h"
@@ -810,6 +810,9 @@ static int ftp_domore_getsock(struct connectdata *conn, curl_socket_t *socks)
810810
* handle ordinary commands.
811811
*/
812812

813+
if(SOCKS_STATE(conn->cnnct.state))
814+
return Curl_SOCKS_getsock(conn, socks, SECONDARYSOCKET);
815+
813816
if(FTP_STOP == ftpc->state) {
814817
int bits = GETSOCK_READSOCK(0);
815818

@@ -919,7 +922,7 @@ static CURLcode ftp_state_use_port(struct connectdata *conn,
919922
struct sockaddr_in6 * const sa6 = (void *)sa;
920923
#endif
921924
static const char mode[][5] = { "EPRT", "PORT" };
922-
int rc;
925+
enum resolve_t rc;
923926
int error;
924927
char *host = NULL;
925928
char *string_ftpport = data->set.str[STRING_FTPPORT];
@@ -1794,7 +1797,7 @@ static CURLcode ftp_state_pasv_resp(struct connectdata *conn,
17941797
CURLcode result;
17951798
struct Curl_easy *data = conn->data;
17961799
struct Curl_dns_entry *addr = NULL;
1797-
int rc;
1800+
enum resolve_t rc;
17981801
unsigned short connectport; /* the local port connect() should use! */
17991802
char *str = &data->state.buffer[4]; /* start on the first letter */
18001803

lib/hostip.c

+12-12
Original file line numberDiff line numberDiff line change
@@ -483,16 +483,16 @@ Curl_cache_addr(struct Curl_easy *data,
483483
* CURLRESOLV_PENDING (1) = waiting for response, no pointer
484484
*/
485485

486-
int Curl_resolv(struct connectdata *conn,
487-
const char *hostname,
488-
int port,
489-
bool allowDOH,
490-
struct Curl_dns_entry **entry)
486+
enum resolve_t Curl_resolv(struct connectdata *conn,
487+
const char *hostname,
488+
int port,
489+
bool allowDOH,
490+
struct Curl_dns_entry **entry)
491491
{
492492
struct Curl_dns_entry *dns = NULL;
493493
struct Curl_easy *data = conn->data;
494494
CURLcode result;
495-
int rc = CURLRESOLV_ERROR; /* default to failure */
495+
enum resolve_t rc = CURLRESOLV_ERROR; /* default to failure */
496496

497497
*entry = NULL;
498498

@@ -642,11 +642,11 @@ RETSIGTYPE alarmfunc(int sig)
642642
* CURLRESOLV_PENDING (1) = waiting for response, no pointer
643643
*/
644644

645-
int Curl_resolv_timeout(struct connectdata *conn,
646-
const char *hostname,
647-
int port,
648-
struct Curl_dns_entry **entry,
649-
timediff_t timeoutms)
645+
enum resolve_t Curl_resolv_timeout(struct connectdata *conn,
646+
const char *hostname,
647+
int port,
648+
struct Curl_dns_entry **entry,
649+
timediff_t timeoutms)
650650
{
651651
#ifdef USE_ALARM_TIMEOUT
652652
#ifdef HAVE_SIGACTION
@@ -662,7 +662,7 @@ int Curl_resolv_timeout(struct connectdata *conn,
662662
volatile unsigned int prev_alarm = 0;
663663
struct Curl_easy *data = conn->data;
664664
#endif /* USE_ALARM_TIMEOUT */
665-
int rc;
665+
enum resolve_t rc;
666666

667667
*entry = NULL;
668668

lib/hostip.h

+15-12
Original file line numberDiff line numberDiff line change
@@ -79,18 +79,21 @@ struct Curl_dns_entry {
7979
* use, or we'll leak memory!
8080
*/
8181
/* return codes */
82-
#define CURLRESOLV_TIMEDOUT -2
83-
#define CURLRESOLV_ERROR -1
84-
#define CURLRESOLV_RESOLVED 0
85-
#define CURLRESOLV_PENDING 1
86-
int Curl_resolv(struct connectdata *conn,
87-
const char *hostname,
88-
int port,
89-
bool allowDOH,
90-
struct Curl_dns_entry **dnsentry);
91-
int Curl_resolv_timeout(struct connectdata *conn, const char *hostname,
92-
int port, struct Curl_dns_entry **dnsentry,
93-
timediff_t timeoutms);
82+
enum resolve_t {
83+
CURLRESOLV_TIMEDOUT = -2,
84+
CURLRESOLV_ERROR = -1,
85+
CURLRESOLV_RESOLVED = 0,
86+
CURLRESOLV_PENDING = 1
87+
};
88+
enum resolve_t Curl_resolv(struct connectdata *conn,
89+
const char *hostname,
90+
int port,
91+
bool allowDOH,
92+
struct Curl_dns_entry **dnsentry);
93+
enum resolve_t Curl_resolv_timeout(struct connectdata *conn,
94+
const char *hostname, int port,
95+
struct Curl_dns_entry **dnsentry,
96+
timediff_t timeoutms);
9497

9598
#ifdef CURLRES_IPV6
9699
/*

lib/multi.c

+4
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
#include "http_proxy.h"
4848
#include "http2.h"
4949
#include "socketpair.h"
50+
#include "socks.h"
5051
/* The last 3 #include files should be in this order */
5152
#include "curl_printf.h"
5253
#include "curl_memory.h"
@@ -856,6 +857,9 @@ static int waitconnect_getsock(struct connectdata *conn,
856857
return Curl_ssl_getsock(conn, sock);
857858
#endif
858859

860+
if(SOCKS_STATE(conn->cnnct.state))
861+
return Curl_SOCKS_getsock(conn, sock, FIRSTSOCKET);
862+
859863
for(i = 0; i<2; i++) {
860864
if(conn->tempsock[i] != CURL_SOCKET_BAD) {
861865
sock[s] = conn->tempsock[i];

lib/sendf.c

+6-5
Original file line numberDiff line numberDiff line change
@@ -692,19 +692,20 @@ CURLcode Curl_read_plain(curl_socket_t sockfd,
692692
ssize_t nread = sread(sockfd, buf, bytesfromsocket);
693693

694694
if(-1 == nread) {
695-
int err = SOCKERRNO;
696-
int return_error;
695+
const int err = SOCKERRNO;
696+
const bool return_error =
697697
#ifdef USE_WINSOCK
698-
return_error = WSAEWOULDBLOCK == err;
698+
WSAEWOULDBLOCK == err
699699
#else
700-
return_error = EWOULDBLOCK == err || EAGAIN == err || EINTR == err;
700+
EWOULDBLOCK == err || EAGAIN == err || EINTR == err
701701
#endif
702+
;
703+
*n = 0; /* no data returned */
702704
if(return_error)
703705
return CURLE_AGAIN;
704706
return CURLE_RECV_ERROR;
705707
}
706708

707-
/* we only return number of bytes read when we return OK */
708709
*n = nread;
709710
return CURLE_OK;
710711
}

0 commit comments

Comments
 (0)