diff options
Diffstat (limited to 'Utilities/cmcurl/lib/connect.c')
-rw-r--r-- | Utilities/cmcurl/lib/connect.c | 241 |
1 files changed, 184 insertions, 57 deletions
diff --git a/Utilities/cmcurl/lib/connect.c b/Utilities/cmcurl/lib/connect.c index 002535b..0a7475c 100644 --- a/Utilities/cmcurl/lib/connect.c +++ b/Utilities/cmcurl/lib/connect.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -75,6 +75,8 @@ #include "conncache.h" #include "multihandle.h" #include "system_win32.h" +#include "quic.h" +#include "socks.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" @@ -165,7 +167,7 @@ tcpkeepalive(struct Curl_easy *data, static CURLcode singleipconnect(struct connectdata *conn, const Curl_addrinfo *ai, /* start connecting to this */ - curl_socket_t *sock); + int sockindex); /* 0 or 1 among the temp ones */ /* * Curl_timeleft() returns the amount of milliseconds left allowed for the @@ -368,6 +370,11 @@ static CURLcode bindlocal(struct connectdata *conn, infof(data, "Name '%s' family %i resolved to '%s' family %i\n", dev, af, myhost, h->addr->ai_family); Curl_resolv_unlock(data, h); + if(af != h->addr->ai_family) { + /* bad IP version combo, signal the caller to try another address + family if available */ + return CURLE_UNSUPPORTED_PROTOCOL; + } done = 1; } else { @@ -590,7 +597,7 @@ static CURLcode trynextip(struct connectdata *conn, } if(ai) { - result = singleipconnect(conn, ai, &conn->tempsock[tempindex]); + result = singleipconnect(conn, ai, tempindex); if(result == CURLE_COULDNT_CONNECT) { ai = ai->ai_next; continue; @@ -620,13 +627,10 @@ void Curl_persistconninfo(struct connectdata *conn) conn->data->info.conn_local_port = conn->local_port; } -UNITTEST bool getaddressinfo(struct sockaddr *sa, char *addr, - long *port); - /* retrieves ip address and port from a sockaddr structure. note it calls Curl_inet_ntop which sets errno on fail, not SOCKERRNO. */ -UNITTEST bool getaddressinfo(struct sockaddr *sa, char *addr, - long *port) +bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen, + char *addr, long *port) { struct sockaddr_in *si = NULL; #ifdef ENABLE_IPV6 @@ -634,6 +638,8 @@ UNITTEST bool getaddressinfo(struct sockaddr *sa, char *addr, #endif #if defined(HAVE_SYS_UN_H) && defined(AF_UNIX) struct sockaddr_un *su = NULL; +#else + (void)salen; #endif switch(sa->sa_family) { @@ -659,8 +665,12 @@ UNITTEST bool getaddressinfo(struct sockaddr *sa, char *addr, #endif #if defined(HAVE_SYS_UN_H) && defined(AF_UNIX) case AF_UNIX: - su = (struct sockaddr_un*)sa; - msnprintf(addr, MAX_IPADR_LEN, "%s", su->sun_path); + if(salen > (curl_socklen_t)sizeof(sa_family_t)) { + su = (struct sockaddr_un*)sa; + msnprintf(addr, MAX_IPADR_LEN, "%s", su->sun_path); + } + else + addr[0] = 0; /* socket with no name */ *port = 0; return TRUE; #endif @@ -678,8 +688,8 @@ UNITTEST bool getaddressinfo(struct sockaddr *sa, char *addr, connection */ void Curl_updateconninfo(struct connectdata *conn, curl_socket_t sockfd) { - if(conn->socktype == SOCK_DGRAM) - /* there's no connection! */ + if(conn->transport != TRNSPRT_TCP) + /* there's no TCP connection! */ return; #if defined(HAVE_GETPEERNAME) || defined(HAVE_GETSOCKNAME) @@ -688,10 +698,11 @@ void Curl_updateconninfo(struct connectdata *conn, curl_socket_t sockfd) char buffer[STRERROR_LEN]; struct Curl_sockaddr_storage ssrem; struct Curl_sockaddr_storage ssloc; - curl_socklen_t len; + curl_socklen_t plen; + curl_socklen_t slen; #ifdef HAVE_GETPEERNAME - len = sizeof(struct Curl_sockaddr_storage); - if(getpeername(sockfd, (struct sockaddr*) &ssrem, &len)) { + plen = sizeof(struct Curl_sockaddr_storage); + if(getpeername(sockfd, (struct sockaddr*) &ssrem, &plen)) { int error = SOCKERRNO; failf(data, "getpeername() failed with errno %d: %s", error, Curl_strerror(error, buffer, sizeof(buffer))); @@ -699,9 +710,9 @@ void Curl_updateconninfo(struct connectdata *conn, curl_socket_t sockfd) } #endif #ifdef HAVE_GETSOCKNAME - len = sizeof(struct Curl_sockaddr_storage); + slen = sizeof(struct Curl_sockaddr_storage); memset(&ssloc, 0, sizeof(ssloc)); - if(getsockname(sockfd, (struct sockaddr*) &ssloc, &len)) { + if(getsockname(sockfd, (struct sockaddr*) &ssloc, &slen)) { int error = SOCKERRNO; failf(data, "getsockname() failed with errno %d: %s", error, Curl_strerror(error, buffer, sizeof(buffer))); @@ -709,8 +720,8 @@ void Curl_updateconninfo(struct connectdata *conn, curl_socket_t sockfd) } #endif #ifdef HAVE_GETPEERNAME - if(!getaddressinfo((struct sockaddr*)&ssrem, - conn->primary_ip, &conn->primary_port)) { + if(!Curl_addr2string((struct sockaddr*)&ssrem, plen, + conn->primary_ip, &conn->primary_port)) { failf(data, "ssrem inet_ntop() failed with errno %d: %s", errno, Curl_strerror(errno, buffer, sizeof(buffer))); return; @@ -718,8 +729,8 @@ void Curl_updateconninfo(struct connectdata *conn, curl_socket_t sockfd) memcpy(conn->ip_addr_str, conn->primary_ip, MAX_IPADR_LEN); #endif #ifdef HAVE_GETSOCKNAME - if(!getaddressinfo((struct sockaddr*)&ssloc, - conn->local_ip, &conn->local_port)) { + if(!Curl_addr2string((struct sockaddr*)&ssloc, slen, + conn->local_ip, &conn->local_port)) { failf(data, "ssloc inet_ntop() failed with errno %d: %s", errno, Curl_strerror(errno, buffer, sizeof(buffer))); return; @@ -734,6 +745,79 @@ void Curl_updateconninfo(struct connectdata *conn, curl_socket_t sockfd) Curl_persistconninfo(conn); } +/* After a TCP connection to the proxy has been verified, this function does + the next magic steps. If 'done' isn't set TRUE, it is not done yet and + must be called again. + + Note: this function's sub-functions call failf() + +*/ +static CURLcode connect_SOCKS(struct connectdata *conn, int sockindex, + bool *done) +{ + CURLcode result = CURLE_OK; + + if(conn->bits.socksproxy) { +#ifndef CURL_DISABLE_PROXY + /* for the secondary socket (FTP), use the "connect to host" + * but ignore the "connect to port" (use the secondary port) + */ + const char * const host = + conn->bits.httpproxy ? + conn->http_proxy.host.name : + conn->bits.conn_to_host ? + conn->conn_to_host.name : + sockindex == SECONDARYSOCKET ? + conn->secondaryhostname : conn->host.name; + const int port = + conn->bits.httpproxy ? (int)conn->http_proxy.port : + sockindex == SECONDARYSOCKET ? conn->secondary_port : + conn->bits.conn_to_port ? conn->conn_to_port : + conn->remote_port; + switch(conn->socks_proxy.proxytype) { + case CURLPROXY_SOCKS5: + case CURLPROXY_SOCKS5_HOSTNAME: + result = Curl_SOCKS5(conn->socks_proxy.user, conn->socks_proxy.passwd, + host, port, sockindex, conn, done); + break; + + case CURLPROXY_SOCKS4: + case CURLPROXY_SOCKS4A: + result = Curl_SOCKS4(conn->socks_proxy.user, host, port, sockindex, + conn, done); + break; + + default: + failf(conn->data, "unknown proxytype option given"); + result = CURLE_COULDNT_CONNECT; + } /* switch proxytype */ +#else + (void)sockindex; +#endif /* CURL_DISABLE_PROXY */ + } + else + *done = TRUE; /* no SOCKS proxy, so consider us connected */ + + return result; +} + +/* + * post_SOCKS() is called after a successful connect to the peer, which + * *could* be a SOCKS proxy + */ +static void post_SOCKS(struct connectdata *conn, + int sockindex, + bool *connected) +{ + conn->bits.tcpconnect[sockindex] = TRUE; + + *connected = TRUE; + if(sockindex == FIRSTSOCKET) + Curl_pgrsTime(conn->data, TIMER_CONNECT); /* connect done */ + Curl_updateconninfo(conn, conn->sock[sockindex]); + Curl_verboseconnect(conn); +} + /* * Curl_is_connected() checks if the socket has connected. */ @@ -771,11 +855,37 @@ CURLcode Curl_is_connected(struct connectdata *conn, return CURLE_OPERATION_TIMEDOUT; } + if(SOCKS_STATE(conn->cnnct.state)) { + /* still doing SOCKS */ + result = connect_SOCKS(conn, sockindex, connected); + if(!result && *connected) + post_SOCKS(conn, sockindex, connected); + return result; + } + for(i = 0; i<2; i++) { const int other = i ^ 1; if(conn->tempsock[i] == CURL_SOCKET_BAD) continue; +#ifdef ENABLE_QUIC + if(conn->transport == TRNSPRT_QUIC) { + result = Curl_quic_is_connected(conn, i, connected); + if(result) { + error = SOCKERRNO; + goto error; + } + if(*connected) { + /* use this socket from now on */ + conn->sock[sockindex] = conn->tempsock[i]; + conn->ip_addr = conn->tempaddr[i]; + conn->tempsock[i] = CURL_SOCKET_BAD; + connkeep(conn, "HTTP/3 default"); + } + return result; + } +#endif + #ifdef mpeix /* Call this function once now, and ignore the results. We do this to "clear" the error state on the socket so that we can later read it @@ -789,8 +899,8 @@ CURLcode Curl_is_connected(struct connectdata *conn, if(rc == 0) { /* no connection yet */ error = 0; if(Curl_timediff(now, conn->connecttime) >= conn->timeoutms_per_addr) { - infof(data, "After %ldms connect time, move on!\n", - conn->timeoutms_per_addr); + infof(data, "After %" CURL_FORMAT_TIMEDIFF_T + "ms connect time, move on!\n", conn->timeoutms_per_addr); error = ETIMEDOUT; } @@ -819,18 +929,13 @@ CURLcode Curl_is_connected(struct connectdata *conn, conn->tempsock[other] = CURL_SOCKET_BAD; } - /* see if we need to do any proxy magic first once we connected */ - result = Curl_connected_proxy(conn, sockindex); - if(result) + /* see if we need to kick off any SOCKS proxy magic once we + connected */ + result = connect_SOCKS(conn, sockindex, connected); + if(result || !*connected) return result; - conn->bits.tcpconnect[sockindex] = TRUE; - - *connected = TRUE; - if(sockindex == FIRSTSOCKET) - Curl_pgrsTime(data, TIMER_CONNECT); /* connect done */ - Curl_updateconninfo(conn, conn->sock[sockindex]); - Curl_verboseconnect(conn); + post_SOCKS(conn, sockindex, connected); return CURLE_OK; } @@ -839,6 +944,9 @@ CURLcode Curl_is_connected(struct connectdata *conn, else if(rc & CURL_CSELECT_ERR) (void)verifyconnect(conn->tempsock[i], &error); +#ifdef ENABLE_QUIC + error: +#endif /* * The connection failed here, we should attempt to connect to the "next * address" for the given host. But first remember the latest error. @@ -848,19 +956,21 @@ CURLcode Curl_is_connected(struct connectdata *conn, SET_SOCKERRNO(error); if(conn->tempaddr[i]) { CURLcode status; +#ifndef CURL_DISABLE_VERBOSE_STRINGS char ipaddress[MAX_IPADR_LEN]; char buffer[STRERROR_LEN]; Curl_printable_address(conn->tempaddr[i], ipaddress, MAX_IPADR_LEN); +#endif infof(data, "connect to %s port %ld failed: %s\n", ipaddress, conn->port, Curl_strerror(error, buffer, sizeof(buffer))); conn->timeoutms_per_addr = conn->tempaddr[i]->ai_next == NULL ? - allow : allow / 2; + allow : allow / 2; status = trynextip(conn, sockindex, i); - if(status != CURLE_COULDNT_CONNECT - || conn->tempsock[other] == CURL_SOCKET_BAD) + if((status != CURLE_COULDNT_CONNECT) || + conn->tempsock[other] == CURL_SOCKET_BAD) /* the last attempt failed and no other sockets remain open */ result = status; } @@ -892,6 +1002,14 @@ CURLcode Curl_is_connected(struct connectdata *conn, failf(data, "Failed to connect to %s port %ld: %s", hostname, conn->port, Curl_strerror(error, buffer, sizeof(buffer))); + +#ifdef WSAETIMEDOUT + if(WSAETIMEDOUT == data->state.os_errno) + result = CURLE_OPERATION_TIMEDOUT; +#elif defined(ETIMEDOUT) + if(ETIMEDOUT == data->state.os_errno) + result = CURLE_OPERATION_TIMEDOUT; +#endif } return result; @@ -900,14 +1018,12 @@ CURLcode Curl_is_connected(struct connectdata *conn, static void tcpnodelay(struct connectdata *conn, curl_socket_t sockfd) { #if defined(TCP_NODELAY) -#if !defined(CURL_DISABLE_VERBOSE_STRINGS) - struct Curl_easy *data = conn->data; -#endif curl_socklen_t onoff = (curl_socklen_t) 1; int level = IPPROTO_TCP; +#if !defined(CURL_DISABLE_VERBOSE_STRINGS) + struct Curl_easy *data = conn->data; char buffer[STRERROR_LEN]; - -#if defined(CURL_DISABLE_VERBOSE_STRINGS) +#else (void) conn; #endif @@ -915,8 +1031,6 @@ static void tcpnodelay(struct connectdata *conn, curl_socket_t sockfd) sizeof(onoff)) < 0) infof(data, "Could not set TCP_NODELAY: %s\n", Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); - else - infof(data, "TCP_NODELAY set\n"); #else (void)conn; (void)sockfd; @@ -999,7 +1113,7 @@ void Curl_sndbufset(curl_socket_t sockfd) */ static CURLcode singleipconnect(struct connectdata *conn, const Curl_addrinfo *ai, - curl_socket_t *sockp) + int sockindex) { struct Curl_sockaddr_ex addr; int rc = -1; @@ -1015,7 +1129,7 @@ static CURLcode singleipconnect(struct connectdata *conn, int optval = 1; #endif char buffer[STRERROR_LEN]; - + curl_socket_t *sockp = &conn->tempsock[sockindex]; *sockp = CURL_SOCKET_BAD; result = Curl_socket(conn, ai, &addr, &sockfd); @@ -1026,8 +1140,8 @@ static CURLcode singleipconnect(struct connectdata *conn, return CURLE_OK; /* store remote address and port used in this connection attempt */ - if(!getaddressinfo((struct sockaddr*)&addr.sa_addr, - ipaddress, &port)) { + if(!Curl_addr2string((struct sockaddr*)&addr.sa_addr, addr.addrlen, + ipaddress, &port)) { /* malformed address or bug in inet_ntop, try next address */ failf(data, "sa_addr inet_ntop() failed with errno %d: %s", errno, Curl_strerror(errno, buffer, sizeof(buffer))); @@ -1094,8 +1208,8 @@ static CURLcode singleipconnect(struct connectdata *conn, if(conn->num_addr > 1) Curl_expire(data, conn->timeoutms_per_addr, EXPIRE_DNS_PER_NAME); - /* Connect TCP sockets, bind UDP */ - if(!isconnected && (conn->socktype == SOCK_STREAM)) { + /* Connect TCP and QUIC sockets */ + if(!isconnected && (conn->transport != TRNSPRT_UDP)) { if(conn->bits.tcp_fastopen) { #if defined(CONNECT_DATA_IDEMPOTENT) /* Darwin */ # if defined(HAVE_BUILTIN_AVAILABLE) @@ -1124,8 +1238,6 @@ static CURLcode singleipconnect(struct connectdata *conn, if(setsockopt(sockfd, IPPROTO_TCP, TCP_FASTOPEN_CONNECT, (void *)&optval, sizeof(optval)) < 0) infof(data, "Failed to enable TCP Fast Open on fd %d\n", sockfd); - else - infof(data, "TCP_FASTOPEN_CONNECT set\n"); rc = connect(sockfd, &addr.sa_addr, addr.addrlen); #elif defined(MSG_FASTOPEN) /* old Linux */ @@ -1141,6 +1253,16 @@ static CURLcode singleipconnect(struct connectdata *conn, if(-1 == rc) error = SOCKERRNO; +#ifdef ENABLE_QUIC + else if(conn->transport == TRNSPRT_QUIC) { + /* pass in 'sockfd' separately since it hasn't been put into the + tempsock array at this point */ + result = Curl_quic_connect(conn, sockfd, sockindex, + &addr.sa_addr, addr.addrlen); + if(result) + error = SOCKERRNO; + } +#endif } else { *sockp = sockfd; @@ -1214,7 +1336,7 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */ /* start connecting to first IP */ while(conn->tempaddr[0]) { - result = singleipconnect(conn, conn->tempaddr[0], &(conn->tempsock[0])); + result = singleipconnect(conn, conn->tempaddr[0], 0); if(!result) break; conn->tempaddr[0] = conn->tempaddr[0]->ai_next; @@ -1326,12 +1448,11 @@ int Curl_closesocket(struct connectdata *conn, curl_socket_t sock) { if(conn && conn->fclosesocket) { - if((sock == conn->sock[SECONDARYSOCKET]) && - conn->sock_accepted[SECONDARYSOCKET]) + if((sock == conn->sock[SECONDARYSOCKET]) && conn->sock_accepted) /* if this socket matches the second socket, and that was created with accept, then we MUST NOT call the callback but clear the accepted status */ - conn->sock_accepted[SECONDARYSOCKET] = FALSE; + conn->sock_accepted = FALSE; else { int rc; Curl_multi_closed(conn->data, sock); @@ -1381,8 +1502,9 @@ CURLcode Curl_socket(struct connectdata *conn, */ addr->family = ai->ai_family; - addr->socktype = conn->socktype; - addr->protocol = conn->socktype == SOCK_DGRAM?IPPROTO_UDP:ai->ai_protocol; + addr->socktype = (conn->transport == TRNSPRT_TCP) ? SOCK_STREAM : SOCK_DGRAM; + addr->protocol = conn->transport != TRNSPRT_TCP ? IPPROTO_UDP : + ai->ai_protocol; addr->addrlen = ai->ai_addrlen; if(addr->addrlen > sizeof(struct Curl_sockaddr_storage)) @@ -1413,6 +1535,11 @@ CURLcode Curl_socket(struct connectdata *conn, /* no socket, no connection */ return CURLE_COULDNT_CONNECT; + if(conn->transport == TRNSPRT_QUIC) { + /* QUIC sockets need to be nonblocking */ + (void)curlx_nonblock(*sockfd, TRUE); + } + #if defined(ENABLE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID) if(conn->scope_id && (addr->family == AF_INET6)) { struct sockaddr_in6 * const sa6 = (void *)&addr->sa_addr; |