diff options
Diffstat (limited to 'Utilities/cmcurl/lib')
139 files changed, 4887 insertions, 3917 deletions
diff --git a/Utilities/cmcurl/lib/CMakeLists.txt b/Utilities/cmcurl/lib/CMakeLists.txt index 9899b9d..bf25d89 100644 --- a/Utilities/cmcurl/lib/CMakeLists.txt +++ b/Utilities/cmcurl/lib/CMakeLists.txt @@ -85,20 +85,25 @@ endif() return() # The rest of this file is not needed for building within CMake. #----------------------------------------------------------------------------- -add_library( - curlu # special libcurlu library just for unittests - STATIC - EXCLUDE_FROM_ALL - ${HHEADERS} ${CSOURCES} -) -target_compile_definitions(curlu PUBLIC UNITTESTS CURL_STATICLIB) +if(BUILD_TESTING) + add_library( + curlu # special libcurlu library just for unittests + STATIC + EXCLUDE_FROM_ALL + ${HHEADERS} ${CSOURCES} + ) + target_compile_definitions(curlu PUBLIC UNITTESTS CURL_STATICLIB) +endif() if(ENABLE_CURLDEBUG) # We must compile these sources separately to avoid memdebug.h redefinitions # applying to them. set_source_files_properties(memdebug.c curl_multibyte.c PROPERTIES SKIP_UNITY_BUILD_INCLUSION ON) endif() -target_link_libraries(curlu PRIVATE ${CURL_LIBS}) + +if(BUILD_TESTING) + target_link_libraries(curlu PRIVATE ${CURL_LIBS}) +endif() transform_makefile_inc("Makefile.soname" "${CMAKE_CURRENT_BINARY_DIR}/Makefile.soname.cmake") include(${CMAKE_CURRENT_BINARY_DIR}/Makefile.soname.cmake) diff --git a/Utilities/cmcurl/lib/altsvc.c b/Utilities/cmcurl/lib/altsvc.c index 22b0b69..35450d6 100644 --- a/Utilities/cmcurl/lib/altsvc.c +++ b/Utilities/cmcurl/lib/altsvc.c @@ -97,7 +97,7 @@ static struct altsvc *altsvc_createid(const char *srchost, unsigned int srcport, unsigned int dstport) { - struct altsvc *as = calloc(sizeof(struct altsvc), 1); + struct altsvc *as = calloc(1, sizeof(struct altsvc)); size_t hlen; size_t dlen; if(!as) @@ -123,15 +123,13 @@ static struct altsvc *altsvc_createid(const char *srchost, dlen -= 2; } - as->src.host = Curl_memdup(srchost, hlen + 1); + as->src.host = Curl_strndup(srchost, hlen); if(!as->src.host) goto error; - as->src.host[hlen] = 0; - as->dst.host = Curl_memdup(dsthost, dlen + 1); + as->dst.host = Curl_strndup(dsthost, dlen); if(!as->dst.host) goto error; - as->dst.host[dlen] = 0; as->src.alpnid = srcalpnid; as->dst.alpnid = dstalpnid; @@ -301,7 +299,7 @@ static CURLcode altsvc_out(struct altsvc *as, FILE *fp) */ struct altsvcinfo *Curl_altsvc_init(void) { - struct altsvcinfo *asi = calloc(sizeof(struct altsvcinfo), 1); + struct altsvcinfo *asi = calloc(1, sizeof(struct altsvcinfo)); if(!asi) return NULL; Curl_llist_init(&asi->list, NULL); diff --git a/Utilities/cmcurl/lib/arpa_telnet.h b/Utilities/cmcurl/lib/arpa_telnet.h index de13738..228b446 100644 --- a/Utilities/cmcurl/lib/arpa_telnet.h +++ b/Utilities/cmcurl/lib/arpa_telnet.h @@ -56,12 +56,14 @@ static const char * const telnetoptions[]= "TERM SPEED", "LFLOW", "LINEMODE", "XDISPLOC", "OLD-ENVIRON", "AUTHENTICATION", "ENCRYPT", "NEW-ENVIRON" }; +#define CURL_TELOPT(x) telnetoptions[x] +#else +#define CURL_TELOPT(x) "" #endif #define CURL_TELOPT_MAXIMUM CURL_TELOPT_NEW_ENVIRON #define CURL_TELOPT_OK(x) ((x) <= CURL_TELOPT_MAXIMUM) -#define CURL_TELOPT(x) telnetoptions[x] #define CURL_NTELOPTS 40 @@ -103,7 +105,12 @@ static const char * const telnetcmds[]= #define CURL_TELCMD_OK(x) ( ((unsigned int)(x) >= CURL_TELCMD_MINIMUM) && \ ((unsigned int)(x) <= CURL_TELCMD_MAXIMUM) ) + +#ifndef CURL_DISABLE_VERBOSE_STRINGS #define CURL_TELCMD(x) telnetcmds[(x)-CURL_TELCMD_MINIMUM] +#else +#define CURL_TELCMD(x) "" +#endif #endif /* CURL_DISABLE_TELNET */ diff --git a/Utilities/cmcurl/lib/asyn-ares.c b/Utilities/cmcurl/lib/asyn-ares.c index e73e41d..437c933 100644 --- a/Utilities/cmcurl/lib/asyn-ares.c +++ b/Utilities/cmcurl/lib/asyn-ares.c @@ -60,13 +60,13 @@ #include "progress.h" #include "timediff.h" -# if defined(CURL_STATICLIB) && !defined(CARES_STATICLIB) && \ - defined(WIN32) -# define CARES_STATICLIB -# endif -# include <ares.h> -# include <ares_version.h> /* really old c-ares didn't include this by - itself */ +#if defined(CURL_STATICLIB) && !defined(CARES_STATICLIB) && \ + defined(_WIN32) +# define CARES_STATICLIB +#endif +#include <ares.h> +#include <ares_version.h> /* really old c-ares didn't include this by + itself */ #if ARES_VERSION >= 0x010500 /* c-ares 1.5.0 or later, the callback proto is modified */ @@ -228,9 +228,9 @@ static void destroy_async_data(struct Curl_async *async); void Curl_resolver_cancel(struct Curl_easy *data) { DEBUGASSERT(data); - if(data->state.async.resolver) - ares_cancel((ares_channel)data->state.async.resolver); - destroy_async_data(&data->state.async); + if(data->conn->resolve_async.resolver) + ares_cancel((ares_channel)data->conn->resolve_async.resolver); + destroy_async_data(&data->conn->resolve_async); } /* @@ -278,14 +278,14 @@ int Curl_resolver_getsock(struct Curl_easy *data, struct timeval timebuf; struct timeval *timeout; long milli; - int max = ares_getsock((ares_channel)data->state.async.resolver, + int max = ares_getsock((ares_channel)data->conn->resolve_async.resolver, (ares_socket_t *)socks, MAX_SOCKSPEREASYHANDLE); maxtime.tv_sec = CURL_TIMEOUT_RESOLVE; maxtime.tv_usec = 0; - timeout = ares_timeout((ares_channel)data->state.async.resolver, &maxtime, - &timebuf); + timeout = ares_timeout((ares_channel)data->conn->resolve_async.resolver, + &maxtime, &timebuf); milli = (long)curlx_tvtoms(timeout); if(milli == 0) milli += 10; @@ -313,8 +313,8 @@ static int waitperform(struct Curl_easy *data, timediff_t timeout_ms) int i; int num = 0; - bitmask = ares_getsock((ares_channel)data->state.async.resolver, socks, - ARES_GETSOCK_MAXNUM); + bitmask = ares_getsock((ares_channel)data->conn->resolve_async.resolver, + socks, ARES_GETSOCK_MAXNUM); for(i = 0; i < ARES_GETSOCK_MAXNUM; i++) { pfd[i].events = 0; @@ -344,12 +344,12 @@ static int waitperform(struct Curl_easy *data, timediff_t timeout_ms) if(!nfds) /* Call ares_process() unconditionally here, even if we simply timed out above, as otherwise the ares name resolve won't timeout! */ - ares_process_fd((ares_channel)data->state.async.resolver, ARES_SOCKET_BAD, - ARES_SOCKET_BAD); + ares_process_fd((ares_channel)data->conn->resolve_async.resolver, + ARES_SOCKET_BAD, ARES_SOCKET_BAD); else { /* move through the descriptors and ask for processing on them */ for(i = 0; i < num; i++) - ares_process_fd((ares_channel)data->state.async.resolver, + ares_process_fd((ares_channel)data->conn->resolve_async.resolver, (pfd[i].revents & (POLLRDNORM|POLLIN))? pfd[i].fd:ARES_SOCKET_BAD, (pfd[i].revents & (POLLWRNORM|POLLOUT))? @@ -368,7 +368,7 @@ static int waitperform(struct Curl_easy *data, timediff_t timeout_ms) CURLcode Curl_resolver_is_resolved(struct Curl_easy *data, struct Curl_dns_entry **dns) { - struct thread_data *res = data->state.async.tdata; + struct thread_data *res = data->conn->resolve_async.tdata; CURLcode result = CURLE_OK; DEBUGASSERT(dns); @@ -397,7 +397,7 @@ CURLcode Curl_resolver_is_resolved(struct Curl_easy *data, ARES_ECANCELLED synchronously for all pending responses. This will leave us with res->num_pending == 0, which is perfect for the next block. */ - ares_cancel((ares_channel)data->state.async.resolver); + ares_cancel((ares_channel)data->conn->resolve_async.resolver); DEBUGASSERT(res->num_pending == 0); } #endif @@ -408,12 +408,12 @@ CURLcode Curl_resolver_is_resolved(struct Curl_easy *data, them */ res->temp_ai = NULL; - if(!data->state.async.dns) + if(!data->conn->resolve_async.dns) result = Curl_resolver_error(data); else - *dns = data->state.async.dns; + *dns = data->conn->resolve_async.dns; - destroy_async_data(&data->state.async); + destroy_async_data(&data->conn->resolve_async); } return result; @@ -464,7 +464,8 @@ CURLcode Curl_resolver_wait_resolv(struct Curl_easy *data, store.tv_sec = itimeout/1000; store.tv_usec = (itimeout%1000)*1000; - tvp = ares_timeout((ares_channel)data->state.async.resolver, &store, &tv); + tvp = ares_timeout((ares_channel)data->conn->resolve_async.resolver, + &store, &tv); /* use the timeout period ares returned to us above if less than one second is left, otherwise just use 1000ms to make sure the progress @@ -478,7 +479,7 @@ CURLcode Curl_resolver_wait_resolv(struct Curl_easy *data, return CURLE_UNRECOVERABLE_POLL; result = Curl_resolver_is_resolved(data, entry); - if(result || data->state.async.done) + if(result || data->conn->resolve_async.done) break; if(Curl_pgrsUpdate(data)) @@ -499,12 +500,12 @@ CURLcode Curl_resolver_wait_resolv(struct Curl_easy *data, } if(result) /* failure, so we cancel the ares operation */ - ares_cancel((ares_channel)data->state.async.resolver); + ares_cancel((ares_channel)data->conn->resolve_async.resolver); /* Operation complete, if the lookup was successful we now have the entry in the cache. */ if(entry) - *entry = data->state.async.dns; + *entry = data->conn->resolve_async.dns; if(result) /* close the connection, since we can't return failure here without @@ -571,12 +572,13 @@ static void query_completed_cb(void *arg, /* (struct connectdata *) */ be valid so only defer it when we know the 'status' says its fine! */ return; - res = data->state.async.tdata; + res = data->conn->resolve_async.tdata; if(res) { res->num_pending--; if(CURL_ASYNC_SUCCESS == status) { - struct Curl_addrinfo *ai = Curl_he2ai(hostent, data->state.async.port); + struct Curl_addrinfo *ai = Curl_he2ai(hostent, + data->conn->resolve_async.port); if(ai) { compound_results(res, ai); } @@ -727,14 +729,16 @@ static void addrinfo_cb(void *arg, int status, int timeouts, struct ares_addrinfo *result) { struct Curl_easy *data = (struct Curl_easy *)arg; - struct thread_data *res = data->state.async.tdata; - (void)timeouts; - if(ARES_SUCCESS == status) { - res->temp_ai = ares2addr(result->nodes); - res->last_status = CURL_ASYNC_SUCCESS; - ares_freeaddrinfo(result); + if(data->conn) { + struct thread_data *res = data->conn->resolve_async.tdata; + (void)timeouts; + if(ARES_SUCCESS == status) { + res->temp_ai = ares2addr(result->nodes); + res->last_status = CURL_ASYNC_SUCCESS; + ares_freeaddrinfo(result); + } + res->num_pending--; } - res->num_pending--; } #endif @@ -755,15 +759,15 @@ struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data, size_t namelen = strlen(hostname); *waitp = 0; /* default to synchronous response */ - res = calloc(sizeof(struct thread_data) + namelen, 1); + res = calloc(1, sizeof(struct thread_data) + namelen); if(res) { strcpy(res->hostname, hostname); - data->state.async.hostname = res->hostname; - data->state.async.port = port; - data->state.async.done = FALSE; /* not done */ - data->state.async.status = 0; /* clear */ - data->state.async.dns = NULL; /* clear */ - data->state.async.tdata = res; + data->conn->resolve_async.hostname = res->hostname; + data->conn->resolve_async.port = port; + data->conn->resolve_async.done = FALSE; /* not done */ + data->conn->resolve_async.status = 0; /* clear */ + data->conn->resolve_async.dns = NULL; /* clear */ + data->conn->resolve_async.tdata = res; /* initial status - failed */ res->last_status = ARES_ENOTFOUND; @@ -793,8 +797,8 @@ struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data, hints.ai_flags = ARES_AI_NUMERICSERV; msnprintf(service, sizeof(service), "%d", port); res->num_pending = 1; - ares_getaddrinfo((ares_channel)data->state.async.resolver, hostname, - service, &hints, addrinfo_cb, data); + ares_getaddrinfo((ares_channel)data->conn->resolve_async.resolver, + hostname, service, &hints, addrinfo_cb, data); } #else @@ -804,10 +808,10 @@ struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data, res->num_pending = 2; /* areschannel is already setup in the Curl_open() function */ - ares_gethostbyname((ares_channel)data->state.async.resolver, hostname, - PF_INET, query_completed_cb, data); - ares_gethostbyname((ares_channel)data->state.async.resolver, hostname, - PF_INET6, query_completed_cb, data); + ares_gethostbyname((ares_channel)data->conn->resolve_async.resolver, + hostname, PF_INET, query_completed_cb, data); + ares_gethostbyname((ares_channel)data->conn->resolve_async.resolver, + hostname, PF_INET6, query_completed_cb, data); } else #endif @@ -815,7 +819,7 @@ struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data, res->num_pending = 1; /* areschannel is already setup in the Curl_open() function */ - ares_gethostbyname((ares_channel)data->state.async.resolver, + ares_gethostbyname((ares_channel)data->conn->resolve_async.resolver, hostname, PF_INET, query_completed_cb, data); } @@ -829,6 +833,7 @@ CURLcode Curl_set_dns_servers(struct Curl_easy *data, char *servers) { CURLcode result = CURLE_NOT_BUILT_IN; + ares_channel channel, lchannel = NULL; int ares_result; /* If server is NULL or empty, this would purge all DNS servers @@ -841,11 +846,23 @@ CURLcode Curl_set_dns_servers(struct Curl_easy *data, return CURLE_OK; #ifdef HAVE_CARES_SERVERS_CSV + if(data->conn) + channel = data->conn->resolve_async.resolver; + else { + /* we are called by setopt on a data without a connection (yet). In that + * case we set the value on a local instance for checking. + * The configured data options are set when the connection for this + * transfer is created. */ + result = Curl_resolver_init(data, (void **)&lchannel); + if(result) + goto out; + channel = lchannel; + } + #ifdef HAVE_CARES_PORTS_CSV - ares_result = ares_set_servers_ports_csv(data->state.async.resolver, - servers); + ares_result = ares_set_servers_ports_csv(channel, servers); #else - ares_result = ares_set_servers_csv(data->state.async.resolver, servers); + ares_result = ares_set_servers_csv(channel, servers); #endif switch(ares_result) { case ARES_SUCCESS: @@ -861,6 +878,9 @@ CURLcode Curl_set_dns_servers(struct Curl_easy *data, result = CURLE_BAD_FUNCTION_ARGUMENT; break; } +out: + if(lchannel) + Curl_resolver_cleanup(lchannel); #else /* too old c-ares version! */ (void)data; (void)(ares_result); @@ -872,11 +892,14 @@ CURLcode Curl_set_dns_interface(struct Curl_easy *data, const char *interf) { #ifdef HAVE_CARES_LOCAL_DEV - if(!interf) - interf = ""; - - ares_set_local_dev((ares_channel)data->state.async.resolver, interf); + if(data->conn) { + /* not a setopt test run, set the value */ + if(!interf) + interf = ""; + ares_set_local_dev((ares_channel)data->conn->resolve_async.resolver, + interf); + } return CURLE_OK; #else /* c-ares version too old! */ (void)data; @@ -900,8 +923,11 @@ CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data, } } - ares_set_local_ip4((ares_channel)data->state.async.resolver, - ntohl(a4.s_addr)); + if(data->conn) { + /* not a setopt test run, set the value */ + ares_set_local_ip4((ares_channel)data->conn->resolve_async.resolver, + ntohl(a4.s_addr)); + } return CURLE_OK; #else /* c-ares version too old! */ @@ -927,7 +953,10 @@ CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data, } } - ares_set_local_ip6((ares_channel)data->state.async.resolver, a6); + if(data->conn) { + /* not a setopt test run, set the value */ + ares_set_local_ip6((ares_channel)data->conn->resolve_async.resolver, a6); + } return CURLE_OK; #else /* c-ares version too old! */ diff --git a/Utilities/cmcurl/lib/asyn-thread.c b/Utilities/cmcurl/lib/asyn-thread.c index a2e294f..63414b6 100644 --- a/Utilities/cmcurl/lib/asyn-thread.c +++ b/Utilities/cmcurl/lib/asyn-thread.c @@ -136,7 +136,7 @@ static void destroy_async_data(struct Curl_async *); */ void Curl_resolver_cancel(struct Curl_easy *data) { - destroy_async_data(&data->state.async); + destroy_async_data(&data->conn->resolve_async); } /* This function is used to init a threaded resolve */ @@ -173,7 +173,7 @@ struct thread_data { static struct thread_sync_data *conn_thread_sync_data(struct Curl_easy *data) { - return &(data->state.async.tdata->tsd); + return &(data->conn->resolve_async.tdata->tsd); } /* Destroy resolver thread synchronization data */ @@ -196,7 +196,7 @@ void destroy_thread_sync_data(struct thread_sync_data *tsd) * the other end (for reading) is always closed in the parent thread. */ if(tsd->sock_pair[1] != CURL_SOCKET_BAD) { - sclose(tsd->sock_pair[1]); + wakeup_close(tsd->sock_pair[1]); } #endif memset(tsd, 0, sizeof(*tsd)); @@ -233,8 +233,8 @@ int init_thread_sync_data(struct thread_data *td, Curl_mutex_init(tsd->mtx); #ifndef CURL_DISABLE_SOCKETPAIR - /* create socket pair, avoid AF_LOCAL since it doesn't build on Solaris */ - if(Curl_socketpair(AF_UNIX, SOCK_STREAM, 0, &tsd->sock_pair[0]) < 0) { + /* create socket pair or pipe */ + if(wakeup_create(&tsd->sock_pair[0]) < 0) { tsd->sock_pair[0] = CURL_SOCKET_BAD; tsd->sock_pair[1] = CURL_SOCKET_BAD; goto err_exit; @@ -254,7 +254,7 @@ int init_thread_sync_data(struct thread_data *td, err_exit: #ifndef CURL_DISABLE_SOCKETPAIR if(tsd->sock_pair[0] != CURL_SOCKET_BAD) { - sclose(tsd->sock_pair[0]); + wakeup_close(tsd->sock_pair[0]); tsd->sock_pair[0] = CURL_SOCKET_BAD; } #endif @@ -320,7 +320,7 @@ static unsigned int CURL_STDCALL getaddrinfo_thread(void *arg) if(tsd->sock_pair[1] != CURL_SOCKET_BAD) { /* DNS has been resolved, signal client task */ buf[0] = 1; - if(swrite(tsd->sock_pair[1], buf, sizeof(buf)) < 0) { + if(wakeup_write(tsd->sock_pair[1], buf, sizeof(buf)) < 0) { /* update sock_erro to errno */ tsd->sock_error = SOCKERRNO; } @@ -428,9 +428,9 @@ static bool init_resolve_thread(struct Curl_easy *data, { struct thread_data *td = calloc(1, sizeof(struct thread_data)); int err = ENOMEM; - struct Curl_async *asp = &data->state.async; + struct Curl_async *asp = &data->conn->resolve_async; - data->state.async.tdata = td; + data->conn->resolve_async.tdata = td; if(!td) goto errno_exit; @@ -488,7 +488,7 @@ static CURLcode thread_wait_resolv(struct Curl_easy *data, CURLcode result = CURLE_OK; DEBUGASSERT(data); - td = data->state.async.tdata; + td = data->conn->resolve_async.tdata; DEBUGASSERT(td); DEBUGASSERT(td->thread_hnd != curl_thread_t_null); @@ -500,18 +500,18 @@ static CURLcode thread_wait_resolv(struct Curl_easy *data, else DEBUGASSERT(0); - data->state.async.done = TRUE; + data->conn->resolve_async.done = TRUE; if(entry) - *entry = data->state.async.dns; + *entry = data->conn->resolve_async.dns; - if(!data->state.async.dns && report) + if(!data->conn->resolve_async.dns && report) /* a name was not resolved, report error */ result = Curl_resolver_error(data); - destroy_async_data(&data->state.async); + destroy_async_data(&data->conn->resolve_async); - if(!data->state.async.dns && report) + if(!data->conn->resolve_async.dns && report) connclose(data->conn, "asynch resolve failed"); return result; @@ -524,7 +524,7 @@ static CURLcode thread_wait_resolv(struct Curl_easy *data, */ void Curl_resolver_kill(struct Curl_easy *data) { - struct thread_data *td = data->state.async.tdata; + struct thread_data *td = data->conn->resolve_async.tdata; /* If we're still resolving, we must wait for the threads to fully clean up, unfortunately. Otherwise, we can simply cancel to clean up any resolver @@ -563,7 +563,7 @@ CURLcode Curl_resolver_wait_resolv(struct Curl_easy *data, CURLcode Curl_resolver_is_resolved(struct Curl_easy *data, struct Curl_dns_entry **entry) { - struct thread_data *td = data->state.async.tdata; + struct thread_data *td = data->conn->resolve_async.tdata; int done = 0; DEBUGASSERT(entry); @@ -581,13 +581,13 @@ CURLcode Curl_resolver_is_resolved(struct Curl_easy *data, if(done) { getaddrinfo_complete(data); - if(!data->state.async.dns) { + if(!data->conn->resolve_async.dns) { CURLcode result = Curl_resolver_error(data); - destroy_async_data(&data->state.async); + destroy_async_data(&data->conn->resolve_async); return result; } - destroy_async_data(&data->state.async); - *entry = data->state.async.dns; + destroy_async_data(&data->conn->resolve_async); + *entry = data->conn->resolve_async.dns; } else { /* poll for name lookup done with exponential backoff up to 250ms */ @@ -619,9 +619,9 @@ int Curl_resolver_getsock(struct Curl_easy *data, curl_socket_t *socks) int ret_val = 0; timediff_t milli; timediff_t ms; - struct resdata *reslv = (struct resdata *)data->state.async.resolver; + struct resdata *reslv = (struct resdata *)data->conn->resolve_async.resolver; #ifndef CURL_DISABLE_SOCKETPAIR - struct thread_data *td = data->state.async.tdata; + struct thread_data *td = data->conn->resolve_async.tdata; #else (void)socks; #endif @@ -662,7 +662,7 @@ struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data, int port, int *waitp) { - struct resdata *reslv = (struct resdata *)data->state.async.resolver; + struct resdata *reslv = (struct resdata *)data->conn->resolve_async.resolver; *waitp = 0; /* default to synchronous response */ @@ -691,7 +691,7 @@ struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data, { struct addrinfo hints; int pf = PF_INET; - struct resdata *reslv = (struct resdata *)data->state.async.resolver; + struct resdata *reslv = (struct resdata *)data->conn->resolve_async.resolver; *waitp = 0; /* default to synchronous response */ diff --git a/Utilities/cmcurl/lib/base64.c b/Utilities/cmcurl/lib/base64.c index 2a49b5a..919eb62 100644 --- a/Utilities/cmcurl/lib/base64.c +++ b/Utilities/cmcurl/lib/base64.c @@ -31,6 +31,7 @@ !defined(CURL_DISABLE_SMTP) || \ !defined(CURL_DISABLE_POP3) || \ !defined(CURL_DISABLE_IMAP) || \ + !defined(CURL_DISABLE_DIGEST_AUTH) || \ !defined(CURL_DISABLE_DOH) || defined(USE_SSL) || defined(BUILDING_CURL) #include "curl/curl.h" #include "warnless.h" diff --git a/Utilities/cmcurl/lib/c-hyper.c b/Utilities/cmcurl/lib/c-hyper.c index 5726ff1..787d6bb 100644 --- a/Utilities/cmcurl/lib/c-hyper.c +++ b/Utilities/cmcurl/lib/c-hyper.c @@ -22,6 +22,10 @@ * ***************************************************************************/ +/* Curl's integration with Hyper. This replaces certain functions in http.c, + * based on configuration #defines. This implementation supports HTTP/1.1 but + * not HTTP/2. + */ #include "curl_setup.h" #if !defined(CURL_DISABLE_HTTP) && defined(USE_HYPER) @@ -172,17 +176,15 @@ static int hyper_each_header(void *userdata, Curl_debug(data, CURLINFO_HEADER_IN, headp, len); - if(!data->state.hconnect || !data->set.suppress_connect_headers) { - writetype = CLIENTWRITE_HEADER; - if(data->state.hconnect) - writetype |= CLIENTWRITE_CONNECT; - if(data->req.httpcode/100 == 1) - writetype |= CLIENTWRITE_1XX; - result = Curl_client_write(data, writetype, headp, len); - if(result) { - data->state.hresult = CURLE_ABORTED_BY_CALLBACK; - return HYPER_ITER_BREAK; - } + writetype = CLIENTWRITE_HEADER; + if(data->state.hconnect) + writetype |= CLIENTWRITE_CONNECT; + if(data->req.httpcode/100 == 1) + writetype |= CLIENTWRITE_1XX; + result = Curl_client_write(data, writetype, headp, len); + if(result) { + data->state.hresult = CURLE_ABORTED_BY_CALLBACK; + return HYPER_ITER_BREAK; } result = Curl_bump_headersize(data, len, FALSE); @@ -201,7 +203,7 @@ static int hyper_body_chunk(void *userdata, const hyper_buf *chunk) struct SingleRequest *k = &data->req; CURLcode result = CURLE_OK; - if(0 == k->bodywrites++) { + if(0 == k->bodywrites) { bool done = FALSE; #if defined(USE_NTLM) struct connectdata *conn = data->conn; @@ -241,11 +243,6 @@ static int hyper_body_chunk(void *userdata, const hyper_buf *chunk) return HYPER_ITER_BREAK; } } - if(k->ignorebody) - return HYPER_ITER_CONTINUE; - if(0 == len) - return HYPER_ITER_CONTINUE; - Curl_debug(data, CURLINFO_DATA_IN, buf, len); result = Curl_client_write(data, CLIENTWRITE_BODY, buf, len); if(result) { @@ -253,12 +250,6 @@ static int hyper_body_chunk(void *userdata, const hyper_buf *chunk) return HYPER_ITER_BREAK; } - data->req.bytecount += len; - result = Curl_pgrsSetDownloadCounter(data, data->req.bytecount); - if(result) { - data->state.hresult = result; - return HYPER_ITER_BREAK; - } return HYPER_ITER_CONTINUE; } @@ -310,13 +301,14 @@ static CURLcode status_line(struct Curl_easy *data, Curl_debug(data, CURLINFO_HEADER_IN, Curl_dyn_ptr(&data->state.headerb), len); - if(!data->state.hconnect || !data->set.suppress_connect_headers) { - writetype = CLIENTWRITE_HEADER|CLIENTWRITE_STATUS; - result = Curl_client_write(data, writetype, - Curl_dyn_ptr(&data->state.headerb), len); - if(result) - return result; - } + writetype = CLIENTWRITE_HEADER|CLIENTWRITE_STATUS; + if(data->state.hconnect) + writetype |= CLIENTWRITE_CONNECT; + result = Curl_client_write(data, writetype, + Curl_dyn_ptr(&data->state.headerb), len); + if(result) + return result; + result = Curl_bump_headersize(data, len, FALSE); return result; } @@ -551,11 +543,9 @@ CURLcode Curl_hyper_stream(struct Curl_easy *data, static CURLcode debug_request(struct Curl_easy *data, const char *method, - const char *path, - bool h2) + const char *path) { - char *req = aprintf("%s %s HTTP/%s\r\n", method, path, - h2?"2":"1.1"); + char *req = aprintf("%s %s HTTP/1.1\r\n", method, path); if(!req) return CURLE_OUT_OF_MEMORY; Curl_debug(data, CURLINFO_HEADER_OUT, req, strlen(req)); @@ -637,7 +627,6 @@ CURLcode Curl_hyper_header(struct Curl_easy *data, hyper_headers *headers, static CURLcode request_target(struct Curl_easy *data, struct connectdata *conn, const char *method, - bool h2, hyper_request *req) { CURLcode result; @@ -649,26 +638,13 @@ static CURLcode request_target(struct Curl_easy *data, if(result) return result; - if(h2 && hyper_request_set_uri_parts(req, - /* scheme */ - (uint8_t *)data->state.up.scheme, - strlen(data->state.up.scheme), - /* authority */ - (uint8_t *)conn->host.name, - strlen(conn->host.name), - /* path_and_query */ - (uint8_t *)Curl_dyn_uptr(&r), - Curl_dyn_len(&r))) { - failf(data, "error setting uri parts to hyper"); - result = CURLE_OUT_OF_MEMORY; - } - else if(!h2 && hyper_request_set_uri(req, (uint8_t *)Curl_dyn_uptr(&r), + if(hyper_request_set_uri(req, (uint8_t *)Curl_dyn_uptr(&r), Curl_dyn_len(&r))) { failf(data, "error setting uri to hyper"); result = CURLE_OUT_OF_MEMORY; } else - result = debug_request(data, method, Curl_dyn_ptr(&r), h2); + result = debug_request(data, method, Curl_dyn_ptr(&r)); Curl_dyn_free(&r); @@ -899,7 +875,6 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) const char *p_accept; /* Accept: string */ const char *method; Curl_HttpReq httpreq; - bool h2 = FALSE; const char *te = NULL; /* transfer-encoding */ hyper_code rc; @@ -907,6 +882,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) may be parts of the request that is not yet sent, since we can deal with the rest of the request in the PERFORM phase. */ *done = TRUE; + Curl_client_cleanup(data); infof(data, "Time for the Hyper dance"); memset(h, 0, sizeof(struct hyptransfer)); @@ -917,6 +893,8 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) Curl_http_method(data, conn, &method, &httpreq); + DEBUGASSERT(data->req.bytecount == 0); + /* setup the authentication headers */ { char *pq = NULL; @@ -972,8 +950,9 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) goto error; } if(conn->alpn == CURL_HTTP_VERSION_2) { - hyper_clientconn_options_http2(options, 1); - h2 = TRUE; + failf(data, "ALPN protocol h2 not supported with Hyper"); + result = CURLE_UNSUPPORTED_PROTOCOL; + goto error; } hyper_clientconn_options_set_preserve_header_case(options, 1); hyper_clientconn_options_set_preserve_header_order(options, 1); @@ -1024,7 +1003,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) } } else { - if(!h2 && !data->state.disableexpect) { + if(!data->state.disableexpect) { data->state.expect100header = TRUE; } } @@ -1035,7 +1014,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) goto error; } - result = request_target(data, conn, method, h2, req); + result = request_target(data, conn, method, req); if(result) goto error; @@ -1056,19 +1035,10 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) if(result) goto error; - if(!h2) { - if(data->state.aptr.host) { - result = Curl_hyper_header(data, headers, data->state.aptr.host); - if(result) - goto error; - } - } - else { - /* For HTTP/2, we show the Host: header as if we sent it, to make it look - like for HTTP/1 but it isn't actually sent since :authority is then - used. */ - Curl_debug(data, CURLINFO_HEADER_OUT, data->state.aptr.host, - strlen(data->state.aptr.host)); + if(data->state.aptr.host) { + result = Curl_hyper_header(data, headers, data->state.aptr.host); + if(result) + goto error; } if(data->state.aptr.proxyuserpwd) { diff --git a/Utilities/cmcurl/lib/cf-h1-proxy.c b/Utilities/cmcurl/lib/cf-h1-proxy.c index 6748021..2e23b0b 100644 --- a/Utilities/cmcurl/lib/cf-h1-proxy.c +++ b/Utilities/cmcurl/lib/cf-h1-proxy.c @@ -374,7 +374,7 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf, curl_socket_t tunnelsocket = Curl_conn_cf_get_socket(cf, data); char *linep; size_t perline; - int error; + int error, writetype; #define SELECT_OK 0 #define SELECT_ERROR 1 @@ -386,12 +386,12 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf, return CURLE_OK; while(ts->keepon) { - ssize_t gotbytes; + ssize_t nread; char byte; /* Read one byte at a time to avoid a race condition. Wait at most one second before looping to ensure continuous pgrsUpdates. */ - result = Curl_read(data, tunnelsocket, &byte, 1, &gotbytes); + result = Curl_read(data, tunnelsocket, &byte, 1, &nread); if(result == CURLE_AGAIN) /* socket buffer drained, return */ return CURLE_OK; @@ -404,7 +404,7 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf, break; } - if(gotbytes <= 0) { + if(nread <= 0) { if(data->set.proxyauth && data->state.authproxy.avail && data->state.aptr.proxyuserpwd) { /* proxy auth was requested and there was proxy auth available, @@ -437,11 +437,11 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf, properly to know when the end of the body is reached */ CHUNKcode r; CURLcode extra; - ssize_t tookcareof = 0; + size_t consumed = 0; /* now parse the chunked piece of data so that we can properly tell when the stream ends */ - r = Curl_httpchunk_read(data, &byte, 1, &tookcareof, &extra); + r = Curl_httpchunk_read(data, &byte, 1, &consumed, &extra); if(r == CHUNKE_STOP) { /* we're done reading chunks! */ infof(data, "chunk reading DONE"); @@ -467,15 +467,12 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf, /* output debug if that is requested */ Curl_debug(data, CURLINFO_HEADER_IN, linep, perline); - if(!data->set.suppress_connect_headers) { - /* send the header to the callback */ - int writetype = CLIENTWRITE_HEADER | CLIENTWRITE_CONNECT | - (ts->headerlines == 1 ? CLIENTWRITE_STATUS : 0); - - result = Curl_client_write(data, writetype, linep, perline); - if(result) - return result; - } + /* send the header to the callback */ + writetype = CLIENTWRITE_HEADER | CLIENTWRITE_CONNECT | + (ts->headerlines == 1 ? CLIENTWRITE_STATUS : 0); + result = Curl_client_write(data, writetype, linep, perline); + if(result) + return result; result = Curl_bump_headersize(data, perline, TRUE); if(result) @@ -502,6 +499,7 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf, else if(ts->chunked_encoding) { CHUNKcode r; CURLcode extra; + size_t consumed = 0; infof(data, "Ignore chunked response-body"); @@ -516,8 +514,7 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf, /* now parse the chunked piece of data so that we can properly tell when the stream ends */ - r = Curl_httpchunk_read(data, linep + 1, 1, &gotbytes, - &extra); + r = Curl_httpchunk_read(data, linep + 1, 1, &consumed, &extra); if(r == CHUNKE_STOP) { /* we're done reading chunks! */ infof(data, "chunk reading DONE"); @@ -1038,31 +1035,29 @@ out: return result; } -static int cf_h1_proxy_get_select_socks(struct Curl_cfilter *cf, +static void cf_h1_proxy_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data, - curl_socket_t *socks) + struct easy_pollset *ps) { struct h1_tunnel_state *ts = cf->ctx; - int fds; - fds = cf->next->cft->get_select_socks(cf->next, data, socks); - if(!fds && cf->next->connected && !cf->connected) { + if(!cf->connected) { /* If we are not connected, but the filter "below" is * and not waiting on something, we are tunneling. */ - socks[0] = Curl_conn_cf_get_socket(cf, data); + curl_socket_t sock = Curl_conn_cf_get_socket(cf, data); if(ts) { /* when we've sent a CONNECT to a proxy, we should rather either wait for the socket to become readable to be able to get the response headers or if we're still sending the request, wait for write. */ - if(ts->CONNECT.sending == HTTPSEND_REQUEST) { - return GETSOCK_WRITESOCK(0); - } - return GETSOCK_READSOCK(0); + if(ts->CONNECT.sending == HTTPSEND_REQUEST) + Curl_pollset_set_out_only(data, ps, sock); + else + Curl_pollset_set_in_only(data, ps, sock); } - return GETSOCK_WRITESOCK(0); + else + Curl_pollset_set_out_only(data, ps, sock); } - return fds; } static void cf_h1_proxy_destroy(struct Curl_cfilter *cf, @@ -1093,7 +1088,7 @@ struct Curl_cftype Curl_cft_h1_proxy = { cf_h1_proxy_connect, cf_h1_proxy_close, Curl_cf_http_proxy_get_host, - cf_h1_proxy_get_select_socks, + cf_h1_proxy_adjust_pollset, Curl_cf_def_data_pending, Curl_cf_def_send, Curl_cf_def_recv, diff --git a/Utilities/cmcurl/lib/cf-h2-proxy.c b/Utilities/cmcurl/lib/cf-h2-proxy.c index dbc895d..147acdc 100644 --- a/Utilities/cmcurl/lib/cf-h2-proxy.c +++ b/Utilities/cmcurl/lib/cf-h2-proxy.c @@ -688,12 +688,8 @@ static int proxy_h2_on_frame_recv(nghttp2_session *session, * window and *assume* that we treat this like a WINDOW_UPDATE. Some * servers send an explicit WINDOW_UPDATE, but not all seem to do that. * To be safe, we UNHOLD a stream in order not to stall. */ - if((data->req.keepon & KEEP_SEND_HOLD) && - (data->req.keepon & KEEP_SEND)) { - data->req.keepon &= ~KEEP_SEND_HOLD; + if(CURL_WANT_SEND(data)) { drain_tunnel(cf, data, &ctx->tunnel); - CURL_TRC_CF(data, cf, "[%d] un-holding after SETTINGS", - stream_id); } break; case NGHTTP2_GOAWAY: @@ -727,12 +723,8 @@ static int proxy_h2_on_frame_recv(nghttp2_session *session, } break; case NGHTTP2_WINDOW_UPDATE: - if((data->req.keepon & KEEP_SEND_HOLD) && - (data->req.keepon & KEEP_SEND)) { - data->req.keepon &= ~KEEP_SEND_HOLD; - Curl_expire(data, 0, EXPIRE_RUN_NOW); - CURL_TRC_CF(data, cf, "[%d] unpausing after win update", - stream_id); + if(CURL_WANT_SEND(data)) { + drain_tunnel(cf, data, &ctx->tunnel); } break; default: @@ -909,7 +901,6 @@ static CURLcode proxy_h2_submit(int32_t *pstream_id, { struct dynhds h2_headers; nghttp2_nv *nva = NULL; - unsigned int i; int32_t stream_id = -1; size_t nheader; CURLcode result; @@ -920,22 +911,12 @@ static CURLcode proxy_h2_submit(int32_t *pstream_id, if(result) goto out; - nheader = Curl_dynhds_count(&h2_headers); - nva = malloc(sizeof(nghttp2_nv) * nheader); + nva = Curl_dynhds_to_nva(&h2_headers, &nheader); if(!nva) { result = CURLE_OUT_OF_MEMORY; goto out; } - for(i = 0; i < nheader; ++i) { - struct dynhds_entry *e = Curl_dynhds_getn(&h2_headers, i); - nva[i].name = (unsigned char *)e->name; - nva[i].namelen = e->namelen; - nva[i].value = (unsigned char *)e->value; - nva[i].valuelen = e->valuelen; - nva[i].flags = NGHTTP2_NV_FLAG_NONE; - } - if(read_callback) { nghttp2_data_provider data_prd; @@ -1187,25 +1168,31 @@ static bool cf_h2_proxy_data_pending(struct Curl_cfilter *cf, return cf->next? cf->next->cft->has_data_pending(cf->next, data) : FALSE; } -static int cf_h2_proxy_get_select_socks(struct Curl_cfilter *cf, - struct Curl_easy *data, - curl_socket_t *sock) +static void cf_h2_proxy_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) { struct cf_h2_proxy_ctx *ctx = cf->ctx; - int bitmap = GETSOCK_BLANK; - struct cf_call_data save; - - CF_DATA_SAVE(save, cf, data); - sock[0] = Curl_conn_cf_get_socket(cf, data); - bitmap |= GETSOCK_READSOCK(0); + curl_socket_t sock = Curl_conn_cf_get_socket(cf, data); + bool want_recv, want_send; - /* HTTP/2 layer wants to send data) AND there's a window to send data in */ - if(nghttp2_session_want_write(ctx->h2) && - nghttp2_session_get_remote_window_size(ctx->h2)) - bitmap |= GETSOCK_WRITESOCK(0); + Curl_pollset_check(data, ps, sock, &want_recv, &want_send); + if(ctx->h2 && (want_recv || want_send)) { + struct cf_call_data save; + bool c_exhaust, s_exhaust; - CF_DATA_RESTORE(cf, save); - return bitmap; + CF_DATA_SAVE(save, cf, data); + c_exhaust = !nghttp2_session_get_remote_window_size(ctx->h2); + s_exhaust = ctx->tunnel.stream_id >= 0 && + !nghttp2_session_get_stream_remote_window_size( + ctx->h2, ctx->tunnel.stream_id); + want_recv = (want_recv || c_exhaust || s_exhaust); + want_send = (!s_exhaust && want_send) || + (!c_exhaust && nghttp2_session_want_write(ctx->h2)); + + Curl_pollset_set(data, ps, sock, want_recv, want_send); + CF_DATA_RESTORE(cf, save); + } } static ssize_t h2_handle_tunnel_close(struct Curl_cfilter *cf, @@ -1542,7 +1529,7 @@ struct Curl_cftype Curl_cft_h2_proxy = { cf_h2_proxy_connect, cf_h2_proxy_close, Curl_cf_http_proxy_get_host, - cf_h2_proxy_get_select_socks, + cf_h2_proxy_adjust_pollset, cf_h2_proxy_data_pending, cf_h2_proxy_send, cf_h2_proxy_recv, @@ -1560,7 +1547,7 @@ CURLcode Curl_cf_h2_proxy_insert_after(struct Curl_cfilter *cf, CURLcode result = CURLE_OUT_OF_MEMORY; (void)data; - ctx = calloc(sizeof(*ctx), 1); + ctx = calloc(1, sizeof(*ctx)); if(!ctx) goto out; diff --git a/Utilities/cmcurl/lib/cf-haproxy.c b/Utilities/cmcurl/lib/cf-haproxy.c index 39ac415..1ca4393 100644 --- a/Utilities/cmcurl/lib/cf-haproxy.c +++ b/Utilities/cmcurl/lib/cf-haproxy.c @@ -171,23 +171,17 @@ static void cf_haproxy_close(struct Curl_cfilter *cf, cf->next->cft->do_close(cf->next, data); } -static int cf_haproxy_get_select_socks(struct Curl_cfilter *cf, - struct Curl_easy *data, - curl_socket_t *socks) +static void cf_haproxy_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) { - int fds; - - fds = cf->next->cft->get_select_socks(cf->next, data, socks); - if(!fds && cf->next->connected && !cf->connected) { + if(cf->next->connected && !cf->connected) { /* If we are not connected, but the filter "below" is * and not waiting on something, we are sending. */ - socks[0] = Curl_conn_cf_get_socket(cf, data); - return GETSOCK_WRITESOCK(0); + Curl_pollset_set_out_only(data, ps, Curl_conn_cf_get_socket(cf, data)); } - return fds; } - struct Curl_cftype Curl_cft_haproxy = { "HAPROXY", 0, @@ -196,7 +190,7 @@ struct Curl_cftype Curl_cft_haproxy = { cf_haproxy_connect, cf_haproxy_close, Curl_cf_def_get_host, - cf_haproxy_get_select_socks, + cf_haproxy_adjust_pollset, Curl_cf_def_data_pending, Curl_cf_def_send, Curl_cf_def_recv, @@ -214,7 +208,7 @@ static CURLcode cf_haproxy_create(struct Curl_cfilter **pcf, CURLcode result; (void)data; - ctx = calloc(sizeof(*ctx), 1); + ctx = calloc(1, sizeof(*ctx)); if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; diff --git a/Utilities/cmcurl/lib/cf-https-connect.c b/Utilities/cmcurl/lib/cf-https-connect.c index be54aec..b4f33c8 100644 --- a/Utilities/cmcurl/lib/cf-https-connect.c +++ b/Utilities/cmcurl/lib/cf-https-connect.c @@ -188,9 +188,6 @@ static CURLcode baller_connected(struct Curl_cfilter *cf, #endif infof(data, "using HTTP/2"); break; - case CURL_HTTP_VERSION_1_1: - infof(data, "using HTTP/1.1"); - break; default: infof(data, "using HTTP/1.x"); break; @@ -325,42 +322,25 @@ out: return result; } -static int cf_hc_get_select_socks(struct Curl_cfilter *cf, +static void cf_hc_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data, - curl_socket_t *socks) + struct easy_pollset *ps) { - struct cf_hc_ctx *ctx = cf->ctx; - size_t i, j, s; - int brc, rc = GETSOCK_BLANK; - curl_socket_t bsocks[MAX_SOCKSPEREASYHANDLE]; - struct cf_hc_baller *ballers[2]; - - if(cf->connected) - return cf->next->cft->get_select_socks(cf->next, data, socks); - - ballers[0] = &ctx->h3_baller; - ballers[1] = &ctx->h21_baller; - for(i = s = 0; i < sizeof(ballers)/sizeof(ballers[0]); i++) { - struct cf_hc_baller *b = ballers[i]; - if(!cf_hc_baller_is_active(b)) - continue; - brc = Curl_conn_cf_get_select_socks(b->cf, data, bsocks); - CURL_TRC_CF(data, cf, "get_selected_socks(%s) -> %x", b->name, brc); - if(!brc) - continue; - for(j = 0; j < MAX_SOCKSPEREASYHANDLE && s < MAX_SOCKSPEREASYHANDLE; ++j) { - if((brc & GETSOCK_WRITESOCK(j)) || (brc & GETSOCK_READSOCK(j))) { - socks[s] = bsocks[j]; - if(brc & GETSOCK_WRITESOCK(j)) - rc |= GETSOCK_WRITESOCK(s); - if(brc & GETSOCK_READSOCK(j)) - rc |= GETSOCK_READSOCK(s); - s++; - } + if(!cf->connected) { + struct cf_hc_ctx *ctx = cf->ctx; + struct cf_hc_baller *ballers[2]; + size_t i; + + ballers[0] = &ctx->h3_baller; + ballers[1] = &ctx->h21_baller; + for(i = 0; i < sizeof(ballers)/sizeof(ballers[0]); i++) { + struct cf_hc_baller *b = ballers[i]; + if(!cf_hc_baller_is_active(b)) + continue; + Curl_conn_cf_adjust_pollset(b->cf, data, ps); } + CURL_TRC_CF(data, cf, "adjust_pollset -> %d socks", ps->num); } - CURL_TRC_CF(data, cf, "get_selected_socks -> %x", rc); - return rc; } static bool cf_hc_data_pending(struct Curl_cfilter *cf, @@ -455,7 +435,7 @@ struct Curl_cftype Curl_cft_http_connect = { cf_hc_connect, cf_hc_close, Curl_cf_def_get_host, - cf_hc_get_select_socks, + cf_hc_adjust_pollset, cf_hc_data_pending, Curl_cf_def_send, Curl_cf_def_recv, @@ -475,7 +455,7 @@ static CURLcode cf_hc_create(struct Curl_cfilter **pcf, CURLcode result = CURLE_OK; (void)data; - ctx = calloc(sizeof(*ctx), 1); + ctx = calloc(1, sizeof(*ctx)); if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; diff --git a/Utilities/cmcurl/lib/cf-socket.c b/Utilities/cmcurl/lib/cf-socket.c index ce3f9e9..e42b4a8 100644 --- a/Utilities/cmcurl/lib/cf-socket.c +++ b/Utilities/cmcurl/lib/cf-socket.c @@ -81,7 +81,7 @@ #include "memdebug.h" -#if defined(ENABLE_IPV6) && defined(IPV6_V6ONLY) && defined(WIN32) +#if defined(ENABLE_IPV6) && defined(IPV6_V6ONLY) && defined(_WIN32) /* It makes support for IPv4-mapped IPv6 addresses. * Linux kernel, NetBSD, FreeBSD and Darwin: default is off; * Windows Vista and later: default is on; @@ -102,11 +102,7 @@ static void tcpnodelay(struct Curl_easy *data, curl_socket_t sockfd) #if defined(TCP_NODELAY) curl_socklen_t onoff = (curl_socklen_t) 1; int level = IPPROTO_TCP; -#if !defined(CURL_DISABLE_VERBOSE_STRINGS) char buffer[STRERROR_LEN]; -#else - (void) data; -#endif if(setsockopt(sockfd, level, TCP_NODELAY, (void *)&onoff, sizeof(onoff)) < 0) @@ -127,6 +123,7 @@ static void nosigpipe(struct Curl_easy *data, curl_socket_t sockfd) { int onoff = 1; + (void)data; if(setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&onoff, sizeof(onoff)) < 0) { #if !defined(CURL_DISABLE_VERBOSE_STRINGS) @@ -662,7 +659,7 @@ static bool verifyconnect(curl_socket_t sockfd, int *error) int err = 0; curl_socklen_t errSize = sizeof(err); -#ifdef WIN32 +#ifdef _WIN32 /* * In October 2003 we effectively nullified this function on Windows due to * problems with it using all CPU in multi-threaded cases. @@ -883,34 +880,14 @@ static void cf_socket_close(struct Curl_cfilter *cf, struct Curl_easy *data) struct cf_socket_ctx *ctx = cf->ctx; if(ctx && CURL_SOCKET_BAD != ctx->sock) { - if(ctx->active) { - /* We share our socket at cf->conn->sock[cf->sockindex] when active. - * If it is no longer there, someone has stolen (and hopefully - * closed it) and we just forget about it. - */ - if(ctx->sock == cf->conn->sock[cf->sockindex]) { - CURL_TRC_CF(data, cf, "cf_socket_close(%" CURL_FORMAT_SOCKET_T - ", active)", ctx->sock); - socket_close(data, cf->conn, !ctx->accepted, ctx->sock); - cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD; - } - else { - CURL_TRC_CF(data, cf, "cf_socket_close(%" CURL_FORMAT_SOCKET_T - ") no longer at conn->sock[], discarding", ctx->sock); - /* TODO: we do not want this to happen. Need to check which - * code is messing with conn->sock[cf->sockindex] */ - } - ctx->sock = CURL_SOCKET_BAD; - if(cf->sockindex == FIRSTSOCKET) - cf->conn->remote_addr = NULL; - } - else { - /* this is our local socket, we did never publish it */ - CURL_TRC_CF(data, cf, "cf_socket_close(%" CURL_FORMAT_SOCKET_T - ", not active)", ctx->sock); - socket_close(data, cf->conn, !ctx->accepted, ctx->sock); - ctx->sock = CURL_SOCKET_BAD; - } + CURL_TRC_CF(data, cf, "cf_socket_close(%" CURL_FORMAT_SOCKET_T + ")", ctx->sock); + if(ctx->sock == cf->conn->sock[cf->sockindex]) + cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD; + socket_close(data, cf->conn, !ctx->accepted, ctx->sock); + ctx->sock = CURL_SOCKET_BAD; + if(ctx->active && cf->sockindex == FIRSTSOCKET) + cf->conn->remote_addr = NULL; Curl_bufq_reset(&ctx->recvbuf); ctx->active = FALSE; ctx->buffer_recv = FALSE; @@ -1169,6 +1146,7 @@ static CURLcode cf_tcp_connect(struct Curl_cfilter *cf, *done = FALSE; /* a very negative world view is best */ if(ctx->sock == CURL_SOCKET_BAD) { + int error; result = cf_socket_open(cf, data); if(result) @@ -1181,8 +1159,12 @@ static CURLcode cf_tcp_connect(struct Curl_cfilter *cf, /* Connect TCP socket */ rc = do_connect(cf, data, cf->conn->bits.tcp_fastopen); + error = SOCKERRNO; + set_local_ip(cf, data); + CURL_TRC_CF(data, cf, "local address %s port %d...", + ctx->l_ip, ctx->l_port); if(-1 == rc) { - result = socket_connect_result(data, ctx->r_ip, SOCKERRNO); + result = socket_connect_result(data, ctx->r_ip, error); goto out; } } @@ -1220,13 +1202,14 @@ static CURLcode cf_tcp_connect(struct Curl_cfilter *cf, out: if(result) { if(ctx->error) { + set_local_ip(cf, data); data->state.os_errno = ctx->error; SET_SOCKERRNO(ctx->error); #ifndef CURL_DISABLE_VERBOSE_STRINGS { char buffer[STRERROR_LEN]; - infof(data, "connect to %s port %u failed: %s", - ctx->r_ip, ctx->r_port, + infof(data, "connect to %s port %u from %s port %d failed: %s", + ctx->r_ip, ctx->r_port, ctx->l_ip, ctx->l_port, Curl_strerror(ctx->error, buffer, sizeof(buffer))); } #endif @@ -1252,20 +1235,19 @@ static void cf_socket_get_host(struct Curl_cfilter *cf, *pport = cf->conn->port; } -static int cf_socket_get_select_socks(struct Curl_cfilter *cf, +static void cf_socket_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data, - curl_socket_t *socks) + struct easy_pollset *ps) { struct cf_socket_ctx *ctx = cf->ctx; - int rc = GETSOCK_BLANK; - (void)data; - if(!cf->connected && ctx->sock != CURL_SOCKET_BAD) { - socks[0] = ctx->sock; - rc |= GETSOCK_WRITESOCK(0); + if(ctx->sock != CURL_SOCKET_BAD) { + if(!cf->connected) + Curl_pollset_set_out_only(data, ps, ctx->sock); + else + Curl_pollset_add_in(data, ps, ctx->sock); + CURL_TRC_CF(data, cf, "adjust_pollset -> %d socks", ps->num); } - - return rc; } static bool cf_socket_data_pending(struct Curl_cfilter *cf, @@ -1518,6 +1500,9 @@ static CURLcode cf_socket_cntrl(struct Curl_cfilter *cf, case CF_CTRL_DATA_SETUP: Curl_persistconninfo(data, cf->conn, ctx->l_ip, ctx->l_port); break; + case CF_CTRL_FORGET_SOCKET: + ctx->sock = CURL_SOCKET_BAD; + break; } return CURLE_OK; } @@ -1612,7 +1597,7 @@ struct Curl_cftype Curl_cft_tcp = { cf_tcp_connect, cf_socket_close, cf_socket_get_host, - cf_socket_get_select_socks, + cf_socket_adjust_pollset, cf_socket_data_pending, cf_socket_send, cf_socket_recv, @@ -1635,7 +1620,7 @@ CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf, (void)data; (void)conn; DEBUGASSERT(transport == TRNSPRT_TCP); - ctx = calloc(sizeof(*ctx), 1); + ctx = calloc(1, sizeof(*ctx)); if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; @@ -1742,7 +1727,7 @@ struct Curl_cftype Curl_cft_udp = { cf_udp_connect, cf_socket_close, cf_socket_get_host, - cf_socket_get_select_socks, + cf_socket_adjust_pollset, cf_socket_data_pending, cf_socket_send, cf_socket_recv, @@ -1765,7 +1750,7 @@ CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf, (void)data; (void)conn; DEBUGASSERT(transport == TRNSPRT_UDP || transport == TRNSPRT_QUIC); - ctx = calloc(sizeof(*ctx), 1); + ctx = calloc(1, sizeof(*ctx)); if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; @@ -1793,7 +1778,7 @@ struct Curl_cftype Curl_cft_unix = { cf_tcp_connect, cf_socket_close, cf_socket_get_host, - cf_socket_get_select_socks, + cf_socket_adjust_pollset, cf_socket_data_pending, cf_socket_send, cf_socket_recv, @@ -1816,7 +1801,7 @@ CURLcode Curl_cf_unix_create(struct Curl_cfilter **pcf, (void)data; (void)conn; DEBUGASSERT(transport == TRNSPRT_UNIX); - ctx = calloc(sizeof(*ctx), 1); + ctx = calloc(1, sizeof(*ctx)); if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; @@ -1857,7 +1842,7 @@ struct Curl_cftype Curl_cft_tcp_accept = { cf_tcp_accept_connect, cf_socket_close, cf_socket_get_host, /* TODO: not accurate */ - cf_socket_get_select_socks, + cf_socket_adjust_pollset, cf_socket_data_pending, cf_socket_send, cf_socket_recv, @@ -1879,7 +1864,7 @@ CURLcode Curl_conn_tcp_listen_set(struct Curl_easy *data, Curl_conn_cf_discard_all(data, conn, sockindex); DEBUGASSERT(conn->sock[sockindex] == CURL_SOCKET_BAD); - ctx = calloc(sizeof(*ctx), 1); + ctx = calloc(1, sizeof(*ctx)); if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; diff --git a/Utilities/cmcurl/lib/cfilters.c b/Utilities/cmcurl/lib/cfilters.c index f74eb40..e78ecd7 100644 --- a/Utilities/cmcurl/lib/cfilters.c +++ b/Utilities/cmcurl/lib/cfilters.c @@ -33,6 +33,7 @@ #include "sockaddr.h" /* required for Curl_sockaddr_storage */ #include "multiif.h" #include "progress.h" +#include "select.h" #include "warnless.h" /* The last 3 #include files should be in this order */ @@ -70,12 +71,14 @@ void Curl_cf_def_get_host(struct Curl_cfilter *cf, struct Curl_easy *data, } } -int Curl_cf_def_get_select_socks(struct Curl_cfilter *cf, +void Curl_cf_def_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data, - curl_socket_t *socks) + struct easy_pollset *ps) { - return cf->next? - cf->next->cft->get_select_socks(cf->next, data, socks) : 0; + /* NOP */ + (void)cf; + (void)data; + (void)ps; } bool Curl_cf_def_data_pending(struct Curl_cfilter *cf, @@ -212,7 +215,7 @@ CURLcode Curl_cf_create(struct Curl_cfilter **pcf, CURLcode result = CURLE_OUT_OF_MEMORY; DEBUGASSERT(cft); - cf = calloc(sizeof(*cf), 1); + cf = calloc(1, sizeof(*cf)); if(!cf) goto out; @@ -303,15 +306,6 @@ void Curl_conn_cf_close(struct Curl_cfilter *cf, struct Curl_easy *data) cf->cft->do_close(cf, data); } -int Curl_conn_cf_get_select_socks(struct Curl_cfilter *cf, - struct Curl_easy *data, - curl_socket_t *socks) -{ - if(cf) - return cf->cft->get_select_socks(cf, data, socks); - return 0; -} - ssize_t Curl_conn_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data, const void *buf, size_t len, CURLcode *err) { @@ -433,22 +427,31 @@ bool Curl_conn_data_pending(struct Curl_easy *data, int sockindex) return FALSE; } -int Curl_conn_get_select_socks(struct Curl_easy *data, int sockindex, - curl_socket_t *socks) +void Curl_conn_cf_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) +{ + /* Get the lowest not-connected filter, if there are any */ + while(cf && !cf->connected && cf->next && !cf->next->connected) + cf = cf->next; + /* From there on, give all filters a chance to adjust the pollset. + * Lower filters are called later, so they may override */ + while(cf) { + cf->cft->adjust_pollset(cf, data, ps); + cf = cf->next; + } +} + +void Curl_conn_adjust_pollset(struct Curl_easy *data, + struct easy_pollset *ps) { - struct Curl_cfilter *cf; + int i; DEBUGASSERT(data); DEBUGASSERT(data->conn); - cf = data->conn->cfilter[sockindex]; - - /* if the next one is not yet connected, that's the one we want */ - while(cf && cf->next && !cf->next->connected) - cf = cf->next; - if(cf) { - return cf->cft->get_select_socks(cf, data, socks); + for(i = 0; i < 2; ++i) { + Curl_conn_cf_adjust_pollset(data->conn->cfilter[i], data, ps); } - return GETSOCK_BLANK; } void Curl_conn_get_host(struct Curl_easy *data, int sockindex, @@ -524,6 +527,18 @@ curl_socket_t Curl_conn_get_socket(struct Curl_easy *data, int sockindex) return data->conn? data->conn->sock[sockindex] : CURL_SOCKET_BAD; } +void Curl_conn_forget_socket(struct Curl_easy *data, int sockindex) +{ + if(data->conn) { + struct Curl_cfilter *cf = data->conn->cfilter[sockindex]; + if(cf) + (void)Curl_conn_cf_cntrl(cf, data, TRUE, + CF_CTRL_FORGET_SOCKET, 0, NULL); + fake_sclose(data->conn->sock[sockindex]); + data->conn->sock[sockindex] = CURL_SOCKET_BAD; + } +} + static CURLcode cf_cntrl_all(struct connectdata *conn, struct Curl_easy *data, bool ignore_result, @@ -646,3 +661,142 @@ size_t Curl_conn_get_max_concurrent(struct Curl_easy *data, &n, NULL) : CURLE_UNKNOWN_OPTION; return (result || n <= 0)? 1 : (size_t)n; } + + +void Curl_pollset_reset(struct Curl_easy *data, + struct easy_pollset *ps) +{ + size_t i; + (void)data; + memset(ps, 0, sizeof(*ps)); + for(i = 0; i< MAX_SOCKSPEREASYHANDLE; i++) + ps->sockets[i] = CURL_SOCKET_BAD; +} + +/** + * + */ +void Curl_pollset_change(struct Curl_easy *data, + struct easy_pollset *ps, curl_socket_t sock, + int add_flags, int remove_flags) +{ + unsigned int i; + + (void)data; + DEBUGASSERT(VALID_SOCK(sock)); + if(!VALID_SOCK(sock)) + return; + + DEBUGASSERT(add_flags <= (CURL_POLL_IN|CURL_POLL_OUT)); + DEBUGASSERT(remove_flags <= (CURL_POLL_IN|CURL_POLL_OUT)); + DEBUGASSERT((add_flags&remove_flags) == 0); /* no overlap */ + for(i = 0; i < ps->num; ++i) { + if(ps->sockets[i] == sock) { + ps->actions[i] &= (unsigned char)(~remove_flags); + ps->actions[i] |= (unsigned char)add_flags; + /* all gone? remove socket */ + if(!ps->actions[i]) { + if((i + 1) < ps->num) { + memmove(&ps->sockets[i], &ps->sockets[i + 1], + (ps->num - (i + 1)) * sizeof(ps->sockets[0])); + memmove(&ps->actions[i], &ps->actions[i + 1], + (ps->num - (i + 1)) * sizeof(ps->actions[0])); + } + --ps->num; + } + return; + } + } + /* not present */ + if(add_flags) { + /* Having more SOCKETS per easy handle than what is defined + * is a programming error. This indicates that we need + * to raise this limit, making easy_pollset larger. + * Since we use this in tight loops, we do not want to make + * the pollset dynamic unnecessarily. + * The current maximum in practise is HTTP/3 eyeballing where + * we have up to 4 sockets involved in connection setup. + */ + DEBUGASSERT(i < MAX_SOCKSPEREASYHANDLE); + if(i < MAX_SOCKSPEREASYHANDLE) { + ps->sockets[i] = sock; + ps->actions[i] = (unsigned char)add_flags; + ps->num = i + 1; + } + } +} + +void Curl_pollset_set(struct Curl_easy *data, + struct easy_pollset *ps, curl_socket_t sock, + bool do_in, bool do_out) +{ + Curl_pollset_change(data, ps, sock, + (do_in?CURL_POLL_IN:0)|(do_out?CURL_POLL_OUT:0), + (!do_in?CURL_POLL_IN:0)|(!do_out?CURL_POLL_OUT:0)); +} + +static void ps_add(struct Curl_easy *data, struct easy_pollset *ps, + int bitmap, curl_socket_t *socks) +{ + if(bitmap) { + int i; + for(i = 0; i < MAX_SOCKSPEREASYHANDLE; ++i) { + if(!(bitmap & GETSOCK_MASK_RW(i)) || !VALID_SOCK((socks[i]))) { + break; + } + if(bitmap & GETSOCK_READSOCK(i)) { + if(bitmap & GETSOCK_WRITESOCK(i)) + Curl_pollset_add_inout(data, ps, socks[i]); + else + /* is READ, since we checked MASK_RW above */ + Curl_pollset_add_in(data, ps, socks[i]); + } + else + Curl_pollset_add_out(data, ps, socks[i]); + } + } +} + +void Curl_pollset_add_socks(struct Curl_easy *data, + struct easy_pollset *ps, + int (*get_socks_cb)(struct Curl_easy *data, + struct connectdata *conn, + curl_socket_t *socks)) +{ + curl_socket_t socks[MAX_SOCKSPEREASYHANDLE]; + int bitmap; + + DEBUGASSERT(data->conn); + bitmap = get_socks_cb(data, data->conn, socks); + ps_add(data, ps, bitmap, socks); +} + +void Curl_pollset_add_socks2(struct Curl_easy *data, + struct easy_pollset *ps, + int (*get_socks_cb)(struct Curl_easy *data, + curl_socket_t *socks)) +{ + curl_socket_t socks[MAX_SOCKSPEREASYHANDLE]; + int bitmap; + + bitmap = get_socks_cb(data, socks); + ps_add(data, ps, bitmap, socks); +} + +void Curl_pollset_check(struct Curl_easy *data, + struct easy_pollset *ps, curl_socket_t sock, + bool *pwant_read, bool *pwant_write) +{ + unsigned int i; + + (void)data; + DEBUGASSERT(VALID_SOCK(sock)); + for(i = 0; i < ps->num; ++i) { + if(ps->sockets[i] == sock) { + *pwant_read = !!(ps->actions[i] & CURL_POLL_IN); + *pwant_write = !!(ps->actions[i] & CURL_POLL_OUT); + return; + } + } + *pwant_read = *pwant_write = FALSE; +} diff --git a/Utilities/cmcurl/lib/cfilters.h b/Utilities/cmcurl/lib/cfilters.h index 2c65264..09a3f16 100644 --- a/Utilities/cmcurl/lib/cfilters.h +++ b/Utilities/cmcurl/lib/cfilters.h @@ -60,14 +60,34 @@ typedef void Curl_cft_get_host(struct Curl_cfilter *cf, const char **pdisplay_host, int *pport); -/* Filters may return sockets and fdset flags they are waiting for. - * The passes array has room for up to MAX_SOCKSPEREASYHANDLE sockets. - * @return read/write fdset for index in socks - * or GETSOCK_BLANK when nothing to wait on +struct easy_pollset; + +/* Passing in an easy_pollset for monitoring of sockets, let + * filters add or remove sockets actions (CURL_POLL_OUT, CURL_POLL_IN). + * This may add a socket or, in case no actions remain, remove + * a socket from the set. + * + * Filter implementations need to call filters "below" *after* they have + * made their adjustments. This allows lower filters to override "upper" + * actions. If a "lower" filter is unable to write, it needs to be able + * to disallow POLL_OUT. + * + * A filter without own restrictions/preferences should not modify + * the pollset. Filters, whose filter "below" is not connected, should + * also do no adjustments. + * + * Examples: a TLS handshake, while ongoing, might remove POLL_IN + * when it needs to write, or vice versa. A HTTP/2 filter might remove + * POLL_OUT when a stream window is exhausted and a WINDOW_UPDATE needs + * to be received first and add instead POLL_IN. + * + * @param cf the filter to ask + * @param data the easy handle the pollset is about + * @param ps the pollset (inout) for the easy handle */ -typedef int Curl_cft_get_select_socks(struct Curl_cfilter *cf, - struct Curl_easy *data, - curl_socket_t *socks); +typedef void Curl_cft_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps); typedef bool Curl_cft_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data); @@ -110,6 +130,7 @@ typedef CURLcode Curl_cft_conn_keep_alive(struct Curl_cfilter *cf, #define CF_CTRL_DATA_DONE_SEND 8 /* 0 NULL ignored */ /* update conn info at connection and data */ #define CF_CTRL_CONN_INFO_UPDATE (256+0) /* 0 NULL ignored */ +#define CF_CTRL_FORGET_SOCKET (256+1) /* 0 NULL ignored */ /** * Handle event/control for the filter. @@ -171,7 +192,7 @@ struct Curl_cftype { Curl_cft_connect *do_connect; /* establish connection */ Curl_cft_close *do_close; /* close conn */ Curl_cft_get_host *get_host; /* host filter talks to */ - Curl_cft_get_select_socks *get_select_socks;/* sockets to select on */ + Curl_cft_adjust_pollset *adjust_pollset; /* adjust transfer poll set */ Curl_cft_data_pending *has_data_pending;/* conn has data pending */ Curl_cft_send *do_send; /* send data */ Curl_cft_recv *do_recv; /* receive data */ @@ -200,9 +221,9 @@ void Curl_cf_def_destroy_this(struct Curl_cfilter *cf, void Curl_cf_def_get_host(struct Curl_cfilter *cf, struct Curl_easy *data, const char **phost, const char **pdisplay_host, int *pport); -int Curl_cf_def_get_select_socks(struct Curl_cfilter *cf, - struct Curl_easy *data, - curl_socket_t *socks); +void Curl_cf_def_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps); bool Curl_cf_def_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data); ssize_t Curl_cf_def_send(struct Curl_cfilter *cf, struct Curl_easy *data, @@ -279,9 +300,6 @@ CURLcode Curl_conn_cf_connect(struct Curl_cfilter *cf, struct Curl_easy *data, bool blocking, bool *done); void Curl_conn_cf_close(struct Curl_cfilter *cf, struct Curl_easy *data); -int Curl_conn_cf_get_select_socks(struct Curl_cfilter *cf, - struct Curl_easy *data, - curl_socket_t *socks); ssize_t Curl_conn_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data, const void *buf, size_t len, CURLcode *err); ssize_t Curl_conn_cf_recv(struct Curl_cfilter *cf, struct Curl_easy *data, @@ -364,11 +382,22 @@ bool Curl_conn_data_pending(struct Curl_easy *data, curl_socket_t Curl_conn_get_socket(struct Curl_easy *data, int sockindex); /** - * Get any select fd flags and the socket filters at chain `sockindex` - * at connection `conn` might be waiting for. + * Tell filters to forget about the socket at sockindex. */ -int Curl_conn_get_select_socks(struct Curl_easy *data, int sockindex, - curl_socket_t *socks); +void Curl_conn_forget_socket(struct Curl_easy *data, int sockindex); + +/** + * Adjust the pollset for the filter chain startgin at `cf`. + */ +void Curl_conn_cf_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps); + +/** + * Adjust pollset from filters installed at transfer's connection. + */ +void Curl_conn_adjust_pollset(struct Curl_easy *data, + struct easy_pollset *ps); /** * Receive data through the filter chain at `sockindex` for connection @@ -468,6 +497,54 @@ size_t Curl_conn_get_max_concurrent(struct Curl_easy *data, int sockindex); +void Curl_pollset_reset(struct Curl_easy *data, + struct easy_pollset *ps); + +/* Change the poll flags (CURL_POLL_IN/CURL_POLL_OUT) to the poll set for + * socket `sock`. If the socket is not already part of the poll set, it + * will be added. + * If the socket is present and all poll flags are cleared, it will be removed. + */ +void Curl_pollset_change(struct Curl_easy *data, + struct easy_pollset *ps, curl_socket_t sock, + int add_flags, int remove_flags); + +void Curl_pollset_set(struct Curl_easy *data, + struct easy_pollset *ps, curl_socket_t sock, + bool do_in, bool do_out); + +#define Curl_pollset_add_in(data, ps, sock) \ + Curl_pollset_change((data), (ps), (sock), CURL_POLL_IN, 0) +#define Curl_pollset_add_out(data, ps, sock) \ + Curl_pollset_change((data), (ps), (sock), CURL_POLL_OUT, 0) +#define Curl_pollset_add_inout(data, ps, sock) \ + Curl_pollset_change((data), (ps), (sock), \ + CURL_POLL_IN|CURL_POLL_OUT, 0) +#define Curl_pollset_set_in_only(data, ps, sock) \ + Curl_pollset_change((data), (ps), (sock), \ + CURL_POLL_IN, CURL_POLL_OUT) +#define Curl_pollset_set_out_only(data, ps, sock) \ + Curl_pollset_change((data), (ps), (sock), \ + CURL_POLL_OUT, CURL_POLL_IN) + +void Curl_pollset_add_socks(struct Curl_easy *data, + struct easy_pollset *ps, + int (*get_socks_cb)(struct Curl_easy *data, + struct connectdata *conn, + curl_socket_t *socks)); +void Curl_pollset_add_socks2(struct Curl_easy *data, + struct easy_pollset *ps, + int (*get_socks_cb)(struct Curl_easy *data, + curl_socket_t *socks)); + +/** + * Check if the pollset, as is, wants to read and/or write regarding + * the given socket. + */ +void Curl_pollset_check(struct Curl_easy *data, + struct easy_pollset *ps, curl_socket_t sock, + bool *pwant_read, bool *pwant_write); + /** * Types and macros used to keep the current easy handle in filter calls, * allowing for nested invocations. See #10336. diff --git a/Utilities/cmcurl/lib/conncache.c b/Utilities/cmcurl/lib/conncache.c index 93d8768..66f18ec 100644 --- a/Utilities/cmcurl/lib/conncache.c +++ b/Utilities/cmcurl/lib/conncache.c @@ -107,7 +107,7 @@ int Curl_conncache_init(struct conncache *connc, int size) connc->closure_handle = curl_easy_init(); if(!connc->closure_handle) return 1; /* bad */ - connc->closure_handle->internal = true; + connc->closure_handle->state.internal = true; Curl_hash_init(&connc->hash, size, Curl_hash_str, Curl_str_key_compare, free_bundle_hash_entry); @@ -243,7 +243,7 @@ CURLcode Curl_conncache_add_conn(struct Curl_easy *data) conn->connection_id = connc->next_connection_id++; connc->num_conn++; - DEBUGF(infof(data, "Added connection %ld. " + DEBUGF(infof(data, "Added connection %" CURL_FORMAT_CURL_OFF_T ". " "The cache now contains %zu members", conn->connection_id, connc->num_conn)); @@ -379,21 +379,26 @@ conncache_find_first_connection(struct conncache *connc) bool Curl_conncache_return_conn(struct Curl_easy *data, struct connectdata *conn) { - /* data->multi->maxconnects can be negative, deal with it. */ - size_t maxconnects = - (data->multi->maxconnects < 0) ? data->multi->num_easy * 4: - data->multi->maxconnects; + unsigned int maxconnects = !data->multi->maxconnects ? + data->multi->num_easy * 4: data->multi->maxconnects; struct connectdata *conn_candidate = NULL; conn->lastused = Curl_now(); /* it was used up until now */ - if(maxconnects > 0 && - Curl_conncache_size(data) > maxconnects) { + if(maxconnects && Curl_conncache_size(data) > maxconnects) { infof(data, "Connection cache is full, closing the oldest one"); conn_candidate = Curl_conncache_extract_oldest(data); if(conn_candidate) { - /* the winner gets the honour of being disconnected */ - Curl_disconnect(data, conn_candidate, /* dead_connection */ FALSE); + /* Use the closure handle for this disconnect so that anything that + happens during the disconnect is not stored and associated with the + 'data' handle which already just finished a transfer and it is + important that details from this (unrelated) disconnect does not + taint meta-data in the data handle. */ + struct conncache *connc = data->state.conn_cache; + connc->closure_handle->state.buffer = data->state.buffer; + connc->closure_handle->set.buffer_size = data->set.buffer_size; + Curl_disconnect(connc->closure_handle, conn_candidate, + /* dead_connection */ FALSE); } } diff --git a/Utilities/cmcurl/lib/connect.c b/Utilities/cmcurl/lib/connect.c index c7ba3e2..ec5ab71 100644 --- a/Utilities/cmcurl/lib/connect.c +++ b/Utilities/cmcurl/lib/connect.c @@ -84,6 +84,9 @@ #include "curl_memory.h" #include "memdebug.h" +#ifndef ARRAYSIZE +#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0])) +#endif /* * Curl_timeleft() returns the amount of milliseconds left allowed for the @@ -348,6 +351,7 @@ void Curl_conncontrol(struct connectdata *conn, */ struct eyeballer { const char *name; + const struct Curl_addrinfo *first; /* complete address list, not owned */ const struct Curl_addrinfo *addr; /* List of addresses to try, not owned */ int ai_family; /* matching address family only */ cf_ip_connect_create *cf_create; /* for creating cf */ @@ -359,9 +363,12 @@ struct eyeballer { expire_id timeout_id; /* ID for Curl_expire() */ CURLcode result; int error; + BIT(rewinded); /* if we rewinded the addr list */ BIT(has_started); /* attempts have started */ BIT(is_done); /* out of addresses/time */ BIT(connected); /* cf has connected */ + BIT(inconclusive); /* connect was not a hard failure, we + * might talk to a restarting server */ }; @@ -408,7 +415,7 @@ static CURLcode eyeballer_new(struct eyeballer **pballer, #endif "ip")); baller->cf_create = cf_create; - baller->addr = addr; + baller->first = baller->addr = addr; baller->ai_family = ai_family; baller->primary = primary; baller->delay_ms = delay_ms; @@ -438,6 +445,13 @@ static void baller_free(struct eyeballer *baller, } } +static void baller_rewind(struct eyeballer *baller) +{ + baller->rewinded = TRUE; + baller->addr = baller->first; + baller->inconclusive = FALSE; +} + static void baller_next_addr(struct eyeballer *baller) { baller->addr = addr_next_match(baller->addr, baller->ai_family); @@ -528,6 +542,10 @@ static CURLcode baller_start_next(struct Curl_cfilter *cf, { if(cf->sockindex == FIRSTSOCKET) { baller_next_addr(baller); + /* If we get inconclusive answers from the server(s), we make + * a second iteration over the address list */ + if(!baller->addr && baller->inconclusive && !baller->rewinded) + baller_rewind(baller); baller_start(cf, data, baller, timeoutms); } else { @@ -566,6 +584,8 @@ static CURLcode baller_connect(struct Curl_cfilter *cf, baller->result = CURLE_OPERATION_TIMEDOUT; } } + else if(baller->result == CURLE_WEIRD_SERVER_REPLY) + baller->inconclusive = TRUE; } return baller->result; } @@ -595,7 +615,7 @@ evaluate: *connected = FALSE; /* a very negative world view is best */ now = Curl_now(); ongoing = not_started = 0; - for(i = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) { + for(i = 0; i < ARRAYSIZE(ctx->baller); i++) { struct eyeballer *baller = ctx->baller[i]; if(!baller || baller->is_done) @@ -656,7 +676,7 @@ evaluate: if(not_started > 0) { int added = 0; - for(i = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) { + for(i = 0; i < ARRAYSIZE(ctx->baller); i++) { struct eyeballer *baller = ctx->baller[i]; if(!baller || baller->has_started) @@ -691,13 +711,13 @@ evaluate: /* all ballers have failed to connect. */ CURL_TRC_CF(data, cf, "all eyeballers failed"); result = CURLE_COULDNT_CONNECT; - for(i = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) { + for(i = 0; i < ARRAYSIZE(ctx->baller); i++) { struct eyeballer *baller = ctx->baller[i]; + if(!baller) + continue; CURL_TRC_CF(data, cf, "%s assess started=%d, result=%d", - baller?baller->name:NULL, - baller?baller->has_started:0, - baller?baller->result:0); - if(baller && baller->has_started && baller->result) { + baller->name, baller->has_started, baller->result); + if(baller->has_started && baller->result) { result = baller->result; break; } @@ -838,7 +858,7 @@ static void cf_he_ctx_clear(struct Curl_cfilter *cf, struct Curl_easy *data) DEBUGASSERT(ctx); DEBUGASSERT(data); - for(i = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) { + for(i = 0; i < ARRAYSIZE(ctx->baller); i++) { baller_free(ctx->baller[i], data); ctx->baller[i] = NULL; } @@ -846,35 +866,22 @@ static void cf_he_ctx_clear(struct Curl_cfilter *cf, struct Curl_easy *data) ctx->winner = NULL; } -static int cf_he_get_select_socks(struct Curl_cfilter *cf, +static void cf_he_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data, - curl_socket_t *socks) + struct easy_pollset *ps) { struct cf_he_ctx *ctx = cf->ctx; - size_t i, s; - int wrc, rc = GETSOCK_BLANK; - curl_socket_t wsocks[MAX_SOCKSPEREASYHANDLE]; - - if(cf->connected) - return cf->next->cft->get_select_socks(cf->next, data, socks); - - for(i = s = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) { - struct eyeballer *baller = ctx->baller[i]; - if(!baller || !baller->cf) - continue; + size_t i; - wrc = Curl_conn_cf_get_select_socks(baller->cf, data, wsocks); - if(wrc) { - /* TODO: we assume we get at most one socket back */ - socks[s] = wsocks[0]; - if(wrc & GETSOCK_WRITESOCK(0)) - rc |= GETSOCK_WRITESOCK(s); - if(wrc & GETSOCK_READSOCK(0)) - rc |= GETSOCK_READSOCK(s); - s++; + if(!cf->connected) { + for(i = 0; i < ARRAYSIZE(ctx->baller); i++) { + struct eyeballer *baller = ctx->baller[i]; + if(!baller || !baller->cf) + continue; + Curl_conn_cf_adjust_pollset(baller->cf, data, ps); } + CURL_TRC_CF(data, cf, "adjust_pollset -> %d socks", ps->num); } - return rc; } static CURLcode cf_he_connect(struct Curl_cfilter *cf, @@ -956,7 +963,7 @@ static bool cf_he_data_pending(struct Curl_cfilter *cf, if(cf->connected) return cf->next->cft->has_data_pending(cf->next, data); - for(i = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) { + for(i = 0; i < ARRAYSIZE(ctx->baller); i++) { struct eyeballer *baller = ctx->baller[i]; if(!baller || !baller->cf) continue; @@ -975,7 +982,7 @@ static struct curltime get_max_baller_time(struct Curl_cfilter *cf, size_t i; memset(&tmax, 0, sizeof(tmax)); - for(i = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) { + for(i = 0; i < ARRAYSIZE(ctx->baller); i++) { struct eyeballer *baller = ctx->baller[i]; memset(&t, 0, sizeof(t)); @@ -1000,7 +1007,7 @@ static CURLcode cf_he_query(struct Curl_cfilter *cf, int reply_ms = -1; size_t i; - for(i = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) { + for(i = 0; i < ARRAYSIZE(ctx->baller); i++) { struct eyeballer *baller = ctx->baller[i]; int breply_ms; @@ -1055,7 +1062,7 @@ struct Curl_cftype Curl_cft_happy_eyeballs = { cf_he_connect, cf_he_close, Curl_cf_def_get_host, - cf_he_get_select_socks, + cf_he_adjust_pollset, cf_he_data_pending, Curl_cf_def_send, Curl_cf_def_recv, @@ -1089,7 +1096,7 @@ cf_happy_eyeballs_create(struct Curl_cfilter **pcf, (void)data; (void)conn; *pcf = NULL; - ctx = calloc(sizeof(*ctx), 1); + ctx = calloc(1, sizeof(*ctx)); if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; @@ -1122,13 +1129,13 @@ struct transport_provider transport_providers[] = { #ifdef ENABLE_QUIC { TRNSPRT_QUIC, Curl_cf_quic_create }, #endif +#ifndef CURL_DISABLE_TFTP { TRNSPRT_UDP, Curl_cf_udp_create }, +#endif +#ifdef USE_UNIX_SOCKETS { TRNSPRT_UNIX, Curl_cf_unix_create }, -}; - -#ifndef ARRAYSIZE -#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0])) #endif +}; static cf_ip_connect_create *get_cf_create(int transport) { @@ -1319,7 +1326,7 @@ struct Curl_cftype Curl_cft_setup = { cf_setup_connect, cf_setup_close, Curl_cf_def_get_host, - Curl_cf_def_get_select_socks, + Curl_cf_def_adjust_pollset, Curl_cf_def_data_pending, Curl_cf_def_send, Curl_cf_def_recv, @@ -1340,7 +1347,7 @@ static CURLcode cf_setup_create(struct Curl_cfilter **pcf, CURLcode result = CURLE_OK; (void)data; - ctx = calloc(sizeof(*ctx), 1); + ctx = calloc(1, sizeof(*ctx)); if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; diff --git a/Utilities/cmcurl/lib/content_encoding.c b/Utilities/cmcurl/lib/content_encoding.c index ec4750e..082e0fa 100644 --- a/Utilities/cmcurl/lib/content_encoding.c +++ b/Utilities/cmcurl/lib/content_encoding.c @@ -63,6 +63,9 @@ #ifndef CURL_DISABLE_HTTP +/* allow no more than 5 "chained" compression steps */ +#define MAX_ENCODE_STACK 5 + #define DSIZ CURL_MAX_WRITE_SIZE /* buffer size for decompressed data */ @@ -95,7 +98,7 @@ typedef enum { /* Deflate and gzip writer. */ struct zlib_writer { - struct contenc_writer super; + struct Curl_cwriter super; zlibInitState zlib_init; /* zlib init state */ uInt trailerlen; /* Remaining trailer byte count. */ z_stream z; /* State structure for zlib. */ @@ -171,7 +174,7 @@ static CURLcode process_trailer(struct Curl_easy *data, } static CURLcode inflate_stream(struct Curl_easy *data, - struct contenc_writer *writer, + struct Curl_cwriter *writer, int type, zlibInitState started) { struct zlib_writer *zp = (struct zlib_writer *) writer; @@ -196,7 +199,7 @@ static CURLcode inflate_stream(struct Curl_easy *data, return exit_zlib(data, z, &zp->zlib_init, CURLE_OUT_OF_MEMORY); /* because the buffer size is fixed, iteratively decompress and transfer to - the client via downstream_write function. */ + the client via next_write function. */ while(!done) { int status; /* zlib status */ done = TRUE; @@ -217,7 +220,7 @@ static CURLcode inflate_stream(struct Curl_easy *data, if(z->avail_out != DSIZ) { if(status == Z_OK || status == Z_STREAM_END) { zp->zlib_init = started; /* Data started. */ - result = Curl_unencode_write(data, writer->downstream, decomp, + result = Curl_cwriter_write(data, writer->next, type, decomp, DSIZ - z->avail_out); if(result) { exit_zlib(data, z, &zp->zlib_init, result); @@ -274,8 +277,8 @@ static CURLcode inflate_stream(struct Curl_easy *data, /* Deflate handler. */ -static CURLcode deflate_init_writer(struct Curl_easy *data, - struct contenc_writer *writer) +static CURLcode deflate_do_init(struct Curl_easy *data, + struct Curl_cwriter *writer) { struct zlib_writer *zp = (struct zlib_writer *) writer; z_stream *z = &zp->z; /* zlib state structure */ @@ -290,13 +293,16 @@ static CURLcode deflate_init_writer(struct Curl_easy *data, return CURLE_OK; } -static CURLcode deflate_unencode_write(struct Curl_easy *data, - struct contenc_writer *writer, +static CURLcode deflate_do_write(struct Curl_easy *data, + struct Curl_cwriter *writer, int type, const char *buf, size_t nbytes) { struct zlib_writer *zp = (struct zlib_writer *) writer; z_stream *z = &zp->z; /* zlib state structure */ + if(!(type & CLIENTWRITE_BODY)) + return Curl_cwriter_write(data, writer->next, type, buf, nbytes); + /* Set the compressed input when this function is called */ z->next_in = (Bytef *) buf; z->avail_in = (uInt) nbytes; @@ -305,11 +311,11 @@ static CURLcode deflate_unencode_write(struct Curl_easy *data, return process_trailer(data, zp); /* Now uncompress the data */ - return inflate_stream(data, writer, ZLIB_INFLATING); + return inflate_stream(data, writer, type, ZLIB_INFLATING); } -static void deflate_close_writer(struct Curl_easy *data, - struct contenc_writer *writer) +static void deflate_do_close(struct Curl_easy *data, + struct Curl_cwriter *writer) { struct zlib_writer *zp = (struct zlib_writer *) writer; z_stream *z = &zp->z; /* zlib state structure */ @@ -317,19 +323,19 @@ static void deflate_close_writer(struct Curl_easy *data, exit_zlib(data, z, &zp->zlib_init, CURLE_OK); } -static const struct content_encoding deflate_encoding = { +static const struct Curl_cwtype deflate_encoding = { "deflate", NULL, - deflate_init_writer, - deflate_unencode_write, - deflate_close_writer, + deflate_do_init, + deflate_do_write, + deflate_do_close, sizeof(struct zlib_writer) }; /* Gzip handler. */ -static CURLcode gzip_init_writer(struct Curl_easy *data, - struct contenc_writer *writer) +static CURLcode gzip_do_init(struct Curl_easy *data, + struct Curl_cwriter *writer) { struct zlib_writer *zp = (struct zlib_writer *) writer; z_stream *z = &zp->z; /* zlib state structure */ @@ -441,19 +447,22 @@ static enum { } #endif -static CURLcode gzip_unencode_write(struct Curl_easy *data, - struct contenc_writer *writer, +static CURLcode gzip_do_write(struct Curl_easy *data, + struct Curl_cwriter *writer, int type, const char *buf, size_t nbytes) { struct zlib_writer *zp = (struct zlib_writer *) writer; z_stream *z = &zp->z; /* zlib state structure */ + if(!(type & CLIENTWRITE_BODY)) + return Curl_cwriter_write(data, writer->next, type, buf, nbytes); + if(zp->zlib_init == ZLIB_INIT_GZIP) { /* Let zlib handle the gzip decompression entirely */ z->next_in = (Bytef *) buf; z->avail_in = (uInt) nbytes; /* Now uncompress the data */ - return inflate_stream(data, writer, ZLIB_INIT_GZIP); + return inflate_stream(data, writer, type, ZLIB_INIT_GZIP); } #ifndef OLD_ZLIB_SUPPORT @@ -565,12 +574,12 @@ static CURLcode gzip_unencode_write(struct Curl_easy *data, } /* We've parsed the header, now uncompress the data */ - return inflate_stream(data, writer, ZLIB_GZIP_INFLATING); + return inflate_stream(data, writer, type, ZLIB_GZIP_INFLATING); #endif } -static void gzip_close_writer(struct Curl_easy *data, - struct contenc_writer *writer) +static void gzip_do_close(struct Curl_easy *data, + struct Curl_cwriter *writer) { struct zlib_writer *zp = (struct zlib_writer *) writer; z_stream *z = &zp->z; /* zlib state structure */ @@ -578,12 +587,12 @@ static void gzip_close_writer(struct Curl_easy *data, exit_zlib(data, z, &zp->zlib_init, CURLE_OK); } -static const struct content_encoding gzip_encoding = { +static const struct Curl_cwtype gzip_encoding = { "gzip", "x-gzip", - gzip_init_writer, - gzip_unencode_write, - gzip_close_writer, + gzip_do_init, + gzip_do_write, + gzip_do_close, sizeof(struct zlib_writer) }; @@ -593,7 +602,7 @@ static const struct content_encoding gzip_encoding = { #ifdef HAVE_BROTLI /* Brotli writer. */ struct brotli_writer { - struct contenc_writer super; + struct Curl_cwriter super; BrotliDecoderState *br; /* State structure for brotli. */ }; @@ -635,8 +644,8 @@ static CURLcode brotli_map_error(BrotliDecoderErrorCode be) return CURLE_WRITE_ERROR; } -static CURLcode brotli_init_writer(struct Curl_easy *data, - struct contenc_writer *writer) +static CURLcode brotli_do_init(struct Curl_easy *data, + struct Curl_cwriter *writer) { struct brotli_writer *bp = (struct brotli_writer *) writer; (void) data; @@ -645,8 +654,8 @@ static CURLcode brotli_init_writer(struct Curl_easy *data, return bp->br? CURLE_OK: CURLE_OUT_OF_MEMORY; } -static CURLcode brotli_unencode_write(struct Curl_easy *data, - struct contenc_writer *writer, +static CURLcode brotli_do_write(struct Curl_easy *data, + struct Curl_cwriter *writer, int type, const char *buf, size_t nbytes) { struct brotli_writer *bp = (struct brotli_writer *) writer; @@ -657,6 +666,9 @@ static CURLcode brotli_unencode_write(struct Curl_easy *data, CURLcode result = CURLE_OK; BrotliDecoderResult r = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT; + if(!(type & CLIENTWRITE_BODY)) + return Curl_cwriter_write(data, writer->next, type, buf, nbytes); + if(!bp->br) return CURLE_WRITE_ERROR; /* Stream already ended. */ @@ -670,7 +682,7 @@ static CURLcode brotli_unencode_write(struct Curl_easy *data, dstleft = DSIZ; r = BrotliDecoderDecompressStream(bp->br, &nbytes, &src, &dstleft, &dst, NULL); - result = Curl_unencode_write(data, writer->downstream, + result = Curl_cwriter_write(data, writer->next, type, decomp, DSIZ - dstleft); if(result) break; @@ -693,8 +705,8 @@ static CURLcode brotli_unencode_write(struct Curl_easy *data, return result; } -static void brotli_close_writer(struct Curl_easy *data, - struct contenc_writer *writer) +static void brotli_do_close(struct Curl_easy *data, + struct Curl_cwriter *writer) { struct brotli_writer *bp = (struct brotli_writer *) writer; @@ -706,12 +718,12 @@ static void brotli_close_writer(struct Curl_easy *data, } } -static const struct content_encoding brotli_encoding = { +static const struct Curl_cwtype brotli_encoding = { "br", NULL, - brotli_init_writer, - brotli_unencode_write, - brotli_close_writer, + brotli_do_init, + brotli_do_write, + brotli_do_close, sizeof(struct brotli_writer) }; #endif @@ -720,13 +732,13 @@ static const struct content_encoding brotli_encoding = { #ifdef HAVE_ZSTD /* Zstd writer. */ struct zstd_writer { - struct contenc_writer super; + struct Curl_cwriter super; ZSTD_DStream *zds; /* State structure for zstd. */ void *decomp; }; -static CURLcode zstd_init_writer(struct Curl_easy *data, - struct contenc_writer *writer) +static CURLcode zstd_do_init(struct Curl_easy *data, + struct Curl_cwriter *writer) { struct zstd_writer *zp = (struct zstd_writer *) writer; @@ -737,8 +749,8 @@ static CURLcode zstd_init_writer(struct Curl_easy *data, return zp->zds ? CURLE_OK : CURLE_OUT_OF_MEMORY; } -static CURLcode zstd_unencode_write(struct Curl_easy *data, - struct contenc_writer *writer, +static CURLcode zstd_do_write(struct Curl_easy *data, + struct Curl_cwriter *writer, int type, const char *buf, size_t nbytes) { CURLcode result = CURLE_OK; @@ -747,6 +759,9 @@ static CURLcode zstd_unencode_write(struct Curl_easy *data, ZSTD_outBuffer out; size_t errorCode; + if(!(type & CLIENTWRITE_BODY)) + return Curl_cwriter_write(data, writer->next, type, buf, nbytes); + if(!zp->decomp) { zp->decomp = malloc(DSIZ); if(!zp->decomp) @@ -766,7 +781,7 @@ static CURLcode zstd_unencode_write(struct Curl_easy *data, return CURLE_BAD_CONTENT_ENCODING; } if(out.pos > 0) { - result = Curl_unencode_write(data, writer->downstream, + result = Curl_cwriter_write(data, writer->next, type, zp->decomp, out.pos); if(result) break; @@ -778,8 +793,8 @@ static CURLcode zstd_unencode_write(struct Curl_easy *data, return result; } -static void zstd_close_writer(struct Curl_easy *data, - struct contenc_writer *writer) +static void zstd_do_close(struct Curl_easy *data, + struct Curl_cwriter *writer) { struct zstd_writer *zp = (struct zstd_writer *) writer; @@ -795,52 +810,30 @@ static void zstd_close_writer(struct Curl_easy *data, } } -static const struct content_encoding zstd_encoding = { +static const struct Curl_cwtype zstd_encoding = { "zstd", NULL, - zstd_init_writer, - zstd_unencode_write, - zstd_close_writer, + zstd_do_init, + zstd_do_write, + zstd_do_close, sizeof(struct zstd_writer) }; #endif /* Identity handler. */ -static CURLcode identity_init_writer(struct Curl_easy *data, - struct contenc_writer *writer) -{ - (void)data; - (void)writer; - return CURLE_OK; -} - -static CURLcode identity_unencode_write(struct Curl_easy *data, - struct contenc_writer *writer, - const char *buf, size_t nbytes) -{ - return Curl_unencode_write(data, writer->downstream, buf, nbytes); -} - -static void identity_close_writer(struct Curl_easy *data, - struct contenc_writer *writer) -{ - (void) data; - (void) writer; -} - -static const struct content_encoding identity_encoding = { +static const struct Curl_cwtype identity_encoding = { "identity", "none", - identity_init_writer, - identity_unencode_write, - identity_close_writer, - sizeof(struct contenc_writer) + Curl_cwriter_def_init, + Curl_cwriter_def_write, + Curl_cwriter_def_close, + sizeof(struct Curl_cwriter) }; /* supported content encodings table. */ -static const struct content_encoding * const encodings[] = { +static const struct Curl_cwtype * const encodings[] = { &identity_encoding, #ifdef HAVE_LIBZ &deflate_encoding, @@ -856,13 +849,17 @@ static const struct content_encoding * const encodings[] = { }; -/* Return a list of comma-separated names of supported encodings. */ -char *Curl_all_content_encodings(void) +/* Provide a list of comma-separated names of supported encodings. +*/ +void Curl_all_content_encodings(char *buf, size_t blen) { size_t len = 0; - const struct content_encoding * const *cep; - const struct content_encoding *ce; - char *ace; + const struct Curl_cwtype * const *cep; + const struct Curl_cwtype *ce; + + DEBUGASSERT(buf); + DEBUGASSERT(blen); + buf[0] = 0; for(cep = encodings; *cep; cep++) { ce = *cep; @@ -870,12 +867,12 @@ char *Curl_all_content_encodings(void) len += strlen(ce->name) + 2; } - if(!len) - return strdup(CONTENT_ENCODING_DEFAULT); - - ace = malloc(len); - if(ace) { - char *p = ace; + if(!len) { + if(blen >= sizeof(CONTENT_ENCODING_DEFAULT)) + strcpy(buf, CONTENT_ENCODING_DEFAULT); + } + else if(blen > len) { + char *p = buf; for(cep = encodings; *cep; cep++) { ce = *cep; if(!strcasecompare(ce->name, CONTENT_ENCODING_DEFAULT)) { @@ -887,75 +884,60 @@ char *Curl_all_content_encodings(void) } p[-2] = '\0'; } - - return ace; } - /* Deferred error dummy writer. */ -static CURLcode error_init_writer(struct Curl_easy *data, - struct contenc_writer *writer) +static CURLcode error_do_init(struct Curl_easy *data, + struct Curl_cwriter *writer) { (void)data; (void)writer; return CURLE_OK; } -static CURLcode error_unencode_write(struct Curl_easy *data, - struct contenc_writer *writer, +static CURLcode error_do_write(struct Curl_easy *data, + struct Curl_cwriter *writer, int type, const char *buf, size_t nbytes) { - char *all = Curl_all_content_encodings(); + char all[256]; + (void)Curl_all_content_encodings(all, sizeof(all)); (void) writer; (void) buf; (void) nbytes; - if(!all) - return CURLE_OUT_OF_MEMORY; + if(!(type & CLIENTWRITE_BODY)) + return Curl_cwriter_write(data, writer->next, type, buf, nbytes); + failf(data, "Unrecognized content encoding type. " "libcurl understands %s content encodings.", all); - free(all); return CURLE_BAD_CONTENT_ENCODING; } -static void error_close_writer(struct Curl_easy *data, - struct contenc_writer *writer) +static void error_do_close(struct Curl_easy *data, + struct Curl_cwriter *writer) { (void) data; (void) writer; } -static const struct content_encoding error_encoding = { - NULL, +static const struct Curl_cwtype error_writer = { + "ce-error", NULL, - error_init_writer, - error_unencode_write, - error_close_writer, - sizeof(struct contenc_writer) + error_do_init, + error_do_write, + error_do_close, + sizeof(struct Curl_cwriter) }; -/* Write data using an unencoding writer stack. "nbytes" is not - allowed to be 0. */ -CURLcode Curl_unencode_write(struct Curl_easy *data, - struct contenc_writer *writer, - const char *buf, size_t nbytes) -{ - if(!nbytes) - return CURLE_OK; - if(!writer) - return CURLE_WRITE_ERROR; - return writer->handler->unencode_write(data, writer, buf, nbytes); -} - /* Find the content encoding by name. */ -static const struct content_encoding *find_encoding(const char *name, +static const struct Curl_cwtype *find_encoding(const char *name, size_t len) { - const struct content_encoding * const *cep; + const struct Curl_cwtype * const *cep; for(cep = encodings; *cep; cep++) { - const struct content_encoding *ce = *cep; + const struct Curl_cwtype *ce = *cep; if((strncasecompare(name, ce->name, len) && !ce->name[len]) || (ce->alias && strncasecompare(name, ce->alias, len) && !ce->alias[len])) return ce; @@ -969,7 +951,8 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data, const char *enclist, int is_transfer) { struct SingleRequest *k = &data->req; - unsigned int order = is_transfer? 2: 1; + Curl_cwriter_phase phase = is_transfer? + CURL_CW_TRANSFER_DECODE:CURL_CW_CONTENT_DECODE; CURLcode result; do { @@ -992,23 +975,32 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data, Curl_httpchunk_init(data); /* init our chunky engine. */ } else if(namelen) { - const struct content_encoding *encoding; - struct contenc_writer *writer; - if(is_transfer && !data->set.http_transfer_encoding) + const struct Curl_cwtype *cwt; + struct Curl_cwriter *writer; + + if((is_transfer && !data->set.http_transfer_encoding) || + (!is_transfer && data->set.http_ce_skip)) { /* not requested, ignore */ return CURLE_OK; + } - encoding = find_encoding(name, namelen); - if(!encoding) - encoding = &error_encoding; /* Defer error at stack use. */ + if(Curl_cwriter_count(data, phase) + 1 >= MAX_ENCODE_STACK) { + failf(data, "Reject response due to more than %u content encodings", + MAX_ENCODE_STACK); + return CURLE_BAD_CONTENT_ENCODING; + } + + cwt = find_encoding(name, namelen); + if(!cwt) + cwt = &error_writer; /* Defer error at use. */ - result = Curl_client_create_writer(&writer, data, encoding, order); + result = Curl_cwriter_create(&writer, data, cwt, phase); if(result) return result; - result = Curl_client_add_writer(data, writer); + result = Curl_cwriter_add(data, writer); if(result) { - Curl_client_free_writer(data, writer); + Curl_cwriter_free(data, writer); return result; } } @@ -1028,20 +1020,15 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data, return CURLE_NOT_BUILT_IN; } -CURLcode Curl_unencode_write(struct Curl_easy *data, - struct contenc_writer *writer, - const char *buf, size_t nbytes) +void Curl_all_content_encodings(char *buf, size_t blen) { - (void) data; - (void) writer; - (void) buf; - (void) nbytes; - return CURLE_NOT_BUILT_IN; + DEBUGASSERT(buf); + DEBUGASSERT(blen); + if(blen < sizeof(CONTENT_ENCODING_DEFAULT)) + buf[0] = 0; + else + strcpy(buf, CONTENT_ENCODING_DEFAULT); } -char *Curl_all_content_encodings(void) -{ - return strdup(CONTENT_ENCODING_DEFAULT); /* Satisfy caller. */ -} #endif /* CURL_DISABLE_HTTP */ diff --git a/Utilities/cmcurl/lib/content_encoding.h b/Utilities/cmcurl/lib/content_encoding.h index ef7930c..1addf23 100644 --- a/Utilities/cmcurl/lib/content_encoding.h +++ b/Utilities/cmcurl/lib/content_encoding.h @@ -25,15 +25,10 @@ ***************************************************************************/ #include "curl_setup.h" -struct contenc_writer; +struct Curl_cwriter; -char *Curl_all_content_encodings(void); +void Curl_all_content_encodings(char *buf, size_t blen); CURLcode Curl_build_unencoding_stack(struct Curl_easy *data, const char *enclist, int is_transfer); -CURLcode Curl_unencode_write(struct Curl_easy *data, - struct contenc_writer *writer, - const char *buf, size_t nbytes); -void Curl_unencode_cleanup(struct Curl_easy *data); - #endif /* HEADER_CURL_CONTENT_ENCODING_H */ diff --git a/Utilities/cmcurl/lib/cookie.c b/Utilities/cmcurl/lib/cookie.c index af01203..9095cea 100644 --- a/Utilities/cmcurl/lib/cookie.c +++ b/Utilities/cmcurl/lib/cookie.c @@ -330,7 +330,7 @@ static char *sanitize_cookie_path(const char *cookie_path) */ void Curl_cookie_loadfiles(struct Curl_easy *data) { - struct curl_slist *list = data->set.cookielist; + struct curl_slist *list = data->state.cookielist; if(list) { Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); while(list) { @@ -365,9 +365,7 @@ static void strstore(char **str, const char *newstr, size_t len) DEBUGASSERT(newstr); DEBUGASSERT(str); free(*str); - *str = Curl_memdup(newstr, len + 1); - if(*str) - (*str)[len] = 0; + *str = Curl_strndup(newstr, len); } /* @@ -1029,15 +1027,23 @@ Curl_cookie_add(struct Curl_easy *data, * dereference it. */ if(data && (domain && co->domain && !Curl_host_is_ipnum(co->domain))) { - const psl_ctx_t *psl = Curl_psl_use(data); - int acceptable; - - if(psl) { - acceptable = psl_is_cookie_domain_acceptable(psl, domain, co->domain); - Curl_psl_release(data); + bool acceptable = FALSE; + char lcase[256]; + char lcookie[256]; + size_t dlen = strlen(domain); + size_t clen = strlen(co->domain); + if((dlen < sizeof(lcase)) && (clen < sizeof(lcookie))) { + const psl_ctx_t *psl = Curl_psl_use(data); + if(psl) { + /* the PSL check requires lowercase domain name and pattern */ + Curl_strntolower(lcase, domain, dlen + 1); + Curl_strntolower(lcookie, co->domain, clen + 1); + acceptable = psl_is_cookie_domain_acceptable(psl, lcase, lcookie); + Curl_psl_release(data); + } + else + acceptable = !bad_domain(domain, strlen(domain)); } - else - acceptable = !bad_domain(domain, strlen(domain)); if(!acceptable) { infof(data, "cookie '%s' dropped, domain '%s' must not " @@ -1347,7 +1353,7 @@ static int cookie_sort_ct(const void *p1, const void *p2) static struct Cookie *dup_cookie(struct Cookie *src) { - struct Cookie *d = calloc(sizeof(struct Cookie), 1); + struct Cookie *d = calloc(1, sizeof(struct Cookie)); if(d) { CLONE(domain); CLONE(path); diff --git a/Utilities/cmcurl/lib/curl_config.h.cmake b/Utilities/cmcurl/lib/curl_config.h.cmake index d4bb274..a3c5af5 100644 --- a/Utilities/cmcurl/lib/curl_config.h.cmake +++ b/Utilities/cmcurl/lib/curl_config.h.cmake @@ -67,9 +67,15 @@ /* disables FTP */ #cmakedefine CURL_DISABLE_FTP 1 +/* disables curl_easy_options API for existing options to curl_easy_setopt */ +#cmakedefine CURL_DISABLE_GETOPTIONS 1 + /* disables GOPHER */ #cmakedefine CURL_DISABLE_GOPHER 1 +/* disables headers-api support */ +#cmakedefine CURL_DISABLE_HEADERS_API 1 + /* disables HSTS support */ #cmakedefine CURL_DISABLE_HSTS 1 @@ -91,6 +97,9 @@ /* disables MIME support */ #cmakedefine CURL_DISABLE_MIME 1 +/* disables local binding support */ +#cmakedefine CURL_DISABLE_BINDLOCAL 1 + /* disables MQTT */ #cmakedefine CURL_DISABLE_MQTT 1 @@ -161,9 +170,6 @@ /* Define to 1 if you have _Atomic support. */ #cmakedefine HAVE_ATOMIC 1 -/* Define to 1 if you have the `fchmod' function. */ -#cmakedefine HAVE_FCHMOD 1 - /* Define to 1 if you have the `fnmatch' function. */ #cmakedefine HAVE_FNMATCH 1 @@ -201,6 +207,9 @@ /* Define to 1 if you have the fseeko function. */ #cmakedefine HAVE_FSEEKO 1 +/* Define to 1 if you have the fseeko declaration. */ +#cmakedefine HAVE_DECL_FSEEKO 1 + /* Define to 1 if you have the _fseeki64 function. */ #cmakedefine HAVE__FSEEKI64 1 @@ -306,9 +315,6 @@ /* Define to 1 if symbol `ADDRESS_FAMILY' exists */ #cmakedefine HAVE_ADDRESS_FAMILY 1 -/* Define to 1 if you have the <inttypes.h> header file. */ -#cmakedefine HAVE_INTTYPES_H 1 - /* Define to 1 if you have the ioctlsocket function. */ #cmakedefine HAVE_IOCTLSOCKET 1 @@ -492,9 +498,6 @@ /* Define to 1 if you have the <stdbool.h> header file. */ #cmakedefine HAVE_STDBOOL_H 1 -/* Define to 1 if you have the <stdint.h> header file. */ -#cmakedefine HAVE_STDINT_H 1 - /* Define to 1 if you have the strcasecmp function. */ #cmakedefine HAVE_STRCASECMP 1 @@ -591,12 +594,6 @@ /* Define to 1 if you have the <utime.h> header file. */ #cmakedefine HAVE_UTIME_H 1 -/* Define to 1 if compiler supports C99 variadic macro style. */ -#cmakedefine HAVE_VARIADIC_MACROS_C99 1 - -/* Define to 1 if compiler supports old gcc variadic macro style. */ -#cmakedefine HAVE_VARIADIC_MACROS_GCC 1 - /* Define to 1 if you have the windows.h header file. */ #cmakedefine HAVE_WINDOWS_H 1 diff --git a/Utilities/cmcurl/lib/curl_hmac.h b/Utilities/cmcurl/lib/curl_hmac.h index 2ea03dd..7a5387a 100644 --- a/Utilities/cmcurl/lib/curl_hmac.h +++ b/Utilities/cmcurl/lib/curl_hmac.h @@ -25,7 +25,8 @@ ***************************************************************************/ #if (defined(USE_CURL_NTLM_CORE) && !defined(USE_WINDOWS_SSPI)) \ - || !defined(CURL_DISABLE_AWS) || !defined(CURL_DISABLE_DIGEST_AUTH) + || !defined(CURL_DISABLE_AWS) || !defined(CURL_DISABLE_DIGEST_AUTH) \ + || defined(USE_LIBSSH2) #include <curl/curl.h> diff --git a/Utilities/cmcurl/lib/curl_memory.h b/Utilities/cmcurl/lib/curl_memory.h index b8c46d7..714ad71 100644 --- a/Utilities/cmcurl/lib/curl_memory.h +++ b/Utilities/cmcurl/lib/curl_memory.h @@ -68,7 +68,7 @@ #undef send #undef recv -#ifdef WIN32 +#ifdef _WIN32 # ifdef UNICODE # undef wcsdup # undef _wcsdup @@ -134,7 +134,7 @@ extern curl_free_callback Curl_cfree; extern curl_realloc_callback Curl_crealloc; extern curl_strdup_callback Curl_cstrdup; extern curl_calloc_callback Curl_ccalloc; -#if defined(WIN32) && defined(UNICODE) +#if defined(_WIN32) && defined(UNICODE) extern curl_wcsdup_callback Curl_cwcsdup; #endif @@ -160,7 +160,7 @@ extern curl_wcsdup_callback Curl_cwcsdup; #undef free #define free(ptr) Curl_cfree(ptr) -#ifdef WIN32 +#ifdef _WIN32 # ifdef UNICODE # undef wcsdup # define wcsdup(ptr) Curl_cwcsdup(ptr) diff --git a/Utilities/cmcurl/lib/curl_multibyte.c b/Utilities/cmcurl/lib/curl_multibyte.c index 522ea34..ff21098 100644 --- a/Utilities/cmcurl/lib/curl_multibyte.c +++ b/Utilities/cmcurl/lib/curl_multibyte.c @@ -32,7 +32,7 @@ #include "curl_setup.h" -#if defined(WIN32) +#if defined(_WIN32) #include "curl_multibyte.h" @@ -84,7 +84,7 @@ char *curlx_convert_wchar_to_UTF8(const wchar_t *str_w) return str_utf8; } -#endif /* WIN32 */ +#endif /* _WIN32 */ #if defined(USE_WIN32_LARGE_FILES) || defined(USE_WIN32_SMALL_FILES) diff --git a/Utilities/cmcurl/lib/curl_multibyte.h b/Utilities/cmcurl/lib/curl_multibyte.h index ddac1f6..8b9ac71 100644 --- a/Utilities/cmcurl/lib/curl_multibyte.h +++ b/Utilities/cmcurl/lib/curl_multibyte.h @@ -25,7 +25,7 @@ ***************************************************************************/ #include "curl_setup.h" -#if defined(WIN32) +#if defined(_WIN32) /* * MultiByte conversions using Windows kernel32 library. @@ -33,7 +33,7 @@ wchar_t *curlx_convert_UTF8_to_wchar(const char *str_utf8); char *curlx_convert_wchar_to_UTF8(const wchar_t *str_w); -#endif /* WIN32 */ +#endif /* _WIN32 */ /* * Macros curlx_convert_UTF8_to_tchar(), curlx_convert_tchar_to_UTF8() @@ -54,7 +54,7 @@ char *curlx_convert_wchar_to_UTF8(const wchar_t *str_w); * ensure that the curl memdebug override macros do not replace them. */ -#if defined(UNICODE) && defined(WIN32) +#if defined(UNICODE) && defined(_WIN32) #define curlx_convert_UTF8_to_tchar(ptr) curlx_convert_UTF8_to_wchar((ptr)) #define curlx_convert_tchar_to_UTF8(ptr) curlx_convert_wchar_to_UTF8((ptr)) @@ -78,7 +78,7 @@ typedef union { const unsigned char *const_tbyte_ptr; } xcharp_u; -#endif /* UNICODE && WIN32 */ +#endif /* UNICODE && _WIN32 */ #define curlx_unicodefree(ptr) \ do { \ diff --git a/Utilities/cmcurl/lib/curl_ntlm_core.c b/Utilities/cmcurl/lib/curl_ntlm_core.c index cc0ed91..6f6d75c 100644 --- a/Utilities/cmcurl/lib/curl_ntlm_core.c +++ b/Utilities/cmcurl/lib/curl_ntlm_core.c @@ -111,6 +111,7 @@ # include <wincrypt.h> #else # error "Can't compile NTLM support without a crypto library with DES." +# define CURL_NTLM_NOT_SUPPORTED #endif #include "urldata.h" @@ -130,6 +131,7 @@ #define NTLMv2_BLOB_SIGNATURE "\x01\x01\x00\x00" #define NTLMv2_BLOB_LEN (44 -16 + ntlm->target_info_len + 4) +#if !defined(CURL_NTLM_NOT_SUPPORTED) /* * Turns a 56-bit key into being 64-bit wide. */ @@ -144,6 +146,7 @@ static void extend_key_56_to_64(const unsigned char *key_56, char *key) key[6] = (unsigned char)(((key_56[5] << 2) & 0xFF) | (key_56[6] >> 6)); key[7] = (unsigned char) ((key_56[6] << 1) & 0xFF); } +#endif #if defined(USE_OPENSSL_DES) || defined(USE_WOLFSSL) /* @@ -337,6 +340,10 @@ void Curl_ntlm_core_lm_resp(const unsigned char *keys, encrypt_des(plaintext, results, keys); encrypt_des(plaintext, results + 8, keys + 7); encrypt_des(plaintext, results + 16, keys + 14); +#else + (void)keys; + (void)plaintext; + (void)results; #endif } @@ -347,9 +354,11 @@ CURLcode Curl_ntlm_core_mk_lm_hash(const char *password, unsigned char *lmbuffer /* 21 bytes */) { unsigned char pw[14]; +#if !defined(CURL_NTLM_NOT_SUPPORTED) static const unsigned char magic[] = { 0x4B, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25 /* i.e. KGS!@#$% */ }; +#endif size_t len = CURLMIN(strlen(password), 14); Curl_strntoupper((char *)pw, password, len); diff --git a/Utilities/cmcurl/lib/curl_ntlm_wb.c b/Utilities/cmcurl/lib/curl_ntlm_wb.c index aa7bea7..b087a37 100644 --- a/Utilities/cmcurl/lib/curl_ntlm_wb.c +++ b/Utilities/cmcurl/lib/curl_ntlm_wb.c @@ -68,7 +68,9 @@ /* Portable 'sclose_nolog' used only in child process instead of 'sclose' to avoid fooling the socket leak detector */ -#if defined(HAVE_CLOSESOCKET) +#ifdef HAVE_PIPE +# define sclose_nolog(x) close((x)) +#elif defined(HAVE_CLOSESOCKET) # define sclose_nolog(x) closesocket((x)) #elif defined(HAVE_CLOSESOCKET_CAMEL) # define sclose_nolog(x) CloseSocket((x)) @@ -189,7 +191,7 @@ static CURLcode ntlm_wb_init(struct Curl_easy *data, struct ntlmdata *ntlm, goto done; } - if(Curl_socketpair(AF_UNIX, SOCK_STREAM, 0, sockfds)) { + if(wakeup_create(sockfds)) { failf(data, "Could not open socket pair. errno %d: %s", errno, Curl_strerror(errno, buffer, sizeof(buffer))); goto done; @@ -197,8 +199,8 @@ static CURLcode ntlm_wb_init(struct Curl_easy *data, struct ntlmdata *ntlm, child_pid = fork(); if(child_pid == -1) { - sclose(sockfds[0]); - sclose(sockfds[1]); + wakeup_close(sockfds[0]); + wakeup_close(sockfds[1]); failf(data, "Could not fork. errno %d: %s", errno, Curl_strerror(errno, buffer, sizeof(buffer))); goto done; @@ -268,7 +270,7 @@ static CURLcode ntlm_wb_response(struct Curl_easy *data, struct ntlmdata *ntlm, Curl_dyn_init(&b, MAX_NTLM_WB_RESPONSE); while(len_in > 0) { - ssize_t written = swrite(ntlm->ntlm_auth_hlpr_socket, input, len_in); + ssize_t written = wakeup_write(ntlm->ntlm_auth_hlpr_socket, input, len_in); if(written == -1) { /* Interrupted by a signal, retry it */ if(errno == EINTR) @@ -282,7 +284,7 @@ static CURLcode ntlm_wb_response(struct Curl_easy *data, struct ntlmdata *ntlm, /* Read one line */ while(1) { ssize_t size = - sread(ntlm->ntlm_auth_hlpr_socket, buf, data->set.buffer_size); + wakeup_read(ntlm->ntlm_auth_hlpr_socket, buf, data->set.buffer_size); if(size == -1) { if(errno == EINTR) continue; diff --git a/Utilities/cmcurl/lib/curl_path.h b/Utilities/cmcurl/lib/curl_path.h index 9ed09de..cbe51c2 100644 --- a/Utilities/cmcurl/lib/curl_path.h +++ b/Utilities/cmcurl/lib/curl_path.h @@ -28,7 +28,7 @@ #include <curl/curl.h> #include "urldata.h" -#ifdef WIN32 +#ifdef _WIN32 # undef PATH_MAX # define PATH_MAX MAX_PATH # ifndef R_OK diff --git a/Utilities/cmcurl/lib/curl_rtmp.c b/Utilities/cmcurl/lib/curl_rtmp.c index 406fb42..f7cf54e 100644 --- a/Utilities/cmcurl/lib/curl_rtmp.c +++ b/Utilities/cmcurl/lib/curl_rtmp.c @@ -39,7 +39,7 @@ /* The last #include file should be: */ #include "memdebug.h" -#if defined(WIN32) && !defined(USE_LWIPSOCK) +#if defined(_WIN32) && !defined(USE_LWIPSOCK) #define setsockopt(a,b,c,d,e) (setsockopt)(a,b,c,(const char *)d,(int)e) #define SET_RCVTIMEO(tv,s) int tv = s*1000 #elif defined(LWIP_SO_SNDRCVTIMEO_NONSTANDARD) diff --git a/Utilities/cmcurl/lib/curl_sasl.c b/Utilities/cmcurl/lib/curl_sasl.c index 91ddf10..78ad298 100644 --- a/Utilities/cmcurl/lib/curl_sasl.c +++ b/Utilities/cmcurl/lib/curl_sasl.c @@ -262,6 +262,8 @@ static void sasl_state(struct SASL *sasl, struct Curl_easy *data, sasl->state = newstate; } +#if defined(USE_NTLM) || defined(USE_GSASL) || defined(USE_KERBEROS5) || \ + !defined(CURL_DISABLE_DIGEST_AUTH) /* Get the SASL server message and convert it to binary. */ static CURLcode get_server_message(struct SASL *sasl, struct Curl_easy *data, struct bufref *out) @@ -284,6 +286,7 @@ static CURLcode get_server_message(struct SASL *sasl, struct Curl_easy *data, } return result; } +#endif /* Encode the outgoing SASL message. */ static CURLcode build_message(struct SASL *sasl, struct bufref *msg) diff --git a/Utilities/cmcurl/lib/curl_setup.h b/Utilities/cmcurl/lib/curl_setup.h index 8557cf4..7fe6397 100644 --- a/Utilities/cmcurl/lib/curl_setup.h +++ b/Utilities/cmcurl/lib/curl_setup.h @@ -28,6 +28,11 @@ #define CURL_NO_OLDIES #endif +/* Set default _WIN32_WINNT */ +#ifdef __MINGW32__ +#include <_mingw.h> +#endif + /* * Disable Visual Studio warnings: * 4127 "conditional expression is constant" @@ -36,15 +41,7 @@ #pragma warning(disable:4127) #endif -/* - * Define WIN32 when build target is Win32 API - */ - -#if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32) -#define WIN32 -#endif - -#ifdef WIN32 +#ifdef _WIN32 /* * Don't include unneeded stuff in Windows headers to avoid compiler * warnings and macro clashes. @@ -82,7 +79,7 @@ #ifdef _WIN32_WCE # include "config-win32ce.h" #else -# ifdef WIN32 +# ifdef _WIN32 # include "config-win32.h" # endif #endif @@ -218,6 +215,23 @@ # define CURL_DISABLE_RTSP #endif +/* + * When HTTP is disabled, disable HTTP-only features + */ + +#if defined(CURL_DISABLE_HTTP) +# define CURL_DISABLE_ALTSVC 1 +# define CURL_DISABLE_COOKIES 1 +# define CURL_DISABLE_BASIC_AUTH 1 +# define CURL_DISABLE_BEARER_AUTH 1 +# define CURL_DISABLE_AWS 1 +# define CURL_DISABLE_DOH 1 +# define CURL_DISABLE_FORM_API 1 +# define CURL_DISABLE_HEADERS_API 1 +# define CURL_DISABLE_HSTS 1 +# define CURL_DISABLE_HTTP_AUTH 1 +#endif + /* ================================================================ */ /* No system header file shall be included in this file before this */ /* point. */ @@ -335,23 +349,6 @@ #include <curl/stdcheaders.h> #endif -#ifdef __POCC__ -# include <sys/types.h> -# include <unistd.h> -# define sys_nerr EILSEQ -#endif - -/* - * Salford-C kludge section (mostly borrowed from wxWidgets). - */ -#ifdef __SALFORDC__ - #pragma suppress 353 /* Possible nested comments */ - #pragma suppress 593 /* Define not used */ - #pragma suppress 61 /* enum has no name */ - #pragma suppress 106 /* unnamed, unused parameter */ - #include <clib.h> -#endif - /* Default Windows file API selection. */ #ifdef _WIN32 # if defined(_MSC_VER) && (_INTEGRAL_MAX_BITS >= 64) @@ -515,11 +512,11 @@ 5. set dir/file naming defines */ -#ifdef WIN32 +#ifdef _WIN32 # define DIR_CHAR "\\" -#else /* WIN32 */ +#else /* _WIN32 */ # ifdef MSDOS /* Watt-32 */ @@ -544,27 +541,7 @@ # define DIR_CHAR "/" -# ifndef fileno /* sunos 4 have this as a macro! */ - int fileno(FILE *stream); -# endif - -#endif /* WIN32 */ - -/* - * msvc 6.0 requires PSDK in order to have INET6_ADDRSTRLEN - * defined in ws2tcpip.h as well as to provide IPv6 support. - * Does not apply if lwIP is used. - */ - -#if defined(_MSC_VER) && !defined(__POCC__) && !defined(USE_LWIPSOCK) -# if !defined(HAVE_WS2TCPIP_H) || \ - ((_MSC_VER < 1300) && !defined(INET6_ADDRSTRLEN)) -# undef HAVE_GETADDRINFO_THREADSAFE -# undef HAVE_FREEADDRINFO -# undef HAVE_GETADDRINFO -# undef ENABLE_IPV6 -# endif -#endif +#endif /* _WIN32 */ /* ---------------------------------------------------------------- */ /* resolver specialty compile-time defines */ @@ -572,20 +549,11 @@ /* ---------------------------------------------------------------- */ /* - * lcc-win32 doesn't have _beginthreadex(), lacks threads support. - */ - -#if defined(__LCC__) && defined(WIN32) -# undef USE_THREADS_POSIX -# undef USE_THREADS_WIN32 -#endif - -/* * MSVC threads support requires a multi-threaded runtime library. * _beginthreadex() is not available in single-threaded ones. */ -#if defined(_MSC_VER) && !defined(__POCC__) && !defined(_MT) +#if defined(_MSC_VER) && !defined(_MT) # undef USE_THREADS_POSIX # undef USE_THREADS_WIN32 #endif @@ -596,6 +564,9 @@ #if defined(ENABLE_IPV6) && defined(HAVE_GETADDRINFO) # define CURLRES_IPV6 +#elif defined(ENABLE_IPV6) && (defined(_WIN32) || defined(__CYGWIN__)) +/* assume on Windows that IPv6 without getaddrinfo is a broken build */ +# error "Unexpected build: IPv6 is enabled but getaddrinfo was not found." #else # define CURLRES_IPV4 #endif @@ -615,35 +586,6 @@ /* ---------------------------------------------------------------- */ -/* - * msvc 6.0 does not have struct sockaddr_storage and - * does not define IPPROTO_ESP in winsock2.h. But both - * are available if PSDK is properly installed. - */ - -#if defined(_MSC_VER) && !defined(__POCC__) -# if !defined(HAVE_WINSOCK2_H) || ((_MSC_VER < 1300) && !defined(IPPROTO_ESP)) -# undef HAVE_STRUCT_SOCKADDR_STORAGE -# endif -#endif - -/* - * Intentionally fail to build when using msvc 6.0 without PSDK installed. - * The brave of heart can circumvent this, defining ALLOW_MSVC6_WITHOUT_PSDK - * in lib/config-win32.h although absolutely discouraged and unsupported. - */ - -#if defined(_MSC_VER) && !defined(__POCC__) -# if !defined(HAVE_WINDOWS_H) || ((_MSC_VER < 1300) && !defined(_FILETIME_)) -# if !defined(ALLOW_MSVC6_WITHOUT_PSDK) -# error MSVC 6.0 requires "February 2003 Platform SDK" a.k.a. \ - "Windows Server 2003 PSDK" -# else -# define CURL_DISABLE_LDAP 1 -# endif -# endif -#endif - #if defined(HAVE_LIBIDN2) && defined(HAVE_IDN2_H) && !defined(USE_WIN32_IDN) /* The lib and header are present */ #define USE_LIBIDN2 @@ -709,6 +651,18 @@ # define WARN_UNUSED_RESULT #endif +/* noreturn attribute */ + +#if !defined(CURL_NORETURN) +#if (defined(__GNUC__) && (__GNUC__ >= 3)) || defined(__clang__) +# define CURL_NORETURN __attribute__((__noreturn__)) +#elif defined(_MSC_VER) && (_MSC_VER >= 1200) +# define CURL_NORETURN __declspec(noreturn) +#else +# define CURL_NORETURN +#endif +#endif + /* * Include macros and defines that should only be processed once. */ @@ -767,7 +721,7 @@ /* In Windows the default file mode is text but an application can override it. Therefore we specify it explicitly. https://github.com/curl/curl/pull/258 */ -#if defined(WIN32) || defined(MSDOS) +#if defined(_WIN32) || defined(MSDOS) #define FOPEN_READTEXT "rt" #define FOPEN_WRITETEXT "wt" #define FOPEN_APPENDTEXT "at" @@ -822,7 +776,8 @@ int getpwuid_r(uid_t uid, struct passwd *pwd, char *buf, #define UNITTEST static #endif -#if defined(USE_NGHTTP2) || defined(USE_HYPER) +/* Hyper supports HTTP2 also, but Curl's integration with Hyper does not */ +#if defined(USE_NGHTTP2) #define USE_HTTP2 #endif @@ -835,11 +790,11 @@ int getpwuid_r(uid_t uid, struct passwd *pwd, char *buf, /* Certain Windows implementations are not aligned with what curl expects, so always use the local one on this platform. E.g. the mingw-w64 implementation can return wrong results for non-ASCII inputs. */ -#if defined(HAVE_BASENAME) && defined(WIN32) +#if defined(HAVE_BASENAME) && defined(_WIN32) #undef HAVE_BASENAME #endif -#if defined(USE_UNIX_SOCKETS) && defined(WIN32) +#if defined(USE_UNIX_SOCKETS) && defined(_WIN32) # if !defined(UNIX_PATH_MAX) /* Replicating logic present in afunix.h (distributed with newer Windows 10 SDK versions only) */ diff --git a/Utilities/cmcurl/lib/curl_setup_once.h b/Utilities/cmcurl/lib/curl_setup_once.h index c1ed059..bf0ee66 100644 --- a/Utilities/cmcurl/lib/curl_setup_once.h +++ b/Utilities/cmcurl/lib/curl_setup_once.h @@ -56,7 +56,7 @@ #include <sys/time.h> #endif -#ifdef WIN32 +#ifdef _WIN32 #include <io.h> #include <fcntl.h> #endif @@ -70,11 +70,7 @@ #endif #ifdef USE_WOLFSSL -# if defined(HAVE_STDINT_H) -# include <stdint.h> -# elif defined(HAVE_INTTYPES_H) -# include <inttypes.h> -# endif +#include <stdint.h> #endif #ifdef USE_SCHANNEL diff --git a/Utilities/cmcurl/lib/curl_sspi.h b/Utilities/cmcurl/lib/curl_sspi.h index 5af7c24..b26c391 100644 --- a/Utilities/cmcurl/lib/curl_sspi.h +++ b/Utilities/cmcurl/lib/curl_sspi.h @@ -88,6 +88,22 @@ extern PSecurityFunctionTable s_pSecFn; # define CRYPT_E_REVOKED ((HRESULT)0x80092010L) #endif +#ifndef CRYPT_E_NO_REVOCATION_DLL +# define CRYPT_E_NO_REVOCATION_DLL ((HRESULT)0x80092011L) +#endif + +#ifndef CRYPT_E_NO_REVOCATION_CHECK +# define CRYPT_E_NO_REVOCATION_CHECK ((HRESULT)0x80092012L) +#endif + +#ifndef CRYPT_E_REVOCATION_OFFLINE +# define CRYPT_E_REVOCATION_OFFLINE ((HRESULT)0x80092013L) +#endif + +#ifndef CRYPT_E_NOT_IN_REVOCATION_DATABASE +# define CRYPT_E_NOT_IN_REVOCATION_DATABASE ((HRESULT)0x80092014L) +#endif + #ifdef UNICODE # define SECFLAG_WINNT_AUTH_IDENTITY \ (unsigned long)SEC_WINNT_AUTH_IDENTITY_UNICODE diff --git a/Utilities/cmcurl/lib/curl_trc.c b/Utilities/cmcurl/lib/curl_trc.c index e53b305..0ebe40b 100644 --- a/Utilities/cmcurl/lib/curl_trc.c +++ b/Utilities/cmcurl/lib/curl_trc.c @@ -61,10 +61,6 @@ void Curl_debug(struct Curl_easy *data, curl_infotype type, "* ", "< ", "> ", "{ ", "} ", "{ ", "} " }; if(data->set.fdebug) { bool inCallback = Curl_is_in_callback(data); - /* CURLOPT_DEBUGFUNCTION doc says the user may set CURLOPT_PRIVATE to - distinguish their handle from internal handles. */ - if(data->internal) - DEBUGASSERT(!data->set.private_data); Curl_set_in_callback(data, true); (void)(*data->set.fdebug)(data, type, ptr, size, data->set.debugdata); Curl_set_in_callback(data, inCallback); @@ -109,6 +105,8 @@ void Curl_failf(struct Curl_easy *data, const char *fmt, ...) } } +#if !defined(CURL_DISABLE_VERBOSE_STRINGS) + /* Curl_infof() is for info message along the way */ #define MAXINFO 2048 @@ -128,13 +126,11 @@ void Curl_infof(struct Curl_easy *data, const char *fmt, ...) } } -#if !defined(CURL_DISABLE_VERBOSE_STRINGS) - void Curl_trc_cf_infof(struct Curl_easy *data, struct Curl_cfilter *cf, const char *fmt, ...) { DEBUGASSERT(cf); - if(data && Curl_trc_cf_is_verbose(cf, data)) { + if(Curl_trc_cf_is_verbose(cf, data)) { va_list ap; int len; char buffer[MAXINFO + 2]; @@ -232,24 +228,14 @@ CURLcode Curl_trc_init(void) if(config) { return Curl_trc_opt(config); } -#endif +#endif /* DEBUGBUILD */ return CURLE_OK; } -#else /* !CURL_DISABLE_VERBOSE_STRINGS) */ +#else /* defined(CURL_DISABLE_VERBOSE_STRINGS) */ CURLcode Curl_trc_init(void) { return CURLE_OK; } -#if !defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L) -void Curl_trc_cf_infof(struct Curl_easy *data, struct Curl_cfilter *cf, - const char *fmt, ...) -{ - (void)data; - (void)cf; - (void)fmt; -} -#endif - -#endif /* !DEBUGBUILD */ +#endif /* !defined(CURL_DISABLE_VERBOSE_STRINGS) */ diff --git a/Utilities/cmcurl/lib/curl_trc.h b/Utilities/cmcurl/lib/curl_trc.h index 84b5471..ade9108 100644 --- a/Utilities/cmcurl/lib/curl_trc.h +++ b/Utilities/cmcurl/lib/curl_trc.h @@ -55,19 +55,6 @@ void Curl_debug(struct Curl_easy *data, curl_infotype type, char *ptr, size_t size); /** - * Output an informational message when transfer's verbose logging is enabled. - */ -void Curl_infof(struct Curl_easy *data, -#if defined(__GNUC__) && !defined(printf) && \ - defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \ - !defined(__MINGW32__) - const char *fmt, ...) - __attribute__((format(printf, 2, 3))); -#else - const char *fmt, ...); -#endif - -/** * Output a failure message on registered callbacks for transfer. */ void Curl_failf(struct Curl_easy *data, @@ -82,39 +69,15 @@ void Curl_failf(struct Curl_easy *data, #define failf Curl_failf -/** - * Output an informational message when both transfer's verbose logging - * and connection filters verbose logging are enabled. - */ -void Curl_trc_cf_infof(struct Curl_easy *data, struct Curl_cfilter *cf, -#if defined(__GNUC__) && !defined(printf) && \ - defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \ - !defined(__MINGW32__) - const char *fmt, ...) - __attribute__((format(printf, 3, 4))); -#else - const char *fmt, ...); -#endif - #define CURL_LOG_LVL_NONE 0 #define CURL_LOG_LVL_INFO 1 -#if !defined(CURL_DISABLE_VERBOSE_STRINGS) -/* informational messages enabled */ - -#define Curl_trc_is_verbose(data) ((data) && (data)->set.verbose) -#define Curl_trc_cf_is_verbose(cf, data) \ - ((data) && (data)->set.verbose && \ - (cf) && (cf)->cft->log_level >= CURL_LOG_LVL_INFO) - -/* explainer: we have some mix configuration and werror settings - * that define HAVE_VARIADIC_MACROS_C99 even though C89 is enforced - * on gnuc and some other compiler. Need to treat carefully. - */ -#if defined(HAVE_VARIADIC_MACROS_C99) && \ - defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#define CURL_HAVE_C99 +#endif +#ifdef CURL_HAVE_C99 #define infof(data, ...) \ do { if(Curl_trc_is_verbose(data)) \ Curl_infof(data, __VA_ARGS__); } while(0) @@ -122,29 +85,62 @@ void Curl_trc_cf_infof(struct Curl_easy *data, struct Curl_cfilter *cf, do { if(Curl_trc_cf_is_verbose(cf, data)) \ Curl_trc_cf_infof(data, cf, __VA_ARGS__); } while(0) -#else /* no variadic macro args */ +#else #define infof Curl_infof #define CURL_TRC_CF Curl_trc_cf_infof -#endif /* variadic macro args */ +#endif + +#ifndef CURL_DISABLE_VERBOSE_STRINGS +/* informational messages enabled */ -#else /* !CURL_DISABLE_VERBOSE_STRINGS */ +#define Curl_trc_is_verbose(data) ((data) && (data)->set.verbose) +#define Curl_trc_cf_is_verbose(cf, data) \ + ((data) && (data)->set.verbose && \ + (cf) && (cf)->cft->log_level >= CURL_LOG_LVL_INFO) + +/** + * Output an informational message when transfer's verbose logging is enabled. + */ +void Curl_infof(struct Curl_easy *data, +#if defined(__GNUC__) && !defined(printf) && defined(CURL_HAVE_C99) && \ + !defined(__MINGW32__) + const char *fmt, ...) + __attribute__((format(printf, 2, 3))); +#else + const char *fmt, ...); +#endif + +/** + * Output an informational message when both transfer's verbose logging + * and connection filters verbose logging are enabled. + */ +void Curl_trc_cf_infof(struct Curl_easy *data, struct Curl_cfilter *cf, +#if defined(__GNUC__) && !defined(printf) && defined(CURL_HAVE_C99) && \ + !defined(__MINGW32__) + const char *fmt, ...) + __attribute__((format(printf, 3, 4))); +#else + const char *fmt, ...); +#endif + +#else /* defined(CURL_DISABLE_VERBOSE_STRINGS) */ /* All informational messages are not compiled in for size savings */ #define Curl_trc_is_verbose(d) ((void)(d), FALSE) #define Curl_trc_cf_is_verbose(x,y) ((void)(x), (void)(y), FALSE) -#if defined(HAVE_VARIADIC_MACROS_C99) -#define infof(...) Curl_nop_stmt -#define CURL_TRC_CF(...) Curl_nop_stmt -#define Curl_trc_cf_infof(...) Curl_nop_stmt -#elif defined(HAVE_VARIADIC_MACROS_GCC) -#define infof(x...) Curl_nop_stmt -#define CURL_TRC_CF(x...) Curl_nop_stmt -#define Curl_trc_cf_infof(x...) Curl_nop_stmt -#else -#error "missing VARIADIC macro define, fix and rebuild!" -#endif +static void Curl_infof(struct Curl_easy *data, const char *fmt, ...) +{ + (void)data; (void)fmt; +} + +static void Curl_trc_cf_infof(struct Curl_easy *data, + struct Curl_cfilter *cf, + const char *fmt, ...) +{ + (void)data; (void)cf; (void)fmt; +} -#endif /* CURL_DISABLE_VERBOSE_STRINGS */ +#endif /* !defined(CURL_DISABLE_VERBOSE_STRINGS) */ #endif /* HEADER_CURL_TRC_H */ diff --git a/Utilities/cmcurl/lib/doh.c b/Utilities/cmcurl/lib/doh.c index bb0c89e..1d928e9 100644 --- a/Utilities/cmcurl/lib/doh.c +++ b/Utilities/cmcurl/lib/doh.c @@ -242,7 +242,7 @@ static CURLcode dohprobe(struct Curl_easy *data, /* pass in the struct pointer via a local variable to please coverity and the gcc typecheck helpers */ struct dynbuf *resp = &p->serverdoh; - doh->internal = true; + doh->state.internal = true; ERROR_CHECK_SETOPT(CURLOPT_URL, url); ERROR_CHECK_SETOPT(CURLOPT_DEFAULT_PROTOCOL, "https"); ERROR_CHECK_SETOPT(CURLOPT_WRITEFUNCTION, doh_write_cb); @@ -252,6 +252,7 @@ static CURLcode dohprobe(struct Curl_easy *data, ERROR_CHECK_SETOPT(CURLOPT_HTTPHEADER, headers); #ifdef USE_HTTP2 ERROR_CHECK_SETOPT(CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS); + ERROR_CHECK_SETOPT(CURLOPT_PIPEWAIT, 1L); #endif #ifndef CURLDEBUG /* enforce HTTPS if not debug */ @@ -339,9 +340,10 @@ static CURLcode dohprobe(struct Curl_easy *data, doh->set.dohfor = data; /* identify for which transfer this is done */ p->easy = doh; - /* DoH private_data must be null because the user must have a way to - distinguish their transfer's handle from DoH handles in user - callbacks (ie SSL CTX callback). */ + /* DoH handles must not inherit private_data. The handles may be passed to + the user via callbacks and the user will be able to identify them as + internal handles because private data is not set. The user can then set + private_data via CURLOPT_PRIVATE if they so choose. */ DEBUGASSERT(!doh->set.private_data); if(curl_multi_add_handle(multi, doh)) @@ -372,7 +374,7 @@ struct Curl_addrinfo *Curl_doh(struct Curl_easy *data, int slot; struct dohdata *dohp; struct connectdata *conn = data->conn; - *waitp = TRUE; /* this never returns synchronously */ + *waitp = FALSE; (void)hostname; (void)port; @@ -380,7 +382,7 @@ struct Curl_addrinfo *Curl_doh(struct Curl_easy *data, DEBUGASSERT(conn); /* start clean, consider allocating this struct on demand */ - dohp = data->req.doh = calloc(sizeof(struct dohdata), 1); + dohp = data->req.doh = calloc(1, sizeof(struct dohdata)); if(!dohp) return NULL; @@ -412,12 +414,14 @@ struct Curl_addrinfo *Curl_doh(struct Curl_easy *data, dohp->pending++; } #endif + *waitp = TRUE; /* this never returns synchronously */ return NULL; error: curl_slist_free_all(dohp->headers); data->req.doh->headers = NULL; for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) { + (void)curl_multi_remove_handle(data->multi, dohp->probe[slot].easy); Curl_close(&dohp->probe[slot].easy); } Curl_safefree(data->req.doh); @@ -787,8 +791,8 @@ static void showdoh(struct Curl_easy *data, * must be an associated call later to Curl_freeaddrinfo(). */ -static struct Curl_addrinfo * -doh2ai(const struct dohentry *de, const char *hostname, int port) +static CURLcode doh2ai(const struct dohentry *de, const char *hostname, + int port, struct Curl_addrinfo **aip) { struct Curl_addrinfo *ai; struct Curl_addrinfo *prevai = NULL; @@ -801,9 +805,10 @@ doh2ai(const struct dohentry *de, const char *hostname, int port) int i; size_t hostlen = strlen(hostname) + 1; /* include null-terminator */ - if(!de) - /* no input == no output! */ - return NULL; + DEBUGASSERT(de); + + if(!de->numaddr) + return CURLE_COULDNT_RESOLVE_HOST; for(i = 0; i < de->numaddr; i++) { size_t ss_size; @@ -876,8 +881,9 @@ doh2ai(const struct dohentry *de, const char *hostname, int port) Curl_freeaddrinfo(firstai); firstai = NULL; } + *aip = firstai; - return firstai; + return result; } #ifndef CURL_DISABLE_VERBOSE_STRINGS @@ -898,6 +904,7 @@ UNITTEST void de_cleanup(struct dohentry *d) CURLcode Curl_doh_is_resolved(struct Curl_easy *data, struct Curl_dns_entry **dnsp) { + struct connectdata *conn = data->conn; CURLcode result; struct dohdata *dohp = data->req.doh; *dnsp = NULL; /* defaults to no response */ @@ -906,7 +913,7 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data, if(!dohp->probe[DOH_PROBE_SLOT_IPADDR_V4].easy && !dohp->probe[DOH_PROBE_SLOT_IPADDR_V6].easy) { - failf(data, "Could not DoH-resolve: %s", data->state.async.hostname); + failf(data, "Could not DoH-resolve: %s", conn->resolve_async.hostname); return CONN_IS_PROXIED(data->conn)?CURLE_COULDNT_RESOLVE_PROXY: CURLE_COULDNT_RESOLVE_HOST; } @@ -932,10 +939,12 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data, p->dnstype, &de); Curl_dyn_free(&p->serverdoh); +#ifndef CURL_DISABLE_VERBOSE_STRINGS if(rc[slot]) { infof(data, "DoH: %s type %s for %s", doh_strerror(rc[slot]), type2name(p->dnstype), dohp->host); } +#endif } /* next slot */ result = CURLE_COULDNT_RESOLVE_HOST; /* until we know better */ @@ -947,10 +956,10 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data, infof(data, "DoH Host name: %s", dohp->host); showdoh(data, &de); - ai = doh2ai(&de, dohp->host, dohp->port); - if(!ai) { + result = doh2ai(&de, dohp->host, dohp->port, &ai); + if(result) { de_cleanup(&de); - return CURLE_OUT_OF_MEMORY; + return result; } if(data->share) @@ -967,7 +976,7 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data, Curl_freeaddrinfo(ai); } else { - data->state.async.dns = dns; + conn->resolve_async.dns = dns; *dnsp = dns; result = CURLE_OK; /* address resolution OK */ } diff --git a/Utilities/cmcurl/lib/dynbuf.c b/Utilities/cmcurl/lib/dynbuf.c index 0c9c491..2973d8d 100644 --- a/Utilities/cmcurl/lib/dynbuf.c +++ b/Utilities/cmcurl/lib/dynbuf.c @@ -77,6 +77,7 @@ static CURLcode dyn_nappend(struct dynbuf *s, DEBUGASSERT(indx < s->toobig); DEBUGASSERT(!s->leng || s->bufr); DEBUGASSERT(a <= s->toobig); + DEBUGASSERT(!len || mem); if(fit > s->toobig) { Curl_dyn_free(s); @@ -174,10 +175,12 @@ CURLcode Curl_dyn_addn(struct dynbuf *s, const void *mem, size_t len) */ CURLcode Curl_dyn_add(struct dynbuf *s, const char *str) { - size_t n = strlen(str); + size_t n; + DEBUGASSERT(str); DEBUGASSERT(s); DEBUGASSERT(s->init == DYNINIT); DEBUGASSERT(!s->leng || s->bufr); + n = strlen(str); return dyn_nappend(s, (unsigned char *)str, n); } @@ -191,6 +194,7 @@ CURLcode Curl_dyn_vaddf(struct dynbuf *s, const char *fmt, va_list ap) DEBUGASSERT(s); DEBUGASSERT(s->init == DYNINIT); DEBUGASSERT(!s->leng || s->bufr); + DEBUGASSERT(fmt); rc = Curl_dyn_vprintf(s, fmt, ap); if(!rc) diff --git a/Utilities/cmcurl/lib/dynhds.c b/Utilities/cmcurl/lib/dynhds.c index 979b3e8..d754895 100644 --- a/Utilities/cmcurl/lib/dynhds.c +++ b/Utilities/cmcurl/lib/dynhds.c @@ -27,6 +27,10 @@ #include "strcase.h" /* The last 3 #include files should be in this order */ +#ifdef USE_NGHTTP2 +#include <stdint.h> +#include <nghttp2/nghttp2.h> +#endif /* USE_NGHTTP2 */ #include "curl_printf.h" #include "curl_memory.h" #include "memdebug.h" @@ -365,3 +369,28 @@ CURLcode Curl_dynhds_h1_dprint(struct dynhds *dynhds, struct dynbuf *dbuf) return result; } +#ifdef USE_NGHTTP2 + +nghttp2_nv *Curl_dynhds_to_nva(struct dynhds *dynhds, size_t *pcount) +{ + nghttp2_nv *nva = calloc(1, sizeof(nghttp2_nv) * dynhds->hds_len); + size_t i; + + *pcount = 0; + if(!nva) + return NULL; + + for(i = 0; i < dynhds->hds_len; ++i) { + struct dynhds_entry *e = dynhds->hds[i]; + DEBUGASSERT(e); + nva[i].name = (unsigned char *)e->name; + nva[i].namelen = e->namelen; + nva[i].value = (unsigned char *)e->value; + nva[i].valuelen = e->valuelen; + nva[i].flags = NGHTTP2_NV_FLAG_NONE; + } + *pcount = dynhds->hds_len; + return nva; +} + +#endif /* USE_NGHTTP2 */ diff --git a/Utilities/cmcurl/lib/dynhds.h b/Utilities/cmcurl/lib/dynhds.h index 8a05348..3b53600 100644 --- a/Utilities/cmcurl/lib/dynhds.h +++ b/Utilities/cmcurl/lib/dynhds.h @@ -171,4 +171,13 @@ CURLcode Curl_dynhds_h1_add_line(struct dynhds *dynhds, */ CURLcode Curl_dynhds_h1_dprint(struct dynhds *dynhds, struct dynbuf *dbuf); +#ifdef USE_NGHTTP2 + +#include <stdint.h> +#include <nghttp2/nghttp2.h> + +nghttp2_nv *Curl_dynhds_to_nva(struct dynhds *dynhds, size_t *pcount); + +#endif /* USE_NGHTTP2 */ + #endif /* HEADER_CURL_DYNHDS_H */ diff --git a/Utilities/cmcurl/lib/easy.c b/Utilities/cmcurl/lib/easy.c index 6b4fb8e..322d1a4 100644 --- a/Utilities/cmcurl/lib/easy.c +++ b/Utilities/cmcurl/lib/easy.c @@ -112,7 +112,7 @@ static curl_simple_lock s_lock = CURL_SIMPLE_LOCK_INIT; #define system_strdup strdup #endif -#if defined(_MSC_VER) && defined(_DLL) && !defined(__POCC__) +#if defined(_MSC_VER) && defined(_DLL) # pragma warning(disable:4232) /* MSVC extension, dllimport identity */ #endif @@ -125,11 +125,11 @@ curl_free_callback Curl_cfree = (curl_free_callback)free; curl_realloc_callback Curl_crealloc = (curl_realloc_callback)realloc; curl_strdup_callback Curl_cstrdup = (curl_strdup_callback)system_strdup; curl_calloc_callback Curl_ccalloc = (curl_calloc_callback)calloc; -#if defined(WIN32) && defined(UNICODE) +#if defined(_WIN32) && defined(UNICODE) curl_wcsdup_callback Curl_cwcsdup = Curl_wcsdup; #endif -#if defined(_MSC_VER) && defined(_DLL) && !defined(__POCC__) +#if defined(_MSC_VER) && defined(_DLL) # pragma warning(default:4232) /* MSVC extension, dllimport identity */ #endif @@ -153,7 +153,7 @@ static CURLcode global_init(long flags, bool memoryfuncs) Curl_crealloc = (curl_realloc_callback)realloc; Curl_cstrdup = (curl_strdup_callback)system_strdup; Curl_ccalloc = (curl_calloc_callback)calloc; -#if defined(WIN32) && defined(UNICODE) +#if defined(_WIN32) && defined(UNICODE) Curl_cwcsdup = (curl_wcsdup_callback)_wcsdup; #endif } @@ -188,18 +188,10 @@ static CURLcode global_init(long flags, bool memoryfuncs) goto fail; } -#if defined(USE_SSH) if(Curl_ssh_init()) { + DEBUGF(fprintf(stderr, "Error: Curl_ssh_init failed\n")); goto fail; } -#endif - -#ifdef USE_WOLFSSH - if(WS_SUCCESS != wolfSSH_Init()) { - DEBUGF(fprintf(stderr, "Error: wolfSSH_Init failed\n")); - return CURLE_FAILED_INIT; - } -#endif easy_init_flags = flags; @@ -295,7 +287,7 @@ void curl_global_cleanup(void) Curl_ssl_cleanup(); Curl_resolver_global_cleanup(); -#ifdef WIN32 +#ifdef _WIN32 Curl_win32_cleanup(easy_init_flags); #endif @@ -752,7 +744,7 @@ static CURLcode easy_perform(struct Curl_easy *data, bool events) return CURLE_RECURSIVE_API_CALL; /* Copy the MAXCONNECTS option to the multi handle */ - curl_multi_setopt(multi, CURLMOPT_MAXCONNECTS, data->set.maxconnects); + curl_multi_setopt(multi, CURLMOPT_MAXCONNECTS, (long)data->set.maxconnects); mcode = curl_multi_add_handle(multi, data); if(mcode) { @@ -845,8 +837,10 @@ static CURLcode dupset(struct Curl_easy *dst, struct Curl_easy *src) dst->set = src->set; Curl_mime_initpart(&dst->set.mimepost); - /* clear all string pointers first */ + /* clear all dest string and blob pointers first, in case we error out + mid-function */ memset(dst->set.str, 0, STRING_LAST * sizeof(char *)); + memset(dst->set.blobs, 0, BLOB_LAST * sizeof(struct curl_blob *)); /* duplicate all strings */ for(i = (enum dupstring)0; i< STRING_LASTZEROTERMINATED; i++) { @@ -855,8 +849,6 @@ static CURLcode dupset(struct Curl_easy *dst, struct Curl_easy *src) return result; } - /* clear all blob pointers first */ - memset(dst->set.blobs, 0, BLOB_LAST * sizeof(struct curl_blob *)); /* duplicate all blobs */ for(j = (enum dupblob)0; j < BLOB_LAST; j++) { result = Curl_setblobopt(&dst->set.blobs[j], src->set.blobs[j]); @@ -866,10 +858,13 @@ static CURLcode dupset(struct Curl_easy *dst, struct Curl_easy *src) /* duplicate memory areas pointed to */ i = STRING_COPYPOSTFIELDS; - if(src->set.postfieldsize && src->set.str[i]) { - /* postfieldsize is curl_off_t, Curl_memdup() takes a size_t ... */ - dst->set.str[i] = Curl_memdup(src->set.str[i], - curlx_sotouz(src->set.postfieldsize)); + if(src->set.str[i]) { + if(src->set.postfieldsize == -1) + dst->set.str[i] = strdup(src->set.str[i]); + else + /* postfieldsize is curl_off_t, Curl_memdup() takes a size_t ... */ + dst->set.str[i] = Curl_memdup(src->set.str[i], + curlx_sotouz(src->set.postfieldsize)); if(!dst->set.str[i]) return CURLE_OUT_OF_MEMORY; /* point to the new copy */ @@ -919,18 +914,19 @@ struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data) outcurl->progress.callback = data->progress.callback; #ifndef CURL_DISABLE_COOKIES - if(data->cookies) { + outcurl->state.cookielist = NULL; + if(data->cookies && data->state.cookie_engine) { /* If cookies are enabled in the parent handle, we enable them in the clone as well! */ - outcurl->cookies = Curl_cookie_init(data, NULL, outcurl->cookies, + outcurl->cookies = Curl_cookie_init(outcurl, NULL, outcurl->cookies, data->set.cookiesession); if(!outcurl->cookies) goto fail; } - if(data->set.cookielist) { - outcurl->set.cookielist = Curl_slist_duplicate(data->set.cookielist); - if(!outcurl->set.cookielist) + if(data->state.cookielist) { + outcurl->state.cookielist = Curl_slist_duplicate(data->state.cookielist); + if(!outcurl->state.cookielist) goto fail; } #endif @@ -976,33 +972,6 @@ struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data) (void)Curl_hsts_loadcb(outcurl, outcurl->hsts); } #endif - /* Clone the resolver handle, if present, for the new handle */ - if(Curl_resolver_duphandle(outcurl, - &outcurl->state.async.resolver, - data->state.async.resolver)) - goto fail; - -#ifdef USE_ARES - { - CURLcode rc; - - rc = Curl_set_dns_servers(outcurl, data->set.str[STRING_DNS_SERVERS]); - if(rc && rc != CURLE_NOT_BUILT_IN) - goto fail; - - rc = Curl_set_dns_interface(outcurl, data->set.str[STRING_DNS_INTERFACE]); - if(rc && rc != CURLE_NOT_BUILT_IN) - goto fail; - - rc = Curl_set_dns_local_ip4(outcurl, data->set.str[STRING_DNS_LOCAL_IP4]); - if(rc && rc != CURLE_NOT_BUILT_IN) - goto fail; - - rc = Curl_set_dns_local_ip6(outcurl, data->set.str[STRING_DNS_LOCAL_IP6]); - if(rc && rc != CURLE_NOT_BUILT_IN) - goto fail; - } -#endif /* USE_ARES */ Curl_initinfo(outcurl); @@ -1016,13 +985,10 @@ fail: if(outcurl) { #ifndef CURL_DISABLE_COOKIES - curl_slist_free_all(outcurl->set.cookielist); - outcurl->set.cookielist = NULL; + free(outcurl->cookies); #endif - Curl_safefree(outcurl->state.buffer); + free(outcurl->state.buffer); Curl_dyn_free(&outcurl->state.headerb); - Curl_safefree(outcurl->state.url); - Curl_safefree(outcurl->state.referer); Curl_altsvc_cleanup(&outcurl->asi); Curl_hsts_cleanup(&outcurl->hsts); Curl_freeset(outcurl); diff --git a/Utilities/cmcurl/lib/easy_lock.h b/Utilities/cmcurl/lib/easy_lock.h index d3fffd0..4f6764d 100644 --- a/Utilities/cmcurl/lib/easy_lock.h +++ b/Utilities/cmcurl/lib/easy_lock.h @@ -93,6 +93,15 @@ static inline void curl_simple_lock_unlock(curl_simple_lock *lock) atomic_store_explicit(lock, false, memory_order_release); } +#elif defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H) + +#include <pthread.h> + +#define curl_simple_lock pthread_mutex_t +#define CURL_SIMPLE_LOCK_INIT PTHREAD_MUTEX_INITIALIZER +#define curl_simple_lock_lock(m) pthread_mutex_lock(m) +#define curl_simple_lock_unlock(m) pthread_mutex_unlock(m) + #else #undef GLOBAL_INIT_IS_THREADSAFE diff --git a/Utilities/cmcurl/lib/file.c b/Utilities/cmcurl/lib/file.c index ffa9fb7..c985071 100644 --- a/Utilities/cmcurl/lib/file.c +++ b/Utilities/cmcurl/lib/file.c @@ -69,7 +69,7 @@ #include "curl_memory.h" #include "memdebug.h" -#if defined(WIN32) || defined(MSDOS) || defined(__EMX__) +#if defined(_WIN32) || defined(MSDOS) || defined(__EMX__) #define DOS_FILESYSTEM 1 #elif defined(__amigaos4__) #define AMIGA_FILESYSTEM 1 @@ -414,7 +414,6 @@ static CURLcode file_do(struct Curl_easy *data, bool *done) bool size_known; bool fstated = FALSE; char *buf = data->state.buffer; - curl_off_t bytecount = 0; int fd; struct FILEPROTO *file; @@ -563,7 +562,6 @@ static CURLcode file_do(struct Curl_easy *data, bool *done) if(nread <= 0 || (size_known && (expected_size == 0))) break; - bytecount += nread; if(size_known) expected_size -= nread; @@ -571,10 +569,6 @@ static CURLcode file_do(struct Curl_easy *data, bool *done) if(result) return result; - result = Curl_pgrsSetDownloadCounter(data, bytecount); - if(result) - return result; - if(Curl_pgrsUpdate(data)) result = CURLE_ABORTED_BY_CALLBACK; else diff --git a/Utilities/cmcurl/lib/fopen.c b/Utilities/cmcurl/lib/fopen.c index 75b8a7a..851279f 100644 --- a/Utilities/cmcurl/lib/fopen.c +++ b/Utilities/cmcurl/lib/fopen.c @@ -40,6 +40,51 @@ #include "memdebug.h" /* + The dirslash() function breaks a null-terminated pathname string into + directory and filename components then returns the directory component up + to, *AND INCLUDING*, a final '/'. If there is no directory in the path, + this instead returns a "" string. + + This function returns a pointer to malloc'ed memory. + + The input path to this function is expected to have a file name part. +*/ + +#ifdef _WIN32 +#define PATHSEP "\\" +#define IS_SEP(x) (((x) == '/') || ((x) == '\\')) +#elif defined(MSDOS) || defined(__EMX__) || defined(OS2) +#define PATHSEP "\\" +#define IS_SEP(x) ((x) == '\\') +#else +#define PATHSEP "/" +#define IS_SEP(x) ((x) == '/') +#endif + +static char *dirslash(const char *path) +{ + size_t n; + struct dynbuf out; + DEBUGASSERT(path); + Curl_dyn_init(&out, CURL_MAX_INPUT_LENGTH); + n = strlen(path); + if(n) { + /* find the rightmost path separator, if any */ + while(n && !IS_SEP(path[n-1])) + --n; + /* skip over all the path separators, if any */ + while(n && IS_SEP(path[n-1])) + --n; + } + if(Curl_dyn_addn(&out, path, n)) + return NULL; + /* if there was a directory, append a single trailing slash */ + if(n && Curl_dyn_addn(&out, PATHSEP, 1)) + return NULL; + return Curl_dyn_ptr(&out); +} + +/* * Curl_fopen() opens a file for writing with a temp name, to be renamed * to the final name when completed. If there is an existing file using this * name at the time of the open, this function will clone the mode from that @@ -50,47 +95,44 @@ CURLcode Curl_fopen(struct Curl_easy *data, const char *filename, FILE **fh, char **tempname) { CURLcode result = CURLE_WRITE_ERROR; - unsigned char randsuffix[9]; + unsigned char randbuf[41]; char *tempstore = NULL; struct_stat sb; int fd = -1; + char *dir = NULL; *tempname = NULL; *fh = fopen(filename, FOPEN_WRITETEXT); if(!*fh) goto fail; - if(fstat(fileno(*fh), &sb) == -1 || !S_ISREG(sb.st_mode)) + if(fstat(fileno(*fh), &sb) == -1 || !S_ISREG(sb.st_mode)) { return CURLE_OK; + } fclose(*fh); *fh = NULL; - result = Curl_rand_alnum(data, randsuffix, sizeof(randsuffix)); + result = Curl_rand_alnum(data, randbuf, sizeof(randbuf)); if(result) goto fail; - tempstore = aprintf("%s.%s.tmp", filename, randsuffix); + dir = dirslash(filename); + if(dir) { + /* The temp file name should not end up too long for the target file + system */ + tempstore = aprintf("%s%s.tmp", dir, randbuf); + free(dir); + } + if(!tempstore) { result = CURLE_OUT_OF_MEMORY; goto fail; } result = CURLE_WRITE_ERROR; - fd = open(tempstore, O_WRONLY | O_CREAT | O_EXCL, 0600); + fd = open(tempstore, O_WRONLY | O_CREAT | O_EXCL, 0600|sb.st_mode); if(fd == -1) goto fail; -#ifdef HAVE_FCHMOD - { - struct_stat nsb; - if((fstat(fd, &nsb) != -1) && - (nsb.st_uid == sb.st_uid) && (nsb.st_gid == sb.st_gid)) { - /* if the user and group are the same, clone the original mode */ - if(fchmod(fd, (mode_t)sb.st_mode) == -1) - goto fail; - } - } -#endif - *fh = fdopen(fd, FOPEN_WRITETEXT); if(!*fh) goto fail; @@ -105,7 +147,6 @@ fail: } free(tempstore); - return result; } diff --git a/Utilities/cmcurl/lib/formdata.c b/Utilities/cmcurl/lib/formdata.c index e40c4bc..05dc9b5 100644 --- a/Utilities/cmcurl/lib/formdata.c +++ b/Utilities/cmcurl/lib/formdata.c @@ -603,9 +603,9 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost, app passed in a bad combo, so we better check for that first. */ if(form->name) { /* copy name (without strdup; possibly not null-terminated) */ - form->name = Curl_memdup(form->name, form->namelength? - form->namelength: - strlen(form->name) + 1); + form->name = Curl_strndup(form->name, form->namelength? + form->namelength: + strlen(form->name)); } if(!form->name) { return_value = CURL_FORMADD_MEMORY; @@ -792,7 +792,7 @@ static CURLcode setname(curl_mimepart *part, const char *name, size_t len) /* wrap call to fseeko so it matches the calling convention of callback */ static int fseeko_wrapper(void *stream, curl_off_t offset, int whence) { -#if defined(HAVE_FSEEKO) +#if defined(HAVE_FSEEKO) && defined(HAVE_DECL_FSEEKO) return fseeko(stream, (off_t)offset, whence); #elif defined(HAVE__FSEEKI64) return _fseeki64(stream, (__int64)offset, whence); diff --git a/Utilities/cmcurl/lib/ftp.c b/Utilities/cmcurl/lib/ftp.c index 6e7fda0..28e8ca4 100644 --- a/Utilities/cmcurl/lib/ftp.c +++ b/Utilities/cmcurl/lib/ftp.c @@ -819,7 +819,7 @@ static int ftp_domore_getsock(struct Curl_easy *data, DEBUGF(infof(data, "ftp_domore_getsock()")); if(conn->cfilter[SECONDARYSOCKET] && !Curl_conn_is_connected(conn, SECONDARYSOCKET)) - return Curl_conn_get_select_socks(data, SECONDARYSOCKET, socks); + return 0; if(FTP_STOP == ftpc->state) { int bits = GETSOCK_READSOCK(0); @@ -947,7 +947,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, char *port_start = NULL; char *port_sep = NULL; - addr = calloc(addrlen + 1, 1); + addr = calloc(1, addrlen + 1); if(!addr) { result = CURLE_OUT_OF_MEMORY; goto out; @@ -4380,7 +4380,7 @@ static CURLcode ftp_setup_connection(struct Curl_easy *data, CURLcode result = CURLE_OK; struct ftp_conn *ftpc = &conn->proto.ftpc; - ftp = calloc(sizeof(struct FTP), 1); + ftp = calloc(1, sizeof(struct FTP)); if(!ftp) return CURLE_OUT_OF_MEMORY; diff --git a/Utilities/cmcurl/lib/ftplistparser.c b/Utilities/cmcurl/lib/ftplistparser.c index 2a7ca5b..82f1ea0 100644 --- a/Utilities/cmcurl/lib/ftplistparser.c +++ b/Utilities/cmcurl/lib/ftplistparser.c @@ -55,9 +55,6 @@ /* The last #include file should be: */ #include "memdebug.h" -/* allocs buffer which will contain one line of LIST command response */ -#define FTP_BUFFER_ALLOCSIZE 160 - typedef enum { PL_UNIX_TOTALSIZE = 0, PL_UNIX_FILETYPE, diff --git a/Utilities/cmcurl/lib/functypes.h b/Utilities/cmcurl/lib/functypes.h index 075c02e..ea66d32 100644 --- a/Utilities/cmcurl/lib/functypes.h +++ b/Utilities/cmcurl/lib/functypes.h @@ -38,7 +38,7 @@ 2. For systems with config-*.h files, define them there. */ -#ifdef WIN32 +#ifdef _WIN32 /* int recv(SOCKET, char *, int, int) */ #define RECV_TYPE_ARG1 SOCKET #define RECV_TYPE_ARG2 char * diff --git a/Utilities/cmcurl/lib/getenv.c b/Utilities/cmcurl/lib/getenv.c index 8069784..48ee972 100644 --- a/Utilities/cmcurl/lib/getenv.c +++ b/Utilities/cmcurl/lib/getenv.c @@ -31,10 +31,11 @@ static char *GetEnv(const char *variable) { -#if defined(_WIN32_WCE) || defined(CURL_WINDOWS_APP) +#if defined(_WIN32_WCE) || defined(CURL_WINDOWS_APP) || \ + defined(__ORBIS__) || defined(__PROSPERO__) /* PlayStation 4 and 5 */ (void)variable; return NULL; -#elif defined(WIN32) +#elif defined(_WIN32) /* This uses Windows API instead of C runtime getenv() to get the environment variable since some changes aren't always visible to the latter. #4774 */ char *buf = NULL; diff --git a/Utilities/cmcurl/lib/hostasyn.c b/Utilities/cmcurl/lib/hostasyn.c index 2f6762c..faf01c5 100644 --- a/Utilities/cmcurl/lib/hostasyn.c +++ b/Utilities/cmcurl/lib/hostasyn.c @@ -67,10 +67,11 @@ CURLcode Curl_addrinfo_callback(struct Curl_easy *data, int status, struct Curl_addrinfo *ai) { + struct connectdata *conn = data->conn; struct Curl_dns_entry *dns = NULL; CURLcode result = CURLE_OK; - data->state.async.status = status; + conn->resolve_async.status = status; if(CURL_ASYNC_SUCCESS == status) { if(ai) { @@ -78,8 +79,8 @@ CURLcode Curl_addrinfo_callback(struct Curl_easy *data, Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); dns = Curl_cache_addr(data, ai, - data->state.async.hostname, 0, - data->state.async.port); + conn->resolve_async.hostname, 0, + conn->resolve_async.port); if(data->share) Curl_share_unlock(data, CURL_LOCK_DATA_DNS); @@ -94,12 +95,12 @@ CURLcode Curl_addrinfo_callback(struct Curl_easy *data, } } - data->state.async.dns = dns; + conn->resolve_async.dns = dns; /* Set async.done TRUE last in this function since it may be used multi- threaded and once this is TRUE the other thread may read fields from the async struct */ - data->state.async.done = TRUE; + conn->resolve_async.done = TRUE; /* IPv4: The input hostent struct will be freed by ares when we return from this function */ diff --git a/Utilities/cmcurl/lib/hostip.c b/Utilities/cmcurl/lib/hostip.c index 3cd9a65..e7c318a 100644 --- a/Utilities/cmcurl/lib/hostip.c +++ b/Utilities/cmcurl/lib/hostip.c @@ -117,6 +117,13 @@ static void freednsentry(void *freethis); +#ifndef CURL_DISABLE_VERBOSE_STRINGS +static void show_resolve_info(struct Curl_easy *data, + struct Curl_dns_entry *dns); +#else +#define show_resolve_info(x,y) Curl_nop_stmt +#endif + /* * Curl_printable_address() stores a printable version of the 1st address * given in the 'ai' argument. The result will be stored in the buf that is @@ -481,9 +488,11 @@ Curl_cache_addr(struct Curl_easy *data, return NULL; } #endif + if(!hostlen) + hostlen = strlen(hostname); /* Create a new cache entry */ - dns = calloc(1, sizeof(struct Curl_dns_entry)); + dns = calloc(1, sizeof(struct Curl_dns_entry) + hostlen); if(!dns) { return NULL; } @@ -497,6 +506,9 @@ Curl_cache_addr(struct Curl_easy *data, time(&dns->timestamp); if(dns->timestamp == 0) dns->timestamp = 1; /* zero indicates permanent CURLOPT_RESOLVE entry */ + dns->hostport = port; + if(hostlen) + memcpy(dns->hostname, hostname, hostlen); /* Store the resolved data in our DNS cache. */ dns2 = Curl_hash_add(data->dns.hostcache, entry_id, entry_len + 1, @@ -521,7 +533,7 @@ static struct Curl_addrinfo *get_localhost6(int port, const char *name) struct sockaddr_in6 sa6; unsigned char ipv6[16]; unsigned short port16 = (unsigned short)(port & 0xffff); - ca = calloc(sizeof(struct Curl_addrinfo) + ss_size + hostlen + 1, 1); + ca = calloc(1, sizeof(struct Curl_addrinfo) + ss_size + hostlen + 1); if(!ca) return NULL; @@ -568,7 +580,7 @@ static struct Curl_addrinfo *get_localhost(int port, const char *name) return NULL; memcpy(&sa.sin_addr, &ipv4, sizeof(ipv4)); - ca = calloc(sizeof(struct Curl_addrinfo) + ss_size + hostlen + 1, 1); + ca = calloc(1, sizeof(struct Curl_addrinfo) + ss_size + hostlen + 1); if(!ca) return NULL; ca->ai_flags = 0; @@ -729,7 +741,7 @@ enum resolve_t Curl_resolv(struct Curl_easy *data, Curl_set_in_callback(data, true); st = data->set.resolver_start( #ifdef USE_CURL_ASYNC - data->state.async.resolver, + conn->resolve_async.resolver, #else NULL, #endif @@ -823,8 +835,10 @@ enum resolve_t Curl_resolv(struct Curl_easy *data, if(!dns) /* returned failure, bail out nicely */ Curl_freeaddrinfo(addr); - else + else { rc = CURLRESOLV_RESOLVED; + show_resolve_info(data, dns); + } } } @@ -839,7 +853,7 @@ enum resolve_t Curl_resolv(struct Curl_easy *data, * execution. This effectively causes the remainder of the application to run * within a signal handler which is nonportable and could lead to problems. */ -static +CURL_NORETURN static void alarmfunc(int sig) { (void)sig; @@ -1269,9 +1283,11 @@ err: Curl_freeaddrinfo(head); return CURLE_OUT_OF_MEMORY; } +#ifndef CURL_DISABLE_VERBOSE_STRINGS infof(data, "Added %.*s:%d:%s to DNS cache%s", (int)hlen, host_begin, port, addresses, permanent ? "" : " (non-permanent)"); +#endif /* Wildcard hostname */ if((hlen == 1) && (host_begin[0] == '*')) { @@ -1285,18 +1301,89 @@ err: return CURLE_OK; } +#ifndef CURL_DISABLE_VERBOSE_STRINGS +static void show_resolve_info(struct Curl_easy *data, + struct Curl_dns_entry *dns) +{ + struct Curl_addrinfo *a; + CURLcode result = CURLE_OK; +#ifdef CURLRES_IPV6 + struct dynbuf out[2]; +#else + struct dynbuf out[1]; +#endif + DEBUGASSERT(data); + DEBUGASSERT(dns); + + if(!data->set.verbose || + /* ignore no name or numerical IP addresses */ + !dns->hostname[0] || Curl_host_is_ipnum(dns->hostname)) + return; + + a = dns->addr; + + infof(data, "Host %s:%d was resolved.", + (dns->hostname[0] ? dns->hostname : "(none)"), dns->hostport); + + Curl_dyn_init(&out[0], 1024); +#ifdef CURLRES_IPV6 + Curl_dyn_init(&out[1], 1024); +#endif + + while(a) { + if( +#ifdef CURLRES_IPV6 + a->ai_family == PF_INET6 || +#endif + a->ai_family == PF_INET) { + char buf[MAX_IPADR_LEN]; + struct dynbuf *d = &out[(a->ai_family != PF_INET)]; + Curl_printable_address(a, buf, sizeof(buf)); + if(Curl_dyn_len(d)) + result = Curl_dyn_addn(d, ", ", 2); + if(!result) + result = Curl_dyn_add(d, buf); + if(result) { + infof(data, "too many IP, can't show"); + goto fail; + } + } + a = a->ai_next; + } + +#ifdef CURLRES_IPV6 + infof(data, "IPv6: %s", + (Curl_dyn_len(&out[1]) ? Curl_dyn_ptr(&out[1]) : "(none)")); +#endif + infof(data, "IPv4: %s", + (Curl_dyn_len(&out[0]) ? Curl_dyn_ptr(&out[0]) : "(none)")); + +fail: + Curl_dyn_free(&out[0]); +#ifdef CURLRES_IPV6 + Curl_dyn_free(&out[1]); +#endif +} +#endif + CURLcode Curl_resolv_check(struct Curl_easy *data, struct Curl_dns_entry **dns) { + CURLcode result; #if defined(CURL_DISABLE_DOH) && !defined(CURLRES_ASYNCH) (void)data; (void)dns; #endif #ifndef CURL_DISABLE_DOH - if(data->conn->bits.doh) - return Curl_doh_is_resolved(data, dns); + if(data->conn->bits.doh) { + result = Curl_doh_is_resolved(data, dns); + } + else #endif - return Curl_resolver_is_resolved(data, dns); + result = Curl_resolver_is_resolved(data, dns); + if(*dns) + show_resolve_info(data, *dns); + return result; } int Curl_resolv_getsock(struct Curl_easy *data, @@ -1328,9 +1415,9 @@ CURLcode Curl_once_resolved(struct Curl_easy *data, bool *protocol_done) struct connectdata *conn = data->conn; #ifdef USE_CURL_ASYNC - if(data->state.async.dns) { - conn->dns_entry = data->state.async.dns; - data->state.async.dns = NULL; + if(conn->resolve_async.dns) { + conn->dns_entry = conn->resolve_async.dns; + conn->resolve_async.dns = NULL; } #endif @@ -1352,11 +1439,11 @@ CURLcode Curl_once_resolved(struct Curl_easy *data, bool *protocol_done) #ifdef USE_CURL_ASYNC CURLcode Curl_resolver_error(struct Curl_easy *data) { + struct connectdata *conn = data->conn; const char *host_or_proxy; CURLcode result; #ifndef CURL_DISABLE_PROXY - struct connectdata *conn = data->conn; if(conn->bits.httpproxy) { host_or_proxy = "proxy"; result = CURLE_COULDNT_RESOLVE_PROXY; @@ -1369,7 +1456,7 @@ CURLcode Curl_resolver_error(struct Curl_easy *data) } failf(data, "Could not resolve %s: %s", host_or_proxy, - data->state.async.hostname); + conn->resolve_async.hostname); return result; } diff --git a/Utilities/cmcurl/lib/hostip.h b/Utilities/cmcurl/lib/hostip.h index b68f539..fb53a57 100644 --- a/Utilities/cmcurl/lib/hostip.h +++ b/Utilities/cmcurl/lib/hostip.h @@ -64,6 +64,10 @@ struct Curl_dns_entry { time_t timestamp; /* use-counter, use Curl_resolv_unlock to release reference */ long inuse; + /* hostname port number that resolved to addr. */ + int hostport; + /* hostname that resolved to addr. may be NULL (unix domain sockets). */ + char hostname[1]; }; bool Curl_host_is_ipnum(const char *hostname); diff --git a/Utilities/cmcurl/lib/hostip6.c b/Utilities/cmcurl/lib/hostip6.c index 6b0ba55..18969a7 100644 --- a/Utilities/cmcurl/lib/hostip6.c +++ b/Utilities/cmcurl/lib/hostip6.c @@ -71,8 +71,7 @@ bool Curl_ipvalid(struct Curl_easy *data, struct connectdata *conn) #if defined(CURLRES_SYNCH) #ifdef DEBUG_ADDRINFO -static void dump_addrinfo(struct connectdata *conn, - const struct Curl_addrinfo *ai) +static void dump_addrinfo(const struct Curl_addrinfo *ai) { printf("dump_addrinfo:\n"); for(; ai; ai = ai->ai_next) { @@ -84,7 +83,7 @@ static void dump_addrinfo(struct connectdata *conn, } } #else -#define dump_addrinfo(x,y) Curl_nop_stmt +#define dump_addrinfo(x) Curl_nop_stmt #endif /* @@ -149,7 +148,7 @@ struct Curl_addrinfo *Curl_getaddrinfo(struct Curl_easy *data, Curl_addrinfo_set_port(res, port); } - dump_addrinfo(conn, res); + dump_addrinfo(res); return res; } diff --git a/Utilities/cmcurl/lib/hsts.c b/Utilities/cmcurl/lib/hsts.c index 7ecf004..9314be2 100644 --- a/Utilities/cmcurl/lib/hsts.c +++ b/Utilities/cmcurl/lib/hsts.c @@ -40,6 +40,7 @@ #include "fopen.h" #include "rename.h" #include "share.h" +#include "strdup.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" @@ -76,7 +77,7 @@ static time_t hsts_debugtime(void *unused) struct hsts *Curl_hsts_init(void) { - struct hsts *h = calloc(sizeof(struct hsts), 1); + struct hsts *h = calloc(1, sizeof(struct hsts)); if(h) { Curl_llist_init(&h->list, NULL); } @@ -108,7 +109,7 @@ void Curl_hsts_cleanup(struct hsts **hp) static struct stsentry *hsts_entry(void) { - return calloc(sizeof(struct stsentry), 1); + return calloc(1, sizeof(struct stsentry)); } static CURLcode hsts_create(struct hsts *h, @@ -116,23 +117,30 @@ static CURLcode hsts_create(struct hsts *h, bool subdomains, curl_off_t expires) { - struct stsentry *sts = hsts_entry(); + struct stsentry *sts; char *duphost; size_t hlen; + DEBUGASSERT(h); + DEBUGASSERT(hostname); + + hlen = strlen(hostname); + if(hlen && (hostname[hlen - 1] == '.')) + /* strip off any trailing dot */ + --hlen; + if(!hlen) + /* no host name left */ + return CURLE_BAD_FUNCTION_ARGUMENT; + + sts = hsts_entry(); if(!sts) return CURLE_OUT_OF_MEMORY; - duphost = strdup(hostname); + duphost = Curl_strndup(hostname, hlen); if(!duphost) { free(sts); return CURLE_OUT_OF_MEMORY; } - hlen = strlen(duphost); - if(duphost[hlen - 1] == '.') - /* strip off trailing any dot */ - duphost[--hlen] = 0; - sts->host = duphost; sts->expires = expires; sts->includeSubDomains = subdomains; @@ -564,7 +572,7 @@ CURLcode Curl_hsts_loadcb(struct Curl_easy *data, struct hsts *h) void Curl_hsts_loadfiles(struct Curl_easy *data) { - struct curl_slist *l = data->set.hstslist; + struct curl_slist *l = data->state.hstslist; if(l) { Curl_share_lock(data, CURL_LOCK_DATA_HSTS, CURL_LOCK_ACCESS_SINGLE); diff --git a/Utilities/cmcurl/lib/http.c b/Utilities/cmcurl/lib/http.c index 40ef70d..be6d442 100644 --- a/Utilities/cmcurl/lib/http.c +++ b/Utilities/cmcurl/lib/http.c @@ -836,6 +836,7 @@ output_auth_headers(struct Curl_easy *data, (data->state.aptr.user ? data->state.aptr.user : "")); #else + (void)proxy; infof(data, "Server auth using %s with user '%s'", auth, data->state.aptr.user ? data->state.aptr.user : ""); @@ -845,7 +846,7 @@ output_auth_headers(struct Curl_easy *data, else authstatus->multipass = FALSE; - return CURLE_OK; + return result; } /** @@ -970,17 +971,21 @@ Curl_http_output_auth(struct Curl_easy *data, } #endif -/* - * Curl_http_input_auth() deals with Proxy-Authenticate: and WWW-Authenticate: - * headers. They are dealt with both in the transfer.c main loop and in the - * proxy CONNECT loop. - */ - +#if defined(USE_SPNEGO) || defined(USE_NTLM) || \ + !defined(CURL_DISABLE_DIGEST_AUTH) || \ + !defined(CURL_DISABLE_BASIC_AUTH) || \ + !defined(CURL_DISABLE_BEARER_AUTH) static int is_valid_auth_separator(char ch) { return ch == '\0' || ch == ',' || ISSPACE(ch); } +#endif +/* + * Curl_http_input_auth() deals with Proxy-Authenticate: and WWW-Authenticate: + * headers. They are dealt with both in the transfer.c main loop and in the + * proxy CONNECT loop. + */ CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy, const char *auth) /* the first non-space */ { @@ -992,11 +997,15 @@ CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy, curlnegotiate *negstate = proxy ? &conn->proxy_negotiate_state : &conn->http_negotiate_state; #endif +#if defined(USE_SPNEGO) || \ + defined(USE_NTLM) || \ + !defined(CURL_DISABLE_DIGEST_AUTH) || \ + !defined(CURL_DISABLE_BASIC_AUTH) || \ + !defined(CURL_DISABLE_BEARER_AUTH) + unsigned long *availp; struct auth *authp; - (void) conn; /* In case conditionals make it unused. */ - if(proxy) { availp = &data->info.proxyauthavail; authp = &data->state.authproxy; @@ -1005,6 +1014,11 @@ CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy, availp = &data->info.httpauthavail; authp = &data->state.authhost; } +#else + (void) proxy; +#endif + + (void) conn; /* In case conditionals make it unused. */ /* * Here we check if we want the specific single authentication (using ==) and @@ -1140,7 +1154,14 @@ CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy, } } #else - ; + { + /* + * Empty block to terminate the if-else chain correctly. + * + * A semicolon would yield the same result here, but can cause a + * compiler warning when -Wextra is enabled. + */ + } #endif /* there may be multiple methods on one line, so keep reading */ @@ -1403,7 +1424,7 @@ CURLcode Curl_buffer_send(struct dynbuf *in, * and install our own `data->state.fread_func` that * on subsequent calls reads `in` empty. * - when the whisked away `in` is empty, the `fread_func` - * is restored ot its original state. + * is restored to its original state. * The problem is that `fread_func` can only return * `upload_buffer_size` lengths. If the send we do here * is larger and blocks, we do re-sending with smaller @@ -1678,8 +1699,6 @@ static CURLcode expect100(struct Curl_easy *data, struct dynbuf *req) { CURLcode result = CURLE_OK; - data->state.expect100header = FALSE; /* default to false unless it is set - to TRUE below */ if(!data->state.disableexpect && Curl_use_http_1_1plus(data, conn) && (conn->httpversion < 20)) { /* if not doing HTTP 1.0 or version 2, or disabled explicitly, we add an @@ -2414,14 +2433,16 @@ CURLcode Curl_http_body(struct Curl_easy *data, struct connectdata *conn, /* Convert the form structure into a mime structure, then keep the conversion */ if(!data->state.formp) { - data->state.formp = calloc(sizeof(curl_mimepart), 1); + data->state.formp = calloc(1, sizeof(curl_mimepart)); if(!data->state.formp) return CURLE_OUT_OF_MEMORY; Curl_mime_cleanpart(data->state.formp); result = Curl_getformdata(data, data->state.formp, data->set.httppost, data->state.fread_func); - if(result) + if(result) { + Curl_safefree(data->state.formp); return result; + } data->state.mimepost = data->state.formp; } break; @@ -2494,6 +2515,29 @@ CURLcode Curl_http_body(struct Curl_easy *data, struct connectdata *conn, return result; } +static CURLcode addexpect(struct Curl_easy *data, struct connectdata *conn, + struct dynbuf *r) +{ + data->state.expect100header = FALSE; + /* Avoid Expect: 100-continue if Upgrade: is used */ + if(data->req.upgr101 == UPGR101_INIT) { + struct HTTP *http = data->req.p.http; + /* For really small puts we don't use Expect: headers at all, and for + the somewhat bigger ones we allow the app to disable it. Just make + sure that the expect100header is always set to the preferred value + here. */ + char *ptr = Curl_checkheaders(data, STRCONST("Expect")); + if(ptr) { + data->state.expect100header = + Curl_compareheader(ptr, STRCONST("Expect:"), + STRCONST("100-continue")); + } + else if(http->postsize > EXPECT_100_THRESHOLD || http->postsize < 0) + return expect100(data, conn, r); + } + return CURLE_OK; +} + CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn, struct dynbuf *r, Curl_HttpReq httpreq) { @@ -2506,14 +2550,8 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn, #endif CURLcode result = CURLE_OK; struct HTTP *http = data->req.p.http; - const char *ptr; - - /* If 'authdone' is FALSE, we must not set the write socket index to the - Curl_transfer() call below, as we're not ready to actually upload any - data yet. */ switch(httpreq) { - case HTTPREQ_PUT: /* Let's PUT the data to the server! */ if(conn->bits.authneg) @@ -2531,20 +2569,9 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn, return result; } - /* For really small puts we don't use Expect: headers at all, and for - the somewhat bigger ones we allow the app to disable it. Just make - sure that the expect100header is always set to the preferred value - here. */ - ptr = Curl_checkheaders(data, STRCONST("Expect")); - if(ptr) { - data->state.expect100header = - Curl_compareheader(ptr, STRCONST("Expect:"), STRCONST("100-continue")); - } - else if(http->postsize > EXPECT_100_THRESHOLD || http->postsize < 0) { - result = expect100(data, conn, r); - if(result) - return result; - } + result = addexpect(data, conn, r); + if(result) + return result; /* end of headers */ result = Curl_dyn_addn(r, STRCONST("\r\n")); @@ -2617,22 +2644,9 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn, } #endif - /* For really small posts we don't use Expect: headers at all, and for - the somewhat bigger ones we allow the app to disable it. Just make - sure that the expect100header is always set to the preferred value - here. */ - ptr = Curl_checkheaders(data, STRCONST("Expect")); - if(ptr) { - data->state.expect100header = - Curl_compareheader(ptr, STRCONST("Expect:"), STRCONST("100-continue")); - } - else if(http->postsize > EXPECT_100_THRESHOLD || http->postsize < 0) { - result = expect100(data, conn, r); - if(result) - return result; - } - else - data->state.expect100header = FALSE; + result = addexpect(data, conn, r); + if(result) + return result; /* make the request end in a true CRLF */ result = Curl_dyn_addn(r, STRCONST("\r\n")); @@ -2692,22 +2706,9 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn, return result; } - /* For really small posts we don't use Expect: headers at all, and for - the somewhat bigger ones we allow the app to disable it. Just make - sure that the expect100header is always set to the preferred value - here. */ - ptr = Curl_checkheaders(data, STRCONST("Expect")); - if(ptr) { - data->state.expect100header = - Curl_compareheader(ptr, STRCONST("Expect:"), STRCONST("100-continue")); - } - else if(http->postsize > EXPECT_100_THRESHOLD || http->postsize < 0) { - result = expect100(data, conn, r); - if(result) - return result; - } - else - data->state.expect100header = FALSE; + result = addexpect(data, conn, r); + if(result) + return result; #ifndef USE_HYPER /* With Hyper the body is always passed on separately */ @@ -3193,7 +3194,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) DEBUGASSERT(Curl_conn_is_http2(data, conn, FIRSTSOCKET)); break; case CURL_HTTP_VERSION_1_1: - /* continue with HTTP/1.1 when explicitly requested */ + /* continue with HTTP/1.x when explicitly requested */ break; default: /* Check if user wants to use HTTP/2 with clear TCP */ @@ -3685,7 +3686,7 @@ CURLcode Curl_http_header(struct Curl_easy *data, struct connectdata *conn, k->content_range = TRUE; } } - else + else if(k->httpcode < 300) data->state.resume_from = 0; /* get everything */ } #if !defined(CURL_DISABLE_COOKIES) @@ -3996,35 +3997,30 @@ CURLcode Curl_bump_headersize(struct Curl_easy *data, */ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, struct connectdata *conn, - ssize_t *nread, - bool *stop_reading) + const char *buf, size_t blen, + size_t *pconsumed) { CURLcode result; struct SingleRequest *k = &data->req; - ssize_t onread = *nread; - char *ostr = k->str; char *headp; - char *str_start; char *end_ptr; /* header line within buffer loop */ + *pconsumed = 0; do { - size_t rest_length; - size_t full_length; + size_t line_length; int writetype; - /* str_start is start of line within buf */ - str_start = k->str; - /* data is in network encoding so use 0x0a instead of '\n' */ - end_ptr = memchr(str_start, 0x0a, *nread); + end_ptr = memchr(buf, 0x0a, blen); if(!end_ptr) { /* Not a complete header line within buffer, append the data to the end of the headerbuff. */ - result = Curl_dyn_addn(&data->state.headerb, str_start, *nread); + result = Curl_dyn_addn(&data->state.headerb, buf, blen); if(result) return result; + *pconsumed += blen; if(!k->headerline) { /* check if this looks like a protocol header */ @@ -4036,31 +4032,28 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, if(st == STATUS_BAD) { /* this is not the beginning of a protocol first header line */ k->header = FALSE; - k->badheader = HEADER_ALLBAD; + k->badheader = TRUE; streamclose(conn, "bad HTTP: No end-of-message indicator"); if(!data->set.http09_allowed) { failf(data, "Received HTTP/0.9 when not allowed"); return CURLE_UNSUPPORTED_PROTOCOL; } - break; + goto out; } } - - break; /* read more and try again */ + goto out; /* read more and try again */ } /* decrease the size of the remaining (supposed) header line */ - rest_length = (end_ptr - k->str) + 1; - *nread -= (ssize_t)rest_length; - - k->str = end_ptr + 1; /* move past new line */ - - full_length = k->str - str_start; - - result = Curl_dyn_addn(&data->state.headerb, str_start, full_length); + line_length = (end_ptr - buf) + 1; + result = Curl_dyn_addn(&data->state.headerb, buf, line_length); if(result) return result; + blen -= line_length; + buf += line_length; + *pconsumed += line_length; + /**** * We now have a FULL header line in 'headerb'. *****/ @@ -4078,14 +4071,12 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, return CURLE_UNSUPPORTED_PROTOCOL; } k->header = FALSE; - if(*nread) + if(blen) /* since there's more, this is a partial bad header */ - k->badheader = HEADER_PARTHEADER; + k->badheader = TRUE; else { /* this was all we read so it's all a bad header */ - k->badheader = HEADER_ALLBAD; - *nread = onread; - k->str = ostr; + k->badheader = TRUE; return CURLE_OK; } break; @@ -4139,22 +4130,23 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, /* switch to http2 now. The bytes after response headers are also processed here, otherwise they are lost. */ - result = Curl_http2_upgrade(data, conn, FIRSTSOCKET, - k->str, *nread); + result = Curl_http2_upgrade(data, conn, FIRSTSOCKET, buf, blen); if(result) return result; - *nread = 0; + *pconsumed += blen; + blen = 0; } #ifdef USE_WEBSOCKETS else if(k->upgr101 == UPGR101_WS) { /* verify the response */ - result = Curl_ws_accept(data, k->str, *nread); + result = Curl_ws_accept(data, buf, blen); if(result) return result; k->header = FALSE; /* no more header to parse! */ if(data->set.connect_only) { k->keepon &= ~KEEP_RECV; /* read no more content */ - *nread = 0; + *pconsumed += blen; + blen = 0; } } #endif @@ -4366,7 +4358,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, * out and return home. */ if(data->req.no_body) - *stop_reading = TRUE; + k->download_done = TRUE; #ifndef CURL_DISABLE_RTSP else if((conn->handler->protocol & CURLPROTO_RTSP) && (data->set.rtspreq == RTSPREQ_DESCRIBE) && @@ -4375,7 +4367,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, absent, a length 0 must be assumed. It will prevent libcurl from hanging on DESCRIBE request that got refused for whatever reason */ - *stop_reading = TRUE; + k->download_done = TRUE; #endif /* If max download size is *zero* (nothing) we already have @@ -4386,15 +4378,12 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, if(0 == k->maxdownload && !Curl_conn_is_http2(data, conn, FIRSTSOCKET) && !Curl_conn_is_http3(data, conn, FIRSTSOCKET)) - *stop_reading = TRUE; + k->download_done = TRUE; - if(*stop_reading) { - /* we make sure that this socket isn't read more now */ - k->keepon &= ~KEEP_RECV; - } - - Curl_debug(data, CURLINFO_HEADER_IN, str_start, headerlen); - break; /* exit header line loop */ + Curl_debug(data, CURLINFO_HEADER_IN, + Curl_dyn_ptr(&data->state.headerb), + Curl_dyn_len(&data->state.headerb)); + goto out; /* exit header line loop */ } /* We continue reading headers, reset the line-based header */ @@ -4583,12 +4572,12 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, Curl_dyn_reset(&data->state.headerb); } - while(*k->str); /* header line within buffer */ + while(blen); /* We might have reached the end of the header part here, but there might be a non-header part left in the end of the read buffer. */ - +out: return CURLE_OK; } @@ -4618,17 +4607,6 @@ out: return result; } -/* simple implementation of strndup(), which isn't portable */ -static char *my_strndup(const char *ptr, size_t len) -{ - char *copy = malloc(len + 1); - if(!copy) - return NULL; - memcpy(copy, ptr, len); - copy[len] = '\0'; - return copy; -} - CURLcode Curl_http_req_make(struct httpreq **preq, const char *method, size_t m_len, const char *scheme, size_t s_len, @@ -4647,17 +4625,17 @@ CURLcode Curl_http_req_make(struct httpreq **preq, goto out; memcpy(req->method, method, m_len); if(scheme) { - req->scheme = my_strndup(scheme, s_len); + req->scheme = Curl_strndup(scheme, s_len); if(!req->scheme) goto out; } if(authority) { - req->authority = my_strndup(authority, a_len); + req->authority = Curl_strndup(authority, a_len); if(!req->authority) goto out; } if(path) { - req->path = my_strndup(path, p_len); + req->path = Curl_strndup(path, p_len); if(!req->path) goto out; } diff --git a/Utilities/cmcurl/lib/http.h b/Utilities/cmcurl/lib/http.h index 9ee3c65..56b0913 100644 --- a/Utilities/cmcurl/lib/http.h +++ b/Utilities/cmcurl/lib/http.h @@ -227,8 +227,8 @@ CURLcode Curl_http_size(struct Curl_easy *data); CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, struct connectdata *conn, - ssize_t *nread, - bool *stop_reading); + const char *buf, size_t blen, + size_t *pconsumed); /** * Curl_http_output_auth() setups the authentication headers for the @@ -263,7 +263,7 @@ CURLcode Curl_http_decode_status(int *pstatus, const char *s, size_t len); * All about a core HTTP request, excluding body and trailers */ struct httpreq { - char method[12]; + char method[24]; char *scheme; char *authority; char *path; diff --git a/Utilities/cmcurl/lib/http2.c b/Utilities/cmcurl/lib/http2.c index c8b0594..9738484 100644 --- a/Utilities/cmcurl/lib/http2.c +++ b/Utilities/cmcurl/lib/http2.c @@ -107,14 +107,14 @@ static int populate_settings(nghttp2_settings_entry *iv, return 3; } -static size_t populate_binsettings(uint8_t *binsettings, - struct Curl_easy *data) +static ssize_t populate_binsettings(uint8_t *binsettings, + struct Curl_easy *data) { nghttp2_settings_entry iv[H2_SETTINGS_IV_LEN]; int ivlen; ivlen = populate_settings(iv, data); - /* this returns number of bytes it wrote */ + /* this returns number of bytes it wrote or a negative number on error. */ return nghttp2_pack_settings_payload(binsettings, H2_BINSETTINGS_LEN, iv, ivlen); } @@ -369,12 +369,15 @@ static ssize_t nw_out_writer(void *writer_ctx, { struct Curl_cfilter *cf = writer_ctx; struct Curl_easy *data = CF_DATA_CURRENT(cf); - ssize_t nwritten; - nwritten = Curl_conn_cf_send(cf->next, data, (const char *)buf, buflen, err); - if(nwritten > 0) - CURL_TRC_CF(data, cf, "[0] egress: wrote %zd bytes", nwritten); - return nwritten; + if(data) { + ssize_t nwritten = Curl_conn_cf_send(cf->next, data, + (const char *)buf, buflen, err); + if(nwritten > 0) + CURL_TRC_CF(data, cf, "[0] egress: wrote %zd bytes", nwritten); + return nwritten; + } + return 0; } static ssize_t send_callback(nghttp2_session *h2, @@ -452,9 +455,14 @@ static CURLcode cf_h2_ctx_init(struct Curl_cfilter *cf, * in the H1 request and we upgrade from there. This stream * is opened implicitly as #1. */ uint8_t binsettings[H2_BINSETTINGS_LEN]; - size_t binlen; /* length of the binsettings data */ + ssize_t binlen; /* length of the binsettings data */ binlen = populate_binsettings(binsettings, data); + if(binlen <= 0) { + failf(data, "nghttp2 unexpectedly failed on pack_settings_payload"); + result = CURLE_FAILED_INIT; + goto out; + } result = http2_data_setup(cf, data, &stream); if(result) @@ -1076,16 +1084,11 @@ static CURLcode on_stream_frame(struct Curl_cfilter *cf, stream->reset = TRUE; } stream->send_closed = TRUE; - data->req.keepon &= ~KEEP_SEND_HOLD; drain_stream(cf, data, stream); break; case NGHTTP2_WINDOW_UPDATE: - if((data->req.keepon & KEEP_SEND_HOLD) && - (data->req.keepon & KEEP_SEND)) { - data->req.keepon &= ~KEEP_SEND_HOLD; + if(CURL_WANT_SEND(data)) { drain_stream(cf, data, stream); - CURL_TRC_CF(data, cf, "[%d] un-holding after win update", - stream_id); } break; default: @@ -1230,15 +1233,10 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame, * window and *assume* that we treat this like a WINDOW_UPDATE. Some * servers send an explicit WINDOW_UPDATE, but not all seem to do that. * To be safe, we UNHOLD a stream in order not to stall. */ - if((data->req.keepon & KEEP_SEND_HOLD) && - (data->req.keepon & KEEP_SEND)) { + if(CURL_WANT_SEND(data)) { struct stream_ctx *stream = H2_STREAM_CTX(data); - data->req.keepon &= ~KEEP_SEND_HOLD; - if(stream) { + if(stream) drain_stream(cf, data, stream); - CURL_TRC_CF(data, cf, "[%d] un-holding after SETTINGS", - stream_id); - } } } break; @@ -1338,7 +1336,6 @@ static int on_stream_close(nghttp2_session *session, int32_t stream_id, stream->error = error_code; if(stream->error) stream->reset = TRUE; - data_s->req.keepon &= ~KEEP_SEND_HOLD; if(stream->error) CURL_TRC_CF(data_s, cf, "[%d] RESET: %s (err %d)", @@ -1602,10 +1599,10 @@ static int error_callback(nghttp2_session *session, size_t len, void *userp) { + struct Curl_cfilter *cf = userp; + struct Curl_easy *data = CF_DATA_CURRENT(cf); (void)session; - (void)msg; - (void)len; - (void)userp; + failf(data, "%.*s", (int)len, msg); return 0; } #endif @@ -1621,7 +1618,7 @@ CURLcode Curl_http2_request_upgrade(struct dynbuf *req, size_t blen; struct SingleRequest *k = &data->req; uint8_t binsettings[H2_BINSETTINGS_LEN]; - size_t binlen; /* length of the binsettings data */ + ssize_t binlen; /* length of the binsettings data */ binlen = populate_binsettings(binsettings, data); if(binlen <= 0) { @@ -2052,23 +2049,13 @@ static ssize_t h2_submit(struct stream_ctx **pstream, /* no longer needed */ Curl_h1_req_parse_free(&stream->h1); - nheader = Curl_dynhds_count(&h2_headers); - nva = malloc(sizeof(nghttp2_nv) * nheader); + nva = Curl_dynhds_to_nva(&h2_headers, &nheader); if(!nva) { *err = CURLE_OUT_OF_MEMORY; nwritten = -1; goto out; } - for(i = 0; i < nheader; ++i) { - struct dynhds_entry *e = Curl_dynhds_getn(&h2_headers, i); - nva[i].name = (unsigned char *)e->name; - nva[i].namelen = e->namelen; - nva[i].value = (unsigned char *)e->value; - nva[i].valuelen = e->valuelen; - nva[i].flags = NGHTTP2_NV_FLAG_NONE; - } - h2_pri_spec(data, &pri_spec); if(!nghttp2_session_check_request_allowed(ctx->h2)) CURL_TRC_CF(data, cf, "send request NOT allowed (via nghttp2)"); @@ -2272,14 +2259,6 @@ static ssize_t cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data, * frame buffer or our network out buffer. */ size_t rwin = nghttp2_session_get_stream_remote_window_size(ctx->h2, stream->id); - if(rwin == 0) { - /* H2 flow window exhaustion. We need to HOLD upload until we get - * a WINDOW_UPDATE from the server. */ - data->req.keepon |= KEEP_SEND_HOLD; - CURL_TRC_CF(data, cf, "[%d] holding send as remote flow " - "window is exhausted", stream->id); - } - /* Whatever the cause, we need to return CURL_EAGAIN for this call. * We have unwritten state that needs us being invoked again and EAGAIN * is the only way to ensure that. */ @@ -2331,38 +2310,34 @@ out: return nwritten; } -static int cf_h2_get_select_socks(struct Curl_cfilter *cf, - struct Curl_easy *data, - curl_socket_t *sock) +static void cf_h2_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) { struct cf_h2_ctx *ctx = cf->ctx; - struct SingleRequest *k = &data->req; - struct stream_ctx *stream = H2_STREAM_CTX(data); - int bitmap = GETSOCK_BLANK; - struct cf_call_data save; + bool want_recv = CURL_WANT_RECV(data); + bool want_send = CURL_WANT_SEND(data); - CF_DATA_SAVE(save, cf, data); - sock[0] = Curl_conn_cf_get_socket(cf, data); - - if(!(k->keepon & (KEEP_RECV_PAUSE|KEEP_RECV_HOLD))) - /* Unless paused - in an HTTP/2 connection we can basically always get a - frame so we should always be ready for one */ - bitmap |= GETSOCK_READSOCK(0); - - /* we're (still uploading OR the HTTP/2 layer wants to send data) AND - there's a window to send data in */ - if((((k->keepon & KEEP_SENDBITS) == KEEP_SEND) || - nghttp2_session_want_write(ctx->h2)) && - (nghttp2_session_get_remote_window_size(ctx->h2) && - nghttp2_session_get_stream_remote_window_size(ctx->h2, - stream->id))) - bitmap |= GETSOCK_WRITESOCK(0); + if(ctx->h2 && (want_recv || want_send)) { + struct stream_ctx *stream = H2_STREAM_CTX(data); + curl_socket_t sock = Curl_conn_cf_get_socket(cf, data); + struct cf_call_data save; + bool c_exhaust, s_exhaust; - CF_DATA_RESTORE(cf, save); - return bitmap; + CF_DATA_SAVE(save, cf, data); + c_exhaust = !nghttp2_session_get_remote_window_size(ctx->h2); + s_exhaust = stream && stream->id >= 0 && + !nghttp2_session_get_stream_remote_window_size(ctx->h2, + stream->id); + want_recv = (want_recv || c_exhaust || s_exhaust); + want_send = (!s_exhaust && want_send) || + (!c_exhaust && nghttp2_session_want_write(ctx->h2)); + + Curl_pollset_set(data, ps, sock, want_recv, want_send); + CF_DATA_RESTORE(cf, save); + } } - static CURLcode cf_h2_connect(struct Curl_cfilter *cf, struct Curl_easy *data, bool blocking, bool *done) @@ -2511,14 +2486,15 @@ static CURLcode cf_h2_cntrl(struct Curl_cfilter *cf, case CF_CTRL_DATA_PAUSE: result = http2_data_pause(cf, data, (arg1 != 0)); break; - case CF_CTRL_DATA_DONE_SEND: { + case CF_CTRL_DATA_DONE_SEND: result = http2_data_done_send(cf, data); break; - } - case CF_CTRL_DATA_DONE: { + case CF_CTRL_DATA_DETACH: + http2_data_done(cf, data, TRUE); + break; + case CF_CTRL_DATA_DONE: http2_data_done(cf, data, arg1 != 0); break; - } default: break; } @@ -2606,7 +2582,7 @@ struct Curl_cftype Curl_cft_nghttp2 = { cf_h2_connect, cf_h2_close, Curl_cf_def_get_host, - cf_h2_get_select_socks, + cf_h2_adjust_pollset, cf_h2_data_pending, cf_h2_send, cf_h2_recv, @@ -2626,7 +2602,7 @@ static CURLcode http2_cfilter_add(struct Curl_cfilter **pcf, CURLcode result = CURLE_OUT_OF_MEMORY; DEBUGASSERT(data->conn); - ctx = calloc(sizeof(*ctx), 1); + ctx = calloc(1, sizeof(*ctx)); if(!ctx) goto out; @@ -2652,7 +2628,7 @@ static CURLcode http2_cfilter_insert_after(struct Curl_cfilter *cf, CURLcode result = CURLE_OUT_OF_MEMORY; (void)data; - ctx = calloc(sizeof(*ctx), 1); + ctx = calloc(1, sizeof(*ctx)); if(!ctx) goto out; diff --git a/Utilities/cmcurl/lib/http_aws_sigv4.c b/Utilities/cmcurl/lib/http_aws_sigv4.c index 901c22f..b673055 100644 --- a/Utilities/cmcurl/lib/http_aws_sigv4.c +++ b/Utilities/cmcurl/lib/http_aws_sigv4.c @@ -456,6 +456,7 @@ static CURLcode canon_query(struct Curl_easy *data, for(i = 0; !result && (i < entry); i++, ap++) { size_t len; const char *q = ap->p; + bool found_equals = false; if(!ap->len) continue; for(len = ap->len; len && !result; q++, len--) { @@ -467,9 +468,13 @@ static CURLcode canon_query(struct Curl_easy *data, case '.': case '_': case '~': + /* allowed as-is */ + result = Curl_dyn_addn(dq, q, 1); + break; case '=': /* allowed as-is */ result = Curl_dyn_addn(dq, q, 1); + found_equals = true; break; case '%': /* uppercase the following if hexadecimal */ @@ -497,7 +502,11 @@ static CURLcode canon_query(struct Curl_easy *data, } } } - if(i < entry - 1) { + if(!result && !found_equals) { + /* queries without value still need an equals */ + result = Curl_dyn_addn(dq, "=", 1); + } + if(!result && i < entry - 1) { /* insert ampersands between query pairs */ result = Curl_dyn_addn(dq, "&", 1); } diff --git a/Utilities/cmcurl/lib/http_chunks.c b/Utilities/cmcurl/lib/http_chunks.c index 2a401d1..acdb108 100644 --- a/Utilities/cmcurl/lib/http_chunks.c +++ b/Utilities/cmcurl/lib/http_chunks.c @@ -75,8 +75,6 @@ */ -#define isxdigit_ascii(x) Curl_isxdigit(x) - void Curl_httpchunk_init(struct Curl_easy *data) { struct connectdata *conn = data->conn; @@ -98,9 +96,9 @@ void Curl_httpchunk_init(struct Curl_easy *data) * For example, 0x0d and 0x0a are used instead of '\r' and '\n'. */ CHUNKcode Curl_httpchunk_read(struct Curl_easy *data, - char *datap, - ssize_t datalen, - ssize_t *wrote, + char *buf, + size_t blen, + size_t *pconsumed, CURLcode *extrap) { CURLcode result = CURLE_OK; @@ -108,28 +106,27 @@ CHUNKcode Curl_httpchunk_read(struct Curl_easy *data, struct Curl_chunker *ch = &conn->chunk; struct SingleRequest *k = &data->req; size_t piece; - curl_off_t length = (curl_off_t)datalen; - *wrote = 0; /* nothing's written yet */ + *pconsumed = 0; /* nothing's written yet */ /* the original data is written to the client, but we go on with the chunk read process, to properly calculate the content length */ if(data->set.http_te_skip && !k->ignorebody) { - result = Curl_client_write(data, CLIENTWRITE_BODY, datap, datalen); + result = Curl_client_write(data, CLIENTWRITE_BODY, buf, blen); if(result) { *extrap = result; return CHUNKE_PASSTHRU_ERROR; } } - while(length) { + while(blen) { switch(ch->state) { case CHUNK_HEX: - if(ISXDIGIT(*datap)) { + if(ISXDIGIT(*buf)) { if(ch->hexindex < CHUNK_MAXNUM_LEN) { - ch->hexbuffer[ch->hexindex] = *datap; - datap++; - length--; + ch->hexbuffer[ch->hexindex] = *buf; + buf++; + blen--; ch->hexindex++; } else { @@ -143,7 +140,7 @@ CHUNKcode Curl_httpchunk_read(struct Curl_easy *data, a hexadecimal digit. */ return CHUNKE_ILLEGAL_HEX; - /* length and datap are unmodified */ + /* blen and buf are unmodified */ ch->hexbuffer[ch->hexindex] = 0; if(curlx_strtoofft(ch->hexbuffer, &endptr, 16, &ch->datasize)) @@ -154,7 +151,7 @@ CHUNKcode Curl_httpchunk_read(struct Curl_easy *data, case CHUNK_LF: /* waiting for the LF after a chunk size */ - if(*datap == 0x0a) { + if(*buf == 0x0a) { /* we're now expecting data to come, unless size was zero! */ if(0 == ch->datasize) { ch->state = CHUNK_TRAILER; /* now check for trailers */ @@ -163,19 +160,21 @@ CHUNKcode Curl_httpchunk_read(struct Curl_easy *data, ch->state = CHUNK_DATA; } - datap++; - length--; + buf++; + blen--; break; case CHUNK_DATA: - /* We expect 'datasize' of data. We have 'length' right now, it can be + /* We expect 'datasize' of data. We have 'blen' right now, it can be more or less than 'datasize'. Get the smallest piece. */ - piece = curlx_sotouz((ch->datasize >= length)?length:ch->datasize); + piece = blen; + if(ch->datasize < (curl_off_t)blen) + piece = curlx_sotouz(ch->datasize); /* Write the data portion available */ if(!data->set.http_te_skip && !k->ignorebody) { - result = Curl_client_write(data, CLIENTWRITE_BODY, datap, piece); + result = Curl_client_write(data, CLIENTWRITE_BODY, buf, piece); if(result) { *extrap = result; @@ -183,10 +182,10 @@ CHUNKcode Curl_httpchunk_read(struct Curl_easy *data, } } - *wrote += piece; + *pconsumed += piece; ch->datasize -= piece; /* decrease amount left to expect */ - datap += piece; /* move read pointer forward */ - length -= piece; /* decrease space left in this round */ + buf += piece; /* move read pointer forward */ + blen -= piece; /* decrease space left in this round */ if(0 == ch->datasize) /* end of data this round, we now expect a trailing CRLF */ @@ -194,18 +193,18 @@ CHUNKcode Curl_httpchunk_read(struct Curl_easy *data, break; case CHUNK_POSTLF: - if(*datap == 0x0a) { + if(*buf == 0x0a) { /* The last one before we go back to hex state and start all over. */ Curl_httpchunk_init(data); /* sets state back to CHUNK_HEX */ } - else if(*datap != 0x0d) + else if(*buf != 0x0d) return CHUNKE_BAD_CHUNK; - datap++; - length--; + buf++; + blen--; break; case CHUNK_TRAILER: - if((*datap == 0x0d) || (*datap == 0x0a)) { + if((*buf == 0x0d) || (*buf == 0x0a)) { char *tr = Curl_dyn_ptr(&conn->trailer); /* this is the end of a trailer, but if the trailer was zero bytes there was no trailer and we move on */ @@ -229,7 +228,7 @@ CHUNKcode Curl_httpchunk_read(struct Curl_easy *data, } Curl_dyn_reset(&conn->trailer); ch->state = CHUNK_TRAILER_CR; - if(*datap == 0x0a) + if(*buf == 0x0a) /* already on the LF */ break; } @@ -240,19 +239,19 @@ CHUNKcode Curl_httpchunk_read(struct Curl_easy *data, } } else { - result = Curl_dyn_addn(&conn->trailer, datap, 1); + result = Curl_dyn_addn(&conn->trailer, buf, 1); if(result) return CHUNKE_OUT_OF_MEMORY; } - datap++; - length--; + buf++; + blen--; break; case CHUNK_TRAILER_CR: - if(*datap == 0x0a) { + if(*buf == 0x0a) { ch->state = CHUNK_TRAILER_POSTCR; - datap++; - length--; + buf++; + blen--; } else return CHUNKE_BAD_CHUNK; @@ -261,27 +260,27 @@ CHUNKcode Curl_httpchunk_read(struct Curl_easy *data, case CHUNK_TRAILER_POSTCR: /* We enter this state when a CR should arrive so we expect to have to first pass a CR before we wait for LF */ - if((*datap != 0x0d) && (*datap != 0x0a)) { + if((*buf != 0x0d) && (*buf != 0x0a)) { /* not a CR then it must be another header in the trailer */ ch->state = CHUNK_TRAILER; break; } - if(*datap == 0x0d) { + if(*buf == 0x0d) { /* skip if CR */ - datap++; - length--; + buf++; + blen--; } /* now wait for the final LF */ ch->state = CHUNK_STOP; break; case CHUNK_STOP: - if(*datap == 0x0a) { - length--; + if(*buf == 0x0a) { + blen--; /* Record the length of any data left in the end of the buffer even if there's no more chunks to read */ - ch->datasize = curlx_sotouz(length); + ch->datasize = blen; return CHUNKE_STOP; /* return stop */ } diff --git a/Utilities/cmcurl/lib/http_chunks.h b/Utilities/cmcurl/lib/http_chunks.h index ed50713..0a36f37 100644 --- a/Utilities/cmcurl/lib/http_chunks.h +++ b/Utilities/cmcurl/lib/http_chunks.h @@ -93,8 +93,8 @@ struct Curl_chunker { /* The following functions are defined in http_chunks.c */ void Curl_httpchunk_init(struct Curl_easy *data); -CHUNKcode Curl_httpchunk_read(struct Curl_easy *data, char *datap, - ssize_t length, ssize_t *wrote, +CHUNKcode Curl_httpchunk_read(struct Curl_easy *data, char *buf, + size_t blen, size_t *pconsumed, CURLcode *passthru); #endif /* HEADER_CURL_HTTP_CHUNKS_H */ diff --git a/Utilities/cmcurl/lib/http_proxy.c b/Utilities/cmcurl/lib/http_proxy.c index a1d6da9..8e18325 100644 --- a/Utilities/cmcurl/lib/http_proxy.c +++ b/Utilities/cmcurl/lib/http_proxy.c @@ -299,7 +299,7 @@ struct Curl_cftype Curl_cft_http_proxy = { http_proxy_cf_connect, http_proxy_cf_close, Curl_cf_http_proxy_get_host, - Curl_cf_def_get_select_socks, + Curl_cf_def_adjust_pollset, Curl_cf_def_data_pending, Curl_cf_def_send, Curl_cf_def_recv, diff --git a/Utilities/cmcurl/lib/idn.c b/Utilities/cmcurl/lib/idn.c index a024691..81a177f 100644 --- a/Utilities/cmcurl/lib/idn.c +++ b/Utilities/cmcurl/lib/idn.c @@ -36,7 +36,7 @@ #ifdef USE_LIBIDN2 #include <idn2.h> -#if defined(WIN32) && defined(UNICODE) +#if defined(_WIN32) && defined(UNICODE) #define IDN2_LOOKUP(name, host, flags) \ idn2_lookup_u8((const uint8_t *)name, (uint8_t **)host, flags) #else diff --git a/Utilities/cmcurl/lib/imap.c b/Utilities/cmcurl/lib/imap.c index de64c2a..47cff48 100644 --- a/Utilities/cmcurl/lib/imap.c +++ b/Utilities/cmcurl/lib/imap.c @@ -1194,8 +1194,6 @@ static CURLcode imap_state_fetch_resp(struct Curl_easy *data, if(result) return result; - data->req.bytecount += chunk; - infof(data, "Written %zu bytes, %" CURL_FORMAT_CURL_OFF_TU " bytes are left for transfer", chunk, size - chunk); @@ -1430,7 +1428,7 @@ static CURLcode imap_init(struct Curl_easy *data) CURLcode result = CURLE_OK; struct IMAP *imap; - imap = data->req.p.imap = calloc(sizeof(struct IMAP), 1); + imap = data->req.p.imap = calloc(1, sizeof(struct IMAP)); if(!imap) result = CURLE_OUT_OF_MEMORY; diff --git a/Utilities/cmcurl/lib/ldap.c b/Utilities/cmcurl/lib/ldap.c index 239d3fb..eb5fe795 100644 --- a/Utilities/cmcurl/lib/ldap.c +++ b/Utilities/cmcurl/lib/ldap.c @@ -313,7 +313,6 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) int ldap_ssl = 0; char *val_b64 = NULL; size_t val_b64_sz = 0; - curl_off_t dlsize = 0; #ifdef LDAP_OPT_NETWORK_TIMEOUT struct timeval ldap_timeout = {10, 0}; /* 10 sec connection/search timeout */ #endif @@ -327,7 +326,7 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) *done = TRUE; /* unconditionally */ infof(data, "LDAP local: LDAP Vendor = %s ; LDAP Version = %d", - LDAP_VENDOR_NAME, LDAP_VENDOR_VERSION); + LDAP_VENDOR_NAME, LDAP_VENDOR_VERSION); infof(data, "LDAP local: %s", data->state.url); #ifdef HAVE_LDAP_URL_PARSE @@ -345,7 +344,7 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) if(conn->given->flags & PROTOPT_SSL) ldap_ssl = 1; infof(data, "LDAP local: trying to establish %s connection", - ldap_ssl ? "encrypted" : "cleartext"); + ldap_ssl ? "encrypted" : "cleartext"); #if defined(USE_WIN32_LDAP) host = curlx_convert_UTF8_to_tchar(conn->host.name); @@ -535,6 +534,7 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) goto quit; } + Curl_pgrsSetDownloadCounter(data, 0); rc = ldap_search_s(server, ludp->lud_dn, ludp->lud_scope, ludp->lud_filter, ludp->lud_attrs, 0, &ldapmsg); @@ -596,8 +596,6 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) goto quit; } - dlsize += name_len + 5; - FREE_ON_WINLDAP(name); ldap_memfree(dn); } @@ -659,8 +657,6 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) goto quit; } - dlsize += attr_len + 3; - if((attr_len > 7) && (strcmp(";binary", attr + (attr_len - 7)) == 0)) { /* Binary attribute, encode to base64. */ @@ -689,8 +685,6 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) goto quit; } - - dlsize += val_b64_sz; } } else { @@ -705,8 +699,6 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) goto quit; } - - dlsize += vals[i]->bv_len; } result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)"\n", 1); @@ -719,8 +711,6 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) goto quit; } - - dlsize++; } /* Free memory used to store values */ @@ -734,10 +724,6 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)"\n", 1); if(result) goto quit; - dlsize++; - result = Curl_pgrsSetDownloadCounter(data, dlsize); - if(result) - goto quit; } if(ber) diff --git a/Utilities/cmcurl/lib/md4.c b/Utilities/cmcurl/lib/md4.c index 30ab62e..486e5fa 100644 --- a/Utilities/cmcurl/lib/md4.c +++ b/Utilities/cmcurl/lib/md4.c @@ -32,9 +32,8 @@ #include "warnless.h" #ifdef USE_OPENSSL -#include <openssl/opensslconf.h> -#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3) && \ - !defined(USE_AMISSL) +#include <openssl/opensslv.h> +#if (OPENSSL_VERSION_NUMBER >= 0x30000000L) && !defined(USE_AMISSL) /* OpenSSL 3.0.0 marks the MD4 functions as deprecated */ #define OPENSSL_NO_MD4 #endif diff --git a/Utilities/cmcurl/lib/memdebug.c b/Utilities/cmcurl/lib/memdebug.c index d6952a0..f6ced85 100644 --- a/Utilities/cmcurl/lib/memdebug.c +++ b/Utilities/cmcurl/lib/memdebug.c @@ -208,7 +208,7 @@ ALLOC_FUNC char *curl_dbg_strdup(const char *str, return mem; } -#if defined(WIN32) && defined(UNICODE) +#if defined(_WIN32) && defined(UNICODE) ALLOC_FUNC wchar_t *curl_dbg_wcsdup(const wchar_t *str, int line, const char *source) { diff --git a/Utilities/cmcurl/lib/memdebug.h b/Utilities/cmcurl/lib/memdebug.h index c9eb5dc..78a0125 100644 --- a/Utilities/cmcurl/lib/memdebug.h +++ b/Utilities/cmcurl/lib/memdebug.h @@ -64,7 +64,7 @@ CURL_EXTERN ALLOC_SIZE(2) void *curl_dbg_realloc(void *ptr, CURL_EXTERN void curl_dbg_free(void *ptr, int line, const char *source); CURL_EXTERN ALLOC_FUNC char *curl_dbg_strdup(const char *str, int line, const char *src); -#if defined(WIN32) && defined(UNICODE) +#if defined(_WIN32) && defined(UNICODE) CURL_EXTERN ALLOC_FUNC wchar_t *curl_dbg_wcsdup(const wchar_t *str, int line, const char *source); @@ -121,7 +121,7 @@ CURL_EXTERN int curl_dbg_fclose(FILE *file, int line, const char *source); #define send(a,b,c,d) curl_dbg_send(a,b,c,d, __LINE__, __FILE__) #define recv(a,b,c,d) curl_dbg_recv(a,b,c,d, __LINE__, __FILE__) -#ifdef WIN32 +#ifdef _WIN32 # ifdef UNICODE # undef wcsdup # define wcsdup(ptr) curl_dbg_wcsdup(ptr, __LINE__, __FILE__) diff --git a/Utilities/cmcurl/lib/mime.c b/Utilities/cmcurl/lib/mime.c index 3b27e59..bb66130 100644 --- a/Utilities/cmcurl/lib/mime.c +++ b/Utilities/cmcurl/lib/mime.c @@ -48,7 +48,7 @@ #include "curl_memory.h" #include "memdebug.h" -#ifdef WIN32 +#ifdef _WIN32 # ifndef R_OK # define R_OK 4 # endif @@ -311,8 +311,7 @@ static char *escape_string(struct Curl_easy *data, table = formtable; /* data can be NULL when this function is called indirectly from curl_formget(). */ - if(strategy == MIMESTRATEGY_MAIL || - (data && (data->set.mime_options & CURLMIMEOPT_FORMESCAPE))) + if(strategy == MIMESTRATEGY_MAIL || (data && (data->set.mime_formescape))) table = mimetable; Curl_dyn_init(&db, CURL_MAX_INPUT_LENGTH); diff --git a/Utilities/cmcurl/lib/mprintf.c b/Utilities/cmcurl/lib/mprintf.c index af5d753..6b5df5b 100644 --- a/Utilities/cmcurl/lib/mprintf.c +++ b/Utilities/cmcurl/lib/mprintf.c @@ -66,9 +66,7 @@ * Non-ANSI integer extensions */ -#if (defined(__BORLANDC__) && (__BORLANDC__ >= 0x520)) || \ - (defined(__POCC__) && defined(_MSC_VER)) || \ - (defined(_WIN32_WCE)) || \ +#if (defined(_WIN32_WCE)) || \ (defined(__MINGW32__)) || \ (defined(_MSC_VER) && (_MSC_VER >= 900) && (_INTEGRAL_MAX_BITS >= 64)) # define MP_HAVE_INT_EXTENSIONS @@ -1071,9 +1069,6 @@ static int alloc_addbyter(int output, FILE *data) return outc; /* fputc() returns like this on success */ } -extern int Curl_dyn_vprintf(struct dynbuf *dyn, - const char *format, va_list ap_save); - /* appends the formatted string, returns 0 on success, 1 on error */ int Curl_dyn_vprintf(struct dynbuf *dyn, const char *format, va_list ap_save) { diff --git a/Utilities/cmcurl/lib/mqtt.c b/Utilities/cmcurl/lib/mqtt.c index 54f8882..366235c 100644 --- a/Utilities/cmcurl/lib/mqtt.c +++ b/Utilities/cmcurl/lib/mqtt.c @@ -616,9 +616,6 @@ static void mqstate(struct Curl_easy *data, } -/* for the publish packet */ -#define MQTT_HEADER_LEN 5 /* max 5 bytes */ - static CURLcode mqtt_read_publish(struct Curl_easy *data, bool *done) { CURLcode result = CURLE_OK; @@ -677,7 +674,6 @@ MQTT_SUBACK_COMING: /* FALLTHROUGH */ case MQTT_PUB_REMAIN: { /* read rest of packet, but no more. Cap to buffer size */ - struct SingleRequest *k = &data->req; size_t rest = mq->npacket; if(rest > (size_t)data->set.buffer_size) rest = (size_t)data->set.buffer_size; @@ -693,13 +689,8 @@ MQTT_SUBACK_COMING: result = CURLE_PARTIAL_FILE; goto end; } - Curl_debug(data, CURLINFO_DATA_IN, (char *)pkt, (size_t)nread); mq->npacket -= nread; - k->bytecount += nread; - result = Curl_pgrsSetDownloadCounter(data, k->bytecount); - if(result) - goto end; /* if QoS is set, message contains packet id */ diff --git a/Utilities/cmcurl/lib/multi.c b/Utilities/cmcurl/lib/multi.c index ff753ac..5456113 100644 --- a/Utilities/cmcurl/lib/multi.c +++ b/Utilities/cmcurl/lib/multi.c @@ -55,22 +55,6 @@ #include "curl_memory.h" #include "memdebug.h" -#ifdef __APPLE__ - -#define wakeup_write write -#define wakeup_read read -#define wakeup_close close -#define wakeup_create pipe - -#else /* __APPLE__ */ - -#define wakeup_write swrite -#define wakeup_read sread -#define wakeup_close sclose -#define wakeup_create(p) Curl_socketpair(AF_UNIX, SOCK_STREAM, 0, p) - -#endif /* __APPLE__ */ - /* CURL_SOCKET_HASH_TABLE_SIZE should be a prime number. Increasing it from 97 to 911 takes on a 32-bit machine 4 x 804 = 3211 more bytes. Still, every @@ -231,10 +215,6 @@ struct Curl_sh_entry { unsigned int readers; /* this many transfers want to read */ unsigned int writers; /* this many transfers want to write */ }; -/* bits for 'action' having no bits means this socket is not expecting any - action */ -#define SH_READ 1 -#define SH_WRITE 2 /* look up a given socket in the socket hash, skip invalid sockets */ static struct Curl_sh_entry *sh_getentry(struct Curl_hash *sh, @@ -416,9 +396,6 @@ struct Curl_multi *Curl_multi_handle(int hashsize, /* socket hash */ Curl_llist_init(&multi->msgsent, NULL); multi->multiplexing = TRUE; - - /* -1 means it not set by user, use the default value */ - multi->maxconnects = -1; multi->max_concurrent_streams = 100; #ifdef USE_WINSOCK @@ -1040,49 +1017,61 @@ static int protocol_getsock(struct Curl_easy *data, { if(conn->handler->proto_getsock) return conn->handler->proto_getsock(data, conn, socks); - return Curl_conn_get_select_socks(data, FIRSTSOCKET, socks); + return GETSOCK_BLANK; } -/* returns bitmapped flags for this handle and its sockets. The 'socks[]' - array contains MAX_SOCKSPEREASYHANDLE entries. */ -static int multi_getsock(struct Curl_easy *data, - curl_socket_t *socks) +/* Initializes `poll_set` with the current socket poll actions needed + * for transfer `data`. */ +static void multi_getsock(struct Curl_easy *data, + struct easy_pollset *ps) { - struct connectdata *conn = data->conn; /* The no connection case can happen when this is called from curl_multi_remove_handle() => singlesocket() => multi_getsock(). */ - if(!conn) - return 0; + Curl_pollset_reset(data, ps); + if(!data->conn) + return; switch(data->mstate) { default: - return 0; + break; case MSTATE_RESOLVING: - return Curl_resolv_getsock(data, socks); + Curl_pollset_add_socks2(data, ps, Curl_resolv_getsock); + /* connection filters are not involved in this phase */ + return; case MSTATE_PROTOCONNECTING: case MSTATE_PROTOCONNECT: - return protocol_getsock(data, conn, socks); + Curl_pollset_add_socks(data, ps, protocol_getsock); + break; case MSTATE_DO: case MSTATE_DOING: - return doing_getsock(data, conn, socks); + Curl_pollset_add_socks(data, ps, doing_getsock); + break; case MSTATE_TUNNELING: case MSTATE_CONNECTING: - return Curl_conn_get_select_socks(data, FIRSTSOCKET, socks); + break; case MSTATE_DOING_MORE: - return domore_getsock(data, conn, socks); + Curl_pollset_add_socks(data, ps, domore_getsock); + break; case MSTATE_DID: /* since is set after DO is completed, we switch to waiting for the same as the PERFORMING state */ case MSTATE_PERFORMING: - return Curl_single_getsock(data, conn, socks); + Curl_pollset_add_socks(data, ps, Curl_single_getsock); + break; + + case MSTATE_RATELIMITING: + /* nothing to wait for */ + return; } + /* Let connection filters add/remove as needed */ + Curl_conn_adjust_pollset(data, ps); } CURLMcode curl_multi_fdset(struct Curl_multi *multi, @@ -1094,8 +1083,8 @@ CURLMcode curl_multi_fdset(struct Curl_multi *multi, and then we must make sure that is done. */ struct Curl_easy *data; int this_max_fd = -1; - curl_socket_t sockbunch[MAX_SOCKSPEREASYHANDLE]; - int i; + struct easy_pollset ps; + unsigned int i; (void)exc_fd_set; /* not used */ if(!GOOD_MULTI_HANDLE(multi)) @@ -1104,29 +1093,20 @@ CURLMcode curl_multi_fdset(struct Curl_multi *multi, if(multi->in_callback) return CURLM_RECURSIVE_API_CALL; + memset(&ps, 0, sizeof(ps)); for(data = multi->easyp; data; data = data->next) { - int bitmap; -#ifdef __clang_analyzer_ - /* to prevent "The left operand of '>=' is a garbage value" warnings */ - memset(sockbunch, 0, sizeof(sockbunch)); -#endif - bitmap = multi_getsock(data, sockbunch); - - for(i = 0; i< MAX_SOCKSPEREASYHANDLE; i++) { - if((bitmap & GETSOCK_MASK_RW(i)) && VALID_SOCK((sockbunch[i]))) { - if(!FDSET_SOCK(sockbunch[i])) - /* pretend it doesn't exist */ - continue; - if(bitmap & GETSOCK_READSOCK(i)) - FD_SET(sockbunch[i], read_fd_set); - if(bitmap & GETSOCK_WRITESOCK(i)) - FD_SET(sockbunch[i], write_fd_set); - if((int)sockbunch[i] > this_max_fd) - this_max_fd = (int)sockbunch[i]; - } - else { - break; - } + multi_getsock(data, &ps); + + for(i = 0; i < ps.num; i++) { + if(!FDSET_SOCK(ps.sockets[i])) + /* pretend it doesn't exist */ + continue; + if(ps.actions[i] & CURL_POLL_IN) + FD_SET(ps.sockets[i], read_fd_set); + if(ps.actions[i] & CURL_POLL_OUT) + FD_SET(ps.sockets[i], write_fd_set); + if((int)ps.sockets[i] > this_max_fd) + this_max_fd = (int)ps.sockets[i]; } } @@ -1162,9 +1142,8 @@ static CURLMcode multi_wait(struct Curl_multi *multi, bool use_wakeup) { struct Curl_easy *data; - curl_socket_t sockbunch[MAX_SOCKSPEREASYHANDLE]; - int bitmap; - unsigned int i; + struct easy_pollset ps; + size_t i; unsigned int nfds = 0; unsigned int curlfds; long timeout_internal; @@ -1190,17 +1169,10 @@ static CURLMcode multi_wait(struct Curl_multi *multi, return CURLM_BAD_FUNCTION_ARGUMENT; /* Count up how many fds we have from the multi handle */ + memset(&ps, 0, sizeof(ps)); for(data = multi->easyp; data; data = data->next) { - bitmap = multi_getsock(data, sockbunch); - - for(i = 0; i < MAX_SOCKSPEREASYHANDLE; i++) { - if((bitmap & GETSOCK_MASK_RW(i)) && VALID_SOCK((sockbunch[i]))) { - ++nfds; - } - else { - break; - } - } + multi_getsock(data, &ps); + nfds += ps.num; } /* If the internally desired timeout is actually shorter than requested from @@ -1241,40 +1213,35 @@ static CURLMcode multi_wait(struct Curl_multi *multi, if(curlfds) { /* Add the curl handles to our pollfds first */ for(data = multi->easyp; data; data = data->next) { - bitmap = multi_getsock(data, sockbunch); + multi_getsock(data, &ps); - for(i = 0; i < MAX_SOCKSPEREASYHANDLE; i++) { - if((bitmap & GETSOCK_MASK_RW(i)) && VALID_SOCK((sockbunch[i]))) { - struct pollfd *ufd = &ufds[nfds++]; -#ifdef USE_WINSOCK - long mask = 0; -#endif - ufd->fd = sockbunch[i]; - ufd->events = 0; - if(bitmap & GETSOCK_READSOCK(i)) { + for(i = 0; i < ps.num; i++) { + struct pollfd *ufd = &ufds[nfds++]; #ifdef USE_WINSOCK - mask |= FD_READ|FD_ACCEPT|FD_CLOSE; + long mask = 0; #endif - ufd->events |= POLLIN; - } - if(bitmap & GETSOCK_WRITESOCK(i)) { + ufd->fd = ps.sockets[i]; + ufd->events = 0; + if(ps.actions[i] & CURL_POLL_IN) { #ifdef USE_WINSOCK - mask |= FD_WRITE|FD_CONNECT|FD_CLOSE; - reset_socket_fdwrite(sockbunch[i]); + mask |= FD_READ|FD_ACCEPT|FD_CLOSE; #endif - ufd->events |= POLLOUT; - } + ufd->events |= POLLIN; + } + if(ps.actions[i] & CURL_POLL_OUT) { #ifdef USE_WINSOCK - if(WSAEventSelect(sockbunch[i], multi->wsa_event, mask) != 0) { - if(ufds_malloc) - free(ufds); - return CURLM_INTERNAL_ERROR; - } + mask |= FD_WRITE|FD_CONNECT|FD_CLOSE; + reset_socket_fdwrite(ps.sockets[i]); #endif + ufd->events |= POLLOUT; } - else { - break; +#ifdef USE_WINSOCK + if(WSAEventSelect(ps.sockets[i], multi->wsa_event, mask) != 0) { + if(ufds_malloc) + free(ufds); + return CURLM_INTERNAL_ERROR; } +#endif } } } @@ -1386,21 +1353,16 @@ static CURLMcode multi_wait(struct Curl_multi *multi, if(curlfds) { for(data = multi->easyp; data; data = data->next) { - bitmap = multi_getsock(data, sockbunch); - - for(i = 0; i < MAX_SOCKSPEREASYHANDLE; i++) { - if(bitmap & (GETSOCK_READSOCK(i) | GETSOCK_WRITESOCK(i))) { - wsa_events.lNetworkEvents = 0; - if(WSAEnumNetworkEvents(sockbunch[i], NULL, &wsa_events) == 0) { - if(ret && !pollrc && wsa_events.lNetworkEvents) - retcode++; - } - WSAEventSelect(sockbunch[i], multi->wsa_event, 0); - } - else { - /* break on entry not checked for being readable or writable */ - break; + multi_getsock(data, &ps); + + for(i = 0; i < ps.num; i++) { + wsa_events.lNetworkEvents = 0; + if(WSAEnumNetworkEvents(ps.sockets[i], NULL, + &wsa_events) == 0) { + if(ret && !pollrc && wsa_events.lNetworkEvents) + retcode++; } + WSAEventSelect(ps.sockets[i], multi->wsa_event, 0); } } } @@ -2021,8 +1983,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, if(dns) { #ifdef CURLRES_ASYNCH - data->state.async.dns = dns; - data->state.async.done = TRUE; + conn->resolve_async.dns = dns; + conn->resolve_async.done = TRUE; #endif result = CURLE_OK; infof(data, "Hostname '%s' was found in DNS cache", hostname); @@ -2895,53 +2857,36 @@ CURLMsg *curl_multi_info_read(struct Curl_multi *multi, int *msgs_in_queue) static CURLMcode singlesocket(struct Curl_multi *multi, struct Curl_easy *data) { - curl_socket_t socks[MAX_SOCKSPEREASYHANDLE]; - int i; + struct easy_pollset cur_poll; + unsigned int i; struct Curl_sh_entry *entry; curl_socket_t s; - int num; - unsigned int curraction; - unsigned char actions[MAX_SOCKSPEREASYHANDLE]; int rc; - for(i = 0; i< MAX_SOCKSPEREASYHANDLE; i++) - socks[i] = CURL_SOCKET_BAD; - /* Fill in the 'current' struct with the state as it is now: what sockets to supervise and for what actions */ - curraction = multi_getsock(data, socks); + multi_getsock(data, &cur_poll); /* We have 0 .. N sockets already and we get to know about the 0 .. M sockets we should have from now on. Detect the differences, remove no longer supervised ones and add new ones */ /* walk over the sockets we got right now */ - for(i = 0; (i< MAX_SOCKSPEREASYHANDLE) && - (curraction & GETSOCK_MASK_RW(i)); - i++) { - unsigned char action = CURL_POLL_NONE; - unsigned char prevaction = 0; + for(i = 0; i < cur_poll.num; i++) { + unsigned char cur_action = cur_poll.actions[i]; + unsigned char last_action = 0; int comboaction; - bool sincebefore = FALSE; - s = socks[i]; + s = cur_poll.sockets[i]; /* get it from the hash */ entry = sh_getentry(&multi->sockhash, s); - - if(curraction & GETSOCK_READSOCK(i)) - action |= CURL_POLL_IN; - if(curraction & GETSOCK_WRITESOCK(i)) - action |= CURL_POLL_OUT; - - actions[i] = action; if(entry) { /* check if new for this transfer */ - int j; - for(j = 0; j< data->numsocks; j++) { - if(s == data->sockets[j]) { - prevaction = data->actions[j]; - sincebefore = TRUE; + unsigned int j; + for(j = 0; j< data->last_poll.num; j++) { + if(s == data->last_poll.sockets[j]) { + last_action = data->last_poll.actions[j]; break; } } @@ -2953,23 +2898,23 @@ static CURLMcode singlesocket(struct Curl_multi *multi, /* fatal */ return CURLM_OUT_OF_MEMORY; } - if(sincebefore && (prevaction != action)) { + if(last_action && (last_action != cur_action)) { /* Socket was used already, but different action now */ - if(prevaction & CURL_POLL_IN) + if(last_action & CURL_POLL_IN) entry->readers--; - if(prevaction & CURL_POLL_OUT) + if(last_action & CURL_POLL_OUT) entry->writers--; - if(action & CURL_POLL_IN) + if(cur_action & CURL_POLL_IN) entry->readers++; - if(action & CURL_POLL_OUT) + if(cur_action & CURL_POLL_OUT) entry->writers++; } - else if(!sincebefore) { - /* a new user */ + else if(!last_action) { + /* a new transfer using this socket */ entry->users++; - if(action & CURL_POLL_IN) + if(cur_action & CURL_POLL_IN) entry->readers++; - if(action & CURL_POLL_OUT) + if(cur_action & CURL_POLL_OUT) entry->writers++; /* add 'data' to the transfer hash on this socket! */ @@ -2980,11 +2925,11 @@ static CURLMcode singlesocket(struct Curl_multi *multi, } } - comboaction = (entry->writers? CURL_POLL_OUT : 0) | + comboaction = (entry->writers ? CURL_POLL_OUT : 0) | (entry->readers ? CURL_POLL_IN : 0); /* socket existed before and has the same action set as before */ - if(sincebefore && ((int)entry->action == comboaction)) + if(last_action && ((int)entry->action == comboaction)) /* same, continue */ continue; @@ -2992,6 +2937,7 @@ static CURLMcode singlesocket(struct Curl_multi *multi, set_in_callback(multi, TRUE); rc = multi->socket_cb(data, s, comboaction, multi->socket_userp, entry->socketp); + set_in_callback(multi, FALSE); if(rc == -1) { multi->dead = TRUE; @@ -3002,16 +2948,15 @@ static CURLMcode singlesocket(struct Curl_multi *multi, entry->action = comboaction; /* store the current action state */ } - num = i; /* number of sockets */ - - /* when we've walked over all the sockets we should have right now, we must - make sure to detect sockets that are removed */ - for(i = 0; i< data->numsocks; i++) { - int j; + /* Check for last_poll.sockets that no longer appear in cur_poll.sockets. + * Need to remove the easy handle from the multi->sockhash->transfers and + * remove multi->sockhash entry when this was the last transfer */ + for(i = 0; i< data->last_poll.num; i++) { + unsigned int j; bool stillused = FALSE; - s = data->sockets[i]; - for(j = 0; j < num; j++) { - if(s == socks[j]) { + s = data->last_poll.sockets[i]; + for(j = 0; j < cur_poll.num; j++) { + if(s == cur_poll.sockets[j]) { /* this is still supervised */ stillused = TRUE; break; @@ -3024,7 +2969,7 @@ static CURLMcode singlesocket(struct Curl_multi *multi, /* if this is NULL here, the socket has been closed and notified so already by Curl_multi_closed() */ if(entry) { - unsigned char oldactions = data->actions[i]; + unsigned char oldactions = data->last_poll.actions[i]; /* this socket has been removed. Decrease user count */ entry->users--; if(oldactions & CURL_POLL_OUT) @@ -3052,11 +2997,10 @@ static CURLMcode singlesocket(struct Curl_multi *multi, } } } - } /* for loop over numsocks */ + } /* for loop over num */ - memcpy(data->sockets, socks, num*sizeof(curl_socket_t)); - memcpy(data->actions, actions, num*sizeof(char)); - data->numsocks = num; + /* Remember for next time */ + memcpy(&data->last_poll, &cur_poll, sizeof(data->last_poll)); return CURLM_OK; } @@ -3296,6 +3240,7 @@ CURLMcode curl_multi_setopt(struct Curl_multi *multi, { CURLMcode res = CURLM_OK; va_list param; + unsigned long uarg; if(!GOOD_MULTI_HANDLE(multi)) return CURLM_BAD_HANDLE; @@ -3328,7 +3273,9 @@ CURLMcode curl_multi_setopt(struct Curl_multi *multi, multi->timer_userp = va_arg(param, void *); break; case CURLMOPT_MAXCONNECTS: - multi->maxconnects = va_arg(param, long); + uarg = va_arg(param, unsigned long); + if(uarg <= UINT_MAX) + multi->maxconnects = (unsigned int)uarg; break; case CURLMOPT_MAX_HOST_CONNECTIONS: multi->max_host_connections = va_arg(param, long); @@ -3350,9 +3297,9 @@ CURLMcode curl_multi_setopt(struct Curl_multi *multi, case CURLMOPT_MAX_CONCURRENT_STREAMS: { long streams = va_arg(param, long); - if(streams < 1) + if((streams < 1) || (streams > INT_MAX)) streams = 100; - multi->max_concurrent_streams = curlx_sltoui(streams); + multi->max_concurrent_streams = (unsigned int)streams; } break; default: @@ -3782,11 +3729,11 @@ struct Curl_easy **curl_multi_get_handles(struct Curl_multi *multi) struct Curl_easy **a = malloc(sizeof(struct Curl_easy *) * (multi->num_easy + 1)); if(a) { - int i = 0; + unsigned int i = 0; struct Curl_easy *e = multi->easyp; while(e) { DEBUGASSERT(i < multi->num_easy); - if(!e->internal) + if(!e->state.internal) a[i++] = e; e = e->next; } diff --git a/Utilities/cmcurl/lib/multihandle.h b/Utilities/cmcurl/lib/multihandle.h index 5b16bb6..e03e382 100644 --- a/Utilities/cmcurl/lib/multihandle.h +++ b/Utilities/cmcurl/lib/multihandle.h @@ -93,9 +93,9 @@ struct Curl_multi { struct Curl_easy *easyp; struct Curl_easy *easylp; /* last node */ - int num_easy; /* amount of entries in the linked list above. */ - int num_alive; /* amount of easy handles that are added but have not yet - reached COMPLETE state */ + unsigned int num_easy; /* amount of entries in the linked list above. */ + unsigned int num_alive; /* amount of easy handles that are added but have + not yet reached COMPLETE state */ struct Curl_llist msglist; /* a list of messages from completed transfers */ @@ -136,9 +136,6 @@ struct Curl_multi { /* Shared connection cache (bundles)*/ struct conncache conn_cache; - long maxconnects; /* if >0, a fixed limit of the maximum number of entries - we're allowed to grow the connection cache to */ - long max_host_connections; /* if >0, a fixed limit of the maximum number of connections per host */ @@ -150,8 +147,6 @@ struct Curl_multi { void *timer_userp; struct curltime timer_lastcall; /* the fixed time for the timeout for the previous callback */ - unsigned int max_concurrent_streams; - #ifdef USE_WINSOCK WSAEVENT wsa_event; /* winsock event used for waits */ #else @@ -160,6 +155,10 @@ struct Curl_multi { 0 is used for read, 1 is used for write */ #endif #endif + unsigned int max_concurrent_streams; + unsigned int maxconnects; /* if >0, a fixed limit of the maximum number of + entries we're allowed to grow the connection + cache to */ #define IPV6_UNKNOWN 0 #define IPV6_DEAD 1 #define IPV6_WORKS 2 diff --git a/Utilities/cmcurl/lib/netrc.c b/Utilities/cmcurl/lib/netrc.c index e6a09b1..038c6dc 100644 --- a/Utilities/cmcurl/lib/netrc.c +++ b/Utilities/cmcurl/lib/netrc.c @@ -327,7 +327,7 @@ int Curl_parsenetrc(const char *host, char **loginp, char **passwordp, } retcode = parsenetrc(host, loginp, passwordp, filealloc); free(filealloc); -#ifdef WIN32 +#ifdef _WIN32 if(retcode == NETRC_FILE_MISSING) { /* fallback to the old-style "_netrc" file */ filealloc = curl_maprintf("%s%s_netrc", home, DIR_CHAR); diff --git a/Utilities/cmcurl/lib/openldap.c b/Utilities/cmcurl/lib/openldap.c index 3aff306..131f474 100644 --- a/Utilities/cmcurl/lib/openldap.c +++ b/Utilities/cmcurl/lib/openldap.c @@ -319,31 +319,12 @@ static CURLcode oldap_setup_connection(struct Curl_easy *data, { CURLcode result; LDAPURLDesc *lud; - struct ldapconninfo *li; + (void)conn; /* Early URL syntax check. */ result = oldap_url_parse(data, &lud); ldap_free_urldesc(lud); - if(!result) { - li = calloc(1, sizeof(struct ldapconninfo)); - if(!li) - result = CURLE_OUT_OF_MEMORY; - else { - li->proto = ldap_pvt_url_scheme2proto(data->state.up.scheme); - conn->proto.ldapc = li; - connkeep(conn, "OpenLDAP default"); - - /* Initialize the SASL storage */ - Curl_sasl_init(&li->sasl, data, &saslldap); - - /* Clear the TLS upgraded flag */ - conn->bits.tls_upgraded = FALSE; - - result = oldap_parse_login_options(conn); - } - } - return result; } @@ -537,7 +518,7 @@ static CURLcode oldap_perform_starttls(struct Curl_easy *data) static CURLcode oldap_connect(struct Curl_easy *data, bool *done) { struct connectdata *conn = data->conn; - struct ldapconninfo *li = conn->proto.ldapc; + struct ldapconninfo *li; static const int version = LDAP_VERSION3; int rc; char *hosturl; @@ -547,6 +528,26 @@ static CURLcode oldap_connect(struct Curl_easy *data, bool *done) (void)done; + DEBUGASSERT(!conn->proto.ldapc); + li = calloc(1, sizeof(struct ldapconninfo)); + if(!li) + return CURLE_OUT_OF_MEMORY; + else { + CURLcode result; + li->proto = ldap_pvt_url_scheme2proto(data->state.up.scheme); + conn->proto.ldapc = li; + + /* Initialize the SASL storage */ + Curl_sasl_init(&li->sasl, data, &saslldap); + + /* Clear the TLS upgraded flag */ + conn->bits.tls_upgraded = FALSE; + + result = oldap_parse_login_options(conn); + if(result) + return result; + } + hosturl = aprintf("ldap%s://%s:%d", conn->handler->flags & PROTOPT_SSL? "s": "", conn->host.name, conn->remote_port); @@ -886,6 +887,11 @@ static CURLcode oldap_do(struct Curl_easy *data, bool *done) result = oldap_url_parse(data, &lud); if(!result) { + Sockbuf *sb; + /* re-install the libcurl SSL handlers into the sockbuf. */ + ldap_get_option(li->ld, LDAP_OPT_SOCKBUF, &sb); + ber_sockbuf_add_io(sb, &ldapsb_tls, LBER_SBIOD_LEVEL_TRANSPORT, data); + rc = ldap_search_ext(li->ld, lud->lud_dn, lud->lud_scope, lud->lud_filter, lud->lud_attrs, 0, NULL, NULL, NULL, 0, &msgid); @@ -947,18 +953,12 @@ static CURLcode client_write(struct Curl_easy *data, if(!len && plen && prefix[plen - 1] == ' ') plen--; result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) prefix, plen); - if(!result) - data->req.bytecount += plen; } if(!result && value) { result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) value, len); - if(!result) - data->req.bytecount += len; } if(!result && suffix) { result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) suffix, slen); - if(!result) - data->req.bytecount += slen; } return result; } diff --git a/Utilities/cmcurl/lib/pop3.c b/Utilities/cmcurl/lib/pop3.c index a9d5fdd6..3e0f20a 100644 --- a/Utilities/cmcurl/lib/pop3.c +++ b/Utilities/cmcurl/lib/pop3.c @@ -1088,7 +1088,7 @@ static CURLcode pop3_init(struct Curl_easy *data) CURLcode result = CURLE_OK; struct POP3 *pop3; - pop3 = data->req.p.pop3 = calloc(sizeof(struct POP3), 1); + pop3 = data->req.p.pop3 = calloc(1, sizeof(struct POP3)); if(!pop3) result = CURLE_OUT_OF_MEMORY; diff --git a/Utilities/cmcurl/lib/progress.c b/Utilities/cmcurl/lib/progress.c index e783a9c..e96cbf7 100644 --- a/Utilities/cmcurl/lib/progress.c +++ b/Utilities/cmcurl/lib/progress.c @@ -304,7 +304,7 @@ timediff_t Curl_pgrsLimitWaitTime(curl_off_t cursize, * 'actual' is the time in milliseconds it took to actually download the * last 'size' bytes. */ - actual = Curl_timediff(now, start); + actual = Curl_timediff_ceil(now, start); if(actual < minimum) { /* if it downloaded the data faster than the limit, make it wait the difference */ @@ -319,12 +319,6 @@ timediff_t Curl_pgrsLimitWaitTime(curl_off_t cursize, */ CURLcode Curl_pgrsSetDownloadCounter(struct Curl_easy *data, curl_off_t size) { - if(data->set.max_filesize && (size > data->set.max_filesize)) { - failf(data, "Exceeded the maximum allowed file size " - "(%" CURL_FORMAT_CURL_OFF_T ")", - data->set.max_filesize); - return CURLE_FILESIZE_EXCEEDED; - } data->progress.downloaded = size; return CURLE_OK; } diff --git a/Utilities/cmcurl/lib/rand.c b/Utilities/cmcurl/lib/rand.c index 6bd9613..3383c49 100644 --- a/Utilities/cmcurl/lib/rand.c +++ b/Utilities/cmcurl/lib/rand.c @@ -32,10 +32,6 @@ #ifdef HAVE_ARPA_INET_H #include <arpa/inet.h> #endif -#ifdef HAVE_ARC4RANDOM -/* Some platforms might have the prototype missing (ubuntu + libressl) */ -uint32_t arc4random(void); -#endif #include <curl/curl.h> #include "urldata.h" @@ -50,7 +46,7 @@ uint32_t arc4random(void); #include "curl_memory.h" #include "memdebug.h" -#ifdef WIN32 +#ifdef _WIN32 #if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x600 # define HAVE_WIN_BCRYPTGENRANDOM @@ -105,7 +101,6 @@ CURLcode Curl_win32_random(unsigned char *entropy, size_t length) static CURLcode randit(struct Curl_easy *data, unsigned int *rnd) { - unsigned int r; CURLcode result = CURLE_OK; static unsigned int randseed; static bool seeded = FALSE; @@ -138,7 +133,7 @@ static CURLcode randit(struct Curl_easy *data, unsigned int *rnd) /* ---- non-cryptographic version following ---- */ -#ifdef WIN32 +#ifdef _WIN32 if(!seeded) { result = Curl_win32_random((unsigned char *)rnd, sizeof(*rnd)); if(result != CURLE_NOT_BUILT_IN) @@ -146,12 +141,14 @@ static CURLcode randit(struct Curl_easy *data, unsigned int *rnd) } #endif -#ifdef HAVE_ARC4RANDOM - *rnd = (unsigned int)arc4random(); - return CURLE_OK; +#if defined(HAVE_ARC4RANDOM) && !defined(USE_OPENSSL) + if(!seeded) { + *rnd = (unsigned int)arc4random(); + return CURLE_OK; + } #endif -#if defined(RANDOM_FILE) && !defined(WIN32) +#if defined(RANDOM_FILE) && !defined(_WIN32) if(!seeded) { /* if there's a random file to read a seed from, use it */ int fd = open(RANDOM_FILE, O_RDONLY); @@ -175,9 +172,12 @@ static CURLcode randit(struct Curl_easy *data, unsigned int *rnd) seeded = TRUE; } - /* Return an unsigned 32-bit pseudo-random number. */ - r = randseed = randseed * 1103515245 + 12345; - *rnd = (r << 16) | ((r >> 16) & 0xFFFF); + { + unsigned int r; + /* Return an unsigned 32-bit pseudo-random number. */ + r = randseed = randseed * 1103515245 + 12345; + *rnd = (r << 16) | ((r >> 16) & 0xFFFF); + } return CURLE_OK; } diff --git a/Utilities/cmcurl/lib/rand.h b/Utilities/cmcurl/lib/rand.h index 1d009f5..bc05239 100644 --- a/Utilities/cmcurl/lib/rand.h +++ b/Utilities/cmcurl/lib/rand.h @@ -41,7 +41,7 @@ CURLcode Curl_rand_hex(struct Curl_easy *data, unsigned char *rnd, CURLcode Curl_rand_alnum(struct Curl_easy *data, unsigned char *rnd, size_t num); -#ifdef WIN32 +#ifdef _WIN32 /* Random generator shared between the Schannel vtls and Curl_rand*() functions */ CURLcode Curl_win32_random(unsigned char *entropy, size_t length); diff --git a/Utilities/cmcurl/lib/rename.c b/Utilities/cmcurl/lib/rename.c index 97a66e9..4c88698 100644 --- a/Utilities/cmcurl/lib/rename.c +++ b/Utilities/cmcurl/lib/rename.c @@ -40,7 +40,7 @@ /* return 0 on success, 1 on error */ int Curl_rename(const char *oldpath, const char *newpath) { -#ifdef WIN32 +#ifdef _WIN32 /* rename() on Windows doesn't overwrite, so we can't use it here. MoveFileEx() will overwrite and is usually atomic, however it fails when there are open handles to the file. */ diff --git a/Utilities/cmcurl/lib/rtsp.c b/Utilities/cmcurl/lib/rtsp.c index ccd7264..e673bb8 100644 --- a/Utilities/cmcurl/lib/rtsp.c +++ b/Utilities/cmcurl/lib/rtsp.c @@ -45,8 +45,8 @@ #include "curl_memory.h" #include "memdebug.h" -#define RTP_PKT_LENGTH(p) ((((int)((unsigned char)((p)[2]))) << 8) | \ - ((int)((unsigned char)((p)[3])))) +#define RTP_PKT_LENGTH(p) ((((unsigned int)((unsigned char)((p)[2]))) << 8) | \ + ((unsigned int)((unsigned char)((p)[3])))) /* protocol-specific functions set up to be called by the main engine */ static CURLcode rtsp_do(struct Curl_easy *data, bool *done); @@ -59,14 +59,19 @@ static int rtsp_getsock_do(struct Curl_easy *data, /* * Parse and write out any available RTP data. - * - * nread: amount of data left after k->str. will be modified if RTP - * data is parsed and k->str is moved up - * readmore: whether or not the RTP parser needs more data right away + * @param data the transfer + * @param conn the connection + * @param buf data read from connection + * @param blen amount of data in buf + * @param consumed out, number of blen consumed + * @param readmore out, TRUE iff complete buf was consumed and more data + * is needed */ static CURLcode rtsp_rtp_readwrite(struct Curl_easy *data, struct connectdata *conn, - ssize_t *nread, + const char *buf, + size_t blen, + size_t *pconsumed, bool *readmore); static CURLcode rtsp_setup_connection(struct Curl_easy *data, @@ -88,7 +93,7 @@ static int rtsp_getsock_do(struct Curl_easy *data, struct connectdata *conn, } static -CURLcode rtp_client_write(struct Curl_easy *data, char *ptr, size_t len); +CURLcode rtp_client_write(struct Curl_easy *data, const char *ptr, size_t len); static CURLcode rtsp_parse_transport(struct Curl_easy *data, char *transport); @@ -585,153 +590,249 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) return result; } - -static CURLcode rtsp_rtp_readwrite(struct Curl_easy *data, - struct connectdata *conn, - ssize_t *nread, - bool *readmore) { - struct SingleRequest *k = &data->req; +static CURLcode rtsp_filter_rtp(struct Curl_easy *data, + struct connectdata *conn, + const char *buf, + size_t blen, + bool in_body, + size_t *pconsumed) +{ struct rtsp_conn *rtspc = &(conn->proto.rtspc); - unsigned char *rtp_channel_mask = data->state.rtp_channel_mask; + CURLcode result = CURLE_OK; - char *rtp; /* moving pointer to rtp data */ - ssize_t rtp_dataleft; /* how much data left to parse in this round */ - CURLcode result; - bool interleaved = false; - size_t skip_size = 0; + *pconsumed = 0; + while(blen) { + switch(rtspc->state) { - if(Curl_dyn_len(&rtspc->buf)) { - /* There was some leftover data the last time. Append new buffers */ - if(Curl_dyn_addn(&rtspc->buf, k->str, *nread)) - return CURLE_OUT_OF_MEMORY; - rtp = Curl_dyn_ptr(&rtspc->buf); - rtp_dataleft = Curl_dyn_len(&rtspc->buf); - } - else { - /* Just parse the request buffer directly */ - rtp = k->str; - rtp_dataleft = *nread; - } - - while(rtp_dataleft > 0) { - if(rtp[0] == '$') { - if(rtp_dataleft > 4) { - unsigned char rtp_channel; - int rtp_length; - int idx; - int off; - - /* Parse the header */ - /* The channel identifier immediately follows and is 1 byte */ - rtp_channel = (unsigned char)rtp[1]; - idx = rtp_channel / 8; - off = rtp_channel % 8; - if(!(rtp_channel_mask[idx] & (1 << off))) { - /* invalid channel number, maybe not an RTP packet */ - rtp++; - rtp_dataleft--; - skip_size++; - continue; + case RTP_PARSE_SKIP: { + DEBUGASSERT(Curl_dyn_len(&rtspc->buf) == 0); + if(in_body && buf[0] != '$') { + /* in BODY and no valid start, do not consume and return */ + goto out; + } + while(blen && buf[0] != '$') { + if(!in_body && buf[0] == 'R' && + data->set.rtspreq != RTSPREQ_RECEIVE) { + if(strncmp(buf, "RTSP/", (blen < 5) ? blen : 5) == 0) { + /* This could be the next response, no consume and return */ + if(*pconsumed) { + DEBUGF(infof(data, "RTP rtsp_filter_rtp[SKIP] RTSP/ prefix, " + "skipping %zd bytes of junk", *pconsumed)); + } + rtspc->state = RTP_PARSE_SKIP; + rtspc->in_header = TRUE; + goto out; + } } - if(skip_size > 0) { - DEBUGF(infof(data, "Skip the malformed interleaved data %lu " - "bytes", skip_size)); + /* junk, consume without buffering */ + *pconsumed += 1; + ++buf; + --blen; + } + if(blen && buf[0] == '$') { + /* possible start of an RTP message, buffer */ + if(Curl_dyn_addn(&rtspc->buf, buf, 1)) { + result = CURLE_OUT_OF_MEMORY; + goto out; } - skip_size = 0; - rtspc->rtp_channel = rtp_channel; - - /* The length is two bytes */ - rtp_length = RTP_PKT_LENGTH(rtp); + *pconsumed += 1; + ++buf; + --blen; + rtspc->state = RTP_PARSE_CHANNEL; + } + break; + } - if(rtp_dataleft < rtp_length + 4) { - /* Need more - incomplete payload */ - *readmore = TRUE; - break; + case RTP_PARSE_CHANNEL: { + int idx = ((unsigned char)buf[0]) / 8; + int off = ((unsigned char)buf[0]) % 8; + DEBUGASSERT(Curl_dyn_len(&rtspc->buf) == 1); + if(!(data->state.rtp_channel_mask[idx] & (1 << off))) { + /* invalid channel number, junk or BODY data */ + rtspc->state = RTP_PARSE_SKIP; + if(in_body) { + /* we do not consume this byte, it is BODY data */ + DEBUGF(infof(data, "RTSP: invalid RTP channel %d in BODY, " + "treating as BODY data", idx)); + if(*pconsumed == 0) { + /* We did not consume the initial '$' in our buffer, but had + * it from an earlier call. We cannot un-consume it and have + * to write it directly as BODY data */ + result = Curl_client_write(data, CLIENTWRITE_BODY, + Curl_dyn_ptr(&rtspc->buf), 1); + Curl_dyn_free(&rtspc->buf); + if(result) + goto out; + } + else { + /* un-consume the '$' and leave */ + Curl_dyn_free(&rtspc->buf); + *pconsumed -= 1; + --buf; + ++blen; + goto out; + } } - interleaved = true; - /* We have the full RTP interleaved packet - * Write out the header including the leading '$' */ - DEBUGF(infof(data, "RTP write channel %d rtp_length %d", - rtspc->rtp_channel, rtp_length)); - result = rtp_client_write(data, &rtp[0], rtp_length + 4); - if(result) { - *readmore = FALSE; - return result; + else { + /* not BODY, forget the junk '$'. Do not consume this byte, + * it might be a start */ + infof(data, "RTSP: invalid RTP channel %d, skipping", idx); + Curl_dyn_free(&rtspc->buf); } + break; + } + /* a valid channel, so we expect this to be a real RTP message */ + rtspc->rtp_channel = (unsigned char)buf[0]; + if(Curl_dyn_addn(&rtspc->buf, buf, 1)) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + *pconsumed += 1; + ++buf; + --blen; + rtspc->state = RTP_PARSE_LEN; + break; + } - /* Move forward in the buffer */ - rtp_dataleft -= rtp_length + 4; - rtp += rtp_length + 4; + case RTP_PARSE_LEN: { + size_t rtp_len = Curl_dyn_len(&rtspc->buf); + const char *rtp_buf; + DEBUGASSERT(rtp_len >= 2 && rtp_len < 4); + if(Curl_dyn_addn(&rtspc->buf, buf, 1)) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + *pconsumed += 1; + ++buf; + --blen; + if(rtp_len == 2) + break; + rtp_buf = Curl_dyn_ptr(&rtspc->buf); + rtspc->rtp_len = RTP_PKT_LENGTH(rtp_buf) + 4; + rtspc->state = RTP_PARSE_DATA; + break; + } - if(data->set.rtspreq == RTSPREQ_RECEIVE) { - /* If we are in a passive receive, give control back - * to the app as often as we can. - */ - k->keepon &= ~KEEP_RECV; + case RTP_PARSE_DATA: { + size_t rtp_len = Curl_dyn_len(&rtspc->buf); + size_t needed; + DEBUGASSERT(rtp_len < rtspc->rtp_len); + needed = rtspc->rtp_len - rtp_len; + if(needed <= blen) { + if(Curl_dyn_addn(&rtspc->buf, buf, needed)) { + result = CURLE_OUT_OF_MEMORY; + goto out; } + *pconsumed += needed; + buf += needed; + blen -= needed; + /* complete RTP message in buffer */ + DEBUGF(infof(data, "RTP write channel %d rtp_len %zu", + rtspc->rtp_channel, rtspc->rtp_len)); + result = rtp_client_write(data, Curl_dyn_ptr(&rtspc->buf), + rtspc->rtp_len); + Curl_dyn_free(&rtspc->buf); + rtspc->state = RTP_PARSE_SKIP; + if(result) + goto out; } else { - /* Need more - incomplete header */ - *readmore = TRUE; - break; - } - } - else { - /* If the following data begins with 'RTSP/', which might be an RTSP - message, we should stop skipping the data. */ - /* If `k-> headerline> 0 && !interleaved` is true, we are maybe in the - middle of an RTSP message. It is difficult to determine this, so we - stop skipping. */ - size_t prefix_len = (rtp_dataleft < 5) ? rtp_dataleft : 5; - if((k->headerline > 0 && !interleaved) || - strncmp(rtp, "RTSP/", prefix_len) == 0) { - if(skip_size > 0) { - DEBUGF(infof(data, "Skip the malformed interleaved data %lu " - "bytes", skip_size)); + if(Curl_dyn_addn(&rtspc->buf, buf, blen)) { + result = CURLE_OUT_OF_MEMORY; + goto out; } - break; /* maybe is an RTSP message */ + *pconsumed += blen; + buf += blen; + blen = 0; } - /* Skip incorrect data util the next RTP packet or RTSP message */ - do { - rtp++; - rtp_dataleft--; - skip_size++; - } while(rtp_dataleft > 0 && rtp[0] != '$' && rtp[0] != 'R'); + break; + } + + default: + DEBUGASSERT(0); + return CURLE_RECV_ERROR; } } +out: + return result; +} + +static CURLcode rtsp_rtp_readwrite(struct Curl_easy *data, + struct connectdata *conn, + const char *buf, + size_t blen, + size_t *pconsumed, + bool *readmore) +{ + struct rtsp_conn *rtspc = &(conn->proto.rtspc); + CURLcode result = CURLE_OK; + size_t consumed = 0; + bool in_body; - if(rtp_dataleft && rtp[0] == '$') { - DEBUGF(infof(data, "RTP Rewinding %zd %s", rtp_dataleft, - *readmore ? "(READMORE)" : "")); + if(!data->req.header) + rtspc->in_header = FALSE; + in_body = (data->req.headerline && !rtspc->in_header) && + (data->req.size >= 0) && + (data->req.bytecount < data->req.size); - /* Store the incomplete RTP packet for a "rewind" */ - if(!Curl_dyn_len(&rtspc->buf)) { - /* nothing was stored, add this data */ - if(Curl_dyn_addn(&rtspc->buf, rtp, rtp_dataleft)) - return CURLE_OUT_OF_MEMORY; - } - else { - /* keep the remainder */ - Curl_dyn_tail(&rtspc->buf, rtp_dataleft); - } + *readmore = FALSE; + *pconsumed = 0; + if(!blen) { + goto out; + } - /* As far as the transfer is concerned, this data is consumed */ - *nread = 0; - return CURLE_OK; + /* If header parsing is not onging, extract RTP messages */ + if(!rtspc->in_header) { + result = rtsp_filter_rtp(data, conn, buf, blen, in_body, &consumed); + if(result) + goto out; + *pconsumed += consumed; + buf += consumed; + blen -= consumed; } - /* Fix up k->str to point just after the last RTP packet */ - k->str += *nread - rtp_dataleft; - *nread = rtp_dataleft; + /* we want to parse headers, do so */ + if(data->req.header && blen) { + rtspc->in_header = TRUE; + result = Curl_http_readwrite_headers(data, conn, buf, blen, + &consumed); + if(result) + goto out; + + *pconsumed += consumed; + buf += consumed; + blen -= consumed; + + if(!data->req.header) + rtspc->in_header = FALSE; + + if(!rtspc->in_header) { + /* If header parsing is done and data left, extract RTP messages */ + in_body = (data->req.headerline && !rtspc->in_header) && + (data->req.size >= 0) && + (data->req.bytecount < data->req.size); + result = rtsp_filter_rtp(data, conn, buf, blen, in_body, &consumed); + if(result) + goto out; + *pconsumed += consumed; + } + } - /* If we get here, we have finished with the leftover/merge buffer */ - Curl_dyn_free(&rtspc->buf); + if(rtspc->state != RTP_PARSE_SKIP) + *readmore = TRUE; - return CURLE_OK; +out: + if(!*readmore && data->set.rtspreq == RTSPREQ_RECEIVE) { + /* In special mode RECEIVE, we just process one chunk of network + * data, so we stop the transfer here, if we have no incomplete + * RTP message pending. */ + data->req.keepon &= ~KEEP_RECV; + } + return result; } static -CURLcode rtp_client_write(struct Curl_easy *data, char *ptr, size_t len) +CURLcode rtp_client_write(struct Curl_easy *data, const char *ptr, size_t len) { size_t wrote; curl_write_callback writeit; @@ -756,7 +857,7 @@ CURLcode rtp_client_write(struct Curl_easy *data, char *ptr, size_t len) } Curl_set_in_callback(data, true); - wrote = writeit(ptr, 1, len, user_ptr); + wrote = writeit((char *)ptr, 1, len, user_ptr); Curl_set_in_callback(data, false); if(CURL_WRITEFUNC_PAUSE == wrote) { diff --git a/Utilities/cmcurl/lib/rtsp.h b/Utilities/cmcurl/lib/rtsp.h index 111bac2..237b80f 100644 --- a/Utilities/cmcurl/lib/rtsp.h +++ b/Utilities/cmcurl/lib/rtsp.h @@ -39,6 +39,12 @@ CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, char *header); #endif /* CURL_DISABLE_RTSP */ +typedef enum { + RTP_PARSE_SKIP, + RTP_PARSE_CHANNEL, + RTP_PARSE_LEN, + RTP_PARSE_DATA +} rtp_parse_st; /* * RTSP Connection data * @@ -47,6 +53,9 @@ CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, char *header); struct rtsp_conn { struct dynbuf buf; int rtp_channel; + size_t rtp_len; + rtp_parse_st state; + BIT(in_header); }; /**************************************************************************** diff --git a/Utilities/cmcurl/lib/select.c b/Utilities/cmcurl/lib/select.c index cae9beb..d92e745 100644 --- a/Utilities/cmcurl/lib/select.c +++ b/Utilities/cmcurl/lib/select.c @@ -76,7 +76,7 @@ int Curl_wait_ms(timediff_t timeout_ms) } #if defined(MSDOS) delay(timeout_ms); -#elif defined(WIN32) +#elif defined(_WIN32) /* prevent overflow, timeout_ms is typecast to ULONG/DWORD. */ #if TIMEDIFF_T_MAX >= ULONG_MAX if(timeout_ms >= ULONG_MAX) diff --git a/Utilities/cmcurl/lib/sendf.c b/Utilities/cmcurl/lib/sendf.c index 0482c5d..a2fac0c 100644 --- a/Utilities/cmcurl/lib/sendf.c +++ b/Utilities/cmcurl/lib/sendf.c @@ -50,6 +50,7 @@ #include "strdup.h" #include "http2.h" #include "headers.h" +#include "progress.h" #include "ws.h" /* The last 3 #include files should be in this order */ @@ -57,6 +58,9 @@ #include "curl_memory.h" #include "memdebug.h" + +static CURLcode do_init_stack(struct Curl_easy *data); + #if defined(CURL_DO_LINEEND_CONV) && !defined(CURL_DISABLE_FTP) /* * convert_lineends() changes CRLF (\r\n) end-of-line markers to a single LF @@ -385,17 +389,17 @@ static CURLcode chop_write(struct Curl_easy *data, the future to leave the original data alone. */ CURLcode Curl_client_write(struct Curl_easy *data, - int type, - char *ptr, - size_t len) + int type, char *buf, size_t blen) { + CURLcode result; + #if !defined(CURL_DISABLE_FTP) && defined(CURL_DO_LINEEND_CONV) /* FTP data may need conversion. */ if((type & CLIENTWRITE_BODY) && (data->conn->handler->protocol & PROTO_FAMILY_FTP) && data->conn->proto.ftpc.transfertype == 'A') { /* convert end-of-line markers */ - len = convert_lineends(data, ptr, len); + blen = convert_lineends(data, buf, blen); } #endif /* it is one of those, at least */ @@ -405,14 +409,14 @@ CURLcode Curl_client_write(struct Curl_easy *data, /* INFO is only INFO */ DEBUGASSERT(!(type & CLIENTWRITE_INFO) || (type == CLIENTWRITE_INFO)); - if(type == CLIENTWRITE_BODY) { - if(data->req.ignorebody) - return CURLE_OK; - - if(data->req.writer_stack && !data->set.http_ce_skip) - return Curl_unencode_write(data, data->req.writer_stack, ptr, len); + if(!data->req.writer_stack) { + result = do_init_stack(data); + if(result) + return result; + DEBUGASSERT(data->req.writer_stack); } - return chop_write(data, type, FALSE, ptr, len); + + return Curl_cwriter_write(data, data->req.writer_stack, type, buf, blen); } CURLcode Curl_client_unpause(struct Curl_easy *data) @@ -449,12 +453,12 @@ CURLcode Curl_client_unpause(struct Curl_easy *data) void Curl_client_cleanup(struct Curl_easy *data) { - struct contenc_writer *writer = data->req.writer_stack; + struct Curl_cwriter *writer = data->req.writer_stack; size_t i; while(writer) { - data->req.writer_stack = writer->downstream; - writer->handler->close_writer(data, writer); + data->req.writer_stack = writer->next; + writer->cwt->do_close(data, writer); free(writer); writer = data->req.writer_stack; } @@ -463,61 +467,222 @@ void Curl_client_cleanup(struct Curl_easy *data) Curl_dyn_free(&data->state.tempwrite[i].b); } data->state.tempcount = 0; + data->req.bytecount = 0; + data->req.headerline = 0; +} +/* Write data using an unencoding writer stack. "nbytes" is not + allowed to be 0. */ +CURLcode Curl_cwriter_write(struct Curl_easy *data, + struct Curl_cwriter *writer, int type, + const char *buf, size_t nbytes) +{ + if(!nbytes) + return CURLE_OK; + if(!writer) + return CURLE_WRITE_ERROR; + return writer->cwt->do_write(data, writer, type, buf, nbytes); } -/* Real client writer: no downstream. */ -static CURLcode client_cew_init(struct Curl_easy *data, - struct contenc_writer *writer) +CURLcode Curl_cwriter_def_init(struct Curl_easy *data, + struct Curl_cwriter *writer) { - (void) data; + (void)data; (void)writer; return CURLE_OK; } -static CURLcode client_cew_write(struct Curl_easy *data, - struct contenc_writer *writer, - const char *buf, size_t nbytes) +CURLcode Curl_cwriter_def_write(struct Curl_easy *data, + struct Curl_cwriter *writer, int type, + const char *buf, size_t nbytes) { - (void)writer; - if(!nbytes || data->req.ignorebody) - return CURLE_OK; - return chop_write(data, CLIENTWRITE_BODY, FALSE, (char *)buf, nbytes); + return Curl_cwriter_write(data, writer->next, type, buf, nbytes); } -static void client_cew_close(struct Curl_easy *data, - struct contenc_writer *writer) +void Curl_cwriter_def_close(struct Curl_easy *data, + struct Curl_cwriter *writer) { (void) data; (void) writer; } -static const struct content_encoding client_cew = { +/* Real client writer to installed callbacks. */ +static CURLcode cw_client_write(struct Curl_easy *data, + struct Curl_cwriter *writer, int type, + const char *buf, size_t nbytes) +{ + (void)writer; + if(!nbytes) + return CURLE_OK; + return chop_write(data, type, FALSE, (char *)buf, nbytes); +} + +static const struct Curl_cwtype cw_client = { + "client", NULL, + Curl_cwriter_def_init, + cw_client_write, + Curl_cwriter_def_close, + sizeof(struct Curl_cwriter) +}; + +static size_t get_max_body_write_len(struct Curl_easy *data, curl_off_t limit) +{ + if(limit != -1) { + /* How much more are we allowed to write? */ + curl_off_t remain_diff; + remain_diff = limit - data->req.bytecount; + if(remain_diff < 0) { + /* already written too much! */ + return 0; + } +#if SIZEOF_CURL_OFF_T > SIZEOF_SIZE_T + else if(remain_diff > SSIZE_T_MAX) { + return SIZE_T_MAX; + } +#endif + else { + return (size_t)remain_diff; + } + } + return SIZE_T_MAX; +} + +/* Download client writer in phase CURL_CW_PROTOCOL that + * sees the "real" download body data. */ +static CURLcode cw_download_write(struct Curl_easy *data, + struct Curl_cwriter *writer, int type, + const char *buf, size_t nbytes) +{ + CURLcode result; + size_t nwrite, excess_len = 0; + const char *excess_data = NULL; + + if(!(type & CLIENTWRITE_BODY)) { + if((type & CLIENTWRITE_CONNECT) && data->set.suppress_connect_headers) + return CURLE_OK; + return Curl_cwriter_write(data, writer->next, type, buf, nbytes); + } + + nwrite = nbytes; + if(-1 != data->req.maxdownload) { + size_t wmax = get_max_body_write_len(data, data->req.maxdownload); + if(nwrite > wmax) { + excess_len = nbytes - wmax; + nwrite = wmax; + excess_data = buf + nwrite; + } + + if(nwrite == wmax) { + data->req.download_done = TRUE; + } + } + + if(data->set.max_filesize) { + size_t wmax = get_max_body_write_len(data, data->set.max_filesize); + if(nwrite > wmax) { + nwrite = wmax; + } + } + + data->req.bytecount += nwrite; + ++data->req.bodywrites; + if(!data->req.ignorebody && nwrite) { + result = Curl_cwriter_write(data, writer->next, type, buf, nwrite); + if(result) + return result; + } + result = Curl_pgrsSetDownloadCounter(data, data->req.bytecount); + if(result) + return result; + + if(excess_len) { + if(data->conn->handler->readwrite) { + /* RTSP hack moved from transfer loop to here */ + bool readmore = FALSE; /* indicates data is incomplete, need more */ + size_t consumed = 0; + result = data->conn->handler->readwrite(data, data->conn, + excess_data, excess_len, + &consumed, &readmore); + if(result) + return result; + DEBUGASSERT(consumed <= excess_len); + excess_len -= consumed; + if(readmore) { + data->req.download_done = FALSE; + data->req.keepon |= KEEP_RECV; /* we're not done reading */ + } + } + if(excess_len && !data->req.ignorebody) { + infof(data, + "Excess found writing body:" + " excess = %zu" + ", size = %" CURL_FORMAT_CURL_OFF_T + ", maxdownload = %" CURL_FORMAT_CURL_OFF_T + ", bytecount = %" CURL_FORMAT_CURL_OFF_T, + excess_len, data->req.size, data->req.maxdownload, + data->req.bytecount); + connclose(data->conn, "excess found in a read"); + } + } + else if(nwrite < nbytes) { + failf(data, "Exceeded the maximum allowed file size " + "(%" CURL_FORMAT_CURL_OFF_T ") with %" + CURL_FORMAT_CURL_OFF_T " bytes", + data->set.max_filesize, data->req.bytecount); + return CURLE_FILESIZE_EXCEEDED; + } + + return CURLE_OK; +} + +static const struct Curl_cwtype cw_download = { + "download", NULL, - client_cew_init, - client_cew_write, - client_cew_close, - sizeof(struct contenc_writer) + Curl_cwriter_def_init, + cw_download_write, + Curl_cwriter_def_close, + sizeof(struct Curl_cwriter) +}; + +/* RAW client writer in phase CURL_CW_RAW that + * enabled tracing of raw data. */ +static CURLcode cw_raw_write(struct Curl_easy *data, + struct Curl_cwriter *writer, int type, + const char *buf, size_t nbytes) +{ + if(type & CLIENTWRITE_BODY && data->set.verbose && !data->req.ignorebody) { + Curl_debug(data, CURLINFO_DATA_IN, (char *)buf, nbytes); + } + return Curl_cwriter_write(data, writer->next, type, buf, nbytes); +} + +static const struct Curl_cwtype cw_raw = { + "raw", + NULL, + Curl_cwriter_def_init, + cw_raw_write, + Curl_cwriter_def_close, + sizeof(struct Curl_cwriter) }; /* Create an unencoding writer stage using the given handler. */ -CURLcode Curl_client_create_writer(struct contenc_writer **pwriter, +CURLcode Curl_cwriter_create(struct Curl_cwriter **pwriter, struct Curl_easy *data, - const struct content_encoding *ce_handler, - int order) + const struct Curl_cwtype *cwt, + Curl_cwriter_phase phase) { - struct contenc_writer *writer; + struct Curl_cwriter *writer; CURLcode result = CURLE_OUT_OF_MEMORY; - DEBUGASSERT(ce_handler->writersize >= sizeof(struct contenc_writer)); - writer = (struct contenc_writer *) calloc(1, ce_handler->writersize); + DEBUGASSERT(cwt->cwriter_size >= sizeof(struct Curl_cwriter)); + writer = (struct Curl_cwriter *) calloc(1, cwt->cwriter_size); if(!writer) goto out; - writer->handler = ce_handler; - writer->order = order; - result = ce_handler->init_writer(data, writer); + writer->cwt = cwt; + writer->phase = phase; + result = cwt->do_init(data, writer); out: *pwriter = result? NULL : writer; @@ -526,55 +691,74 @@ out: return result; } -void Curl_client_free_writer(struct Curl_easy *data, - struct contenc_writer *writer) +void Curl_cwriter_free(struct Curl_easy *data, + struct Curl_cwriter *writer) { if(writer) { - writer->handler->close_writer(data, writer); + writer->cwt->do_close(data, writer); free(writer); } } -/* allow no more than 5 "chained" compression steps */ -#define MAX_ENCODE_STACK 5 +size_t Curl_cwriter_count(struct Curl_easy *data, Curl_cwriter_phase phase) +{ + struct Curl_cwriter *w; + size_t n = 0; + for(w = data->req.writer_stack; w; w = w->next) { + if(w->phase == phase) + ++n; + } + return n; +} -static CURLcode init_writer_stack(struct Curl_easy *data) +static CURLcode do_init_stack(struct Curl_easy *data) { + struct Curl_cwriter *writer; + CURLcode result; + DEBUGASSERT(!data->req.writer_stack); - return Curl_client_create_writer(&data->req.writer_stack, - data, &client_cew, 0); + result = Curl_cwriter_create(&data->req.writer_stack, + data, &cw_client, CURL_CW_CLIENT); + if(result) + return result; + + result = Curl_cwriter_create(&writer, data, &cw_download, CURL_CW_PROTOCOL); + if(result) + return result; + result = Curl_cwriter_add(data, writer); + if(result) { + Curl_cwriter_free(data, writer); + } + + result = Curl_cwriter_create(&writer, data, &cw_raw, CURL_CW_RAW); + if(result) + return result; + result = Curl_cwriter_add(data, writer); + if(result) { + Curl_cwriter_free(data, writer); + } + return result; } -CURLcode Curl_client_add_writer(struct Curl_easy *data, - struct contenc_writer *writer) +CURLcode Curl_cwriter_add(struct Curl_easy *data, + struct Curl_cwriter *writer) { CURLcode result; + struct Curl_cwriter **anchor = &data->req.writer_stack; - if(!data->req.writer_stack) { - result = init_writer_stack(data); + if(!*anchor) { + result = do_init_stack(data); if(result) return result; } - if(data->req.writer_stack_depth++ >= MAX_ENCODE_STACK) { - failf(data, "Reject response due to more than %u content encodings", - MAX_ENCODE_STACK); - return CURLE_BAD_CONTENT_ENCODING; - } - - /* Stack the unencoding stage. */ - if(writer->order >= data->req.writer_stack->order) { - writer->downstream = data->req.writer_stack; - data->req.writer_stack = writer; - } - else { - struct contenc_writer *w = data->req.writer_stack; - while(w->downstream && writer->order < w->downstream->order) - w = w->downstream; - writer->downstream = w->downstream; - w->downstream = writer; - } + /* Insert the writer as first in its phase. + * Skip existing writers of lower phases. */ + while(*anchor && (*anchor)->phase < writer->phase) + anchor = &((*anchor)->next); + writer->next = *anchor; + *anchor = writer; return CURLE_OK; } diff --git a/Utilities/cmcurl/lib/sendf.h b/Utilities/cmcurl/lib/sendf.h index 9ee00bb..a70189f 100644 --- a/Utilities/cmcurl/lib/sendf.h +++ b/Utilities/cmcurl/lib/sendf.h @@ -50,43 +50,122 @@ #define CLIENTWRITE_1XX (1<<5) /* a 1xx response related HEADER */ #define CLIENTWRITE_TRAILER (1<<6) /* a trailer HEADER */ +/** + * Write `len` bytes at `prt` to the client. `type` indicates what + * kind of data is being written. + */ CURLcode Curl_client_write(struct Curl_easy *data, int type, char *ptr, size_t len) WARN_UNUSED_RESULT; +/** + * For a paused transfer, there might be buffered data held back. + * Attempt to flush this data to the client. This *may* trigger + * another pause of the transfer. + */ CURLcode Curl_client_unpause(struct Curl_easy *data); + +/** + * Free all resources related to client writing. + */ void Curl_client_cleanup(struct Curl_easy *data); -struct contenc_writer { - const struct content_encoding *handler; /* Encoding handler. */ - struct contenc_writer *downstream; /* Downstream writer. */ - unsigned int order; /* Ordering within writer stack. */ +/** + * Client Writers - a chain passing transfer BODY data to the client. + * Main application: HTTP and related protocols + * Other uses: monitoring of download progress + * + * Writers in the chain are order by their `phase`. First come all + * writers in CURL_CW_RAW, followed by any in CURL_CW_TRANSFER_DECODE, + * followed by any in CURL_CW_PROTOCOL, etc. + * + * When adding a writer, it is inserted as first in its phase. This means + * the order of adding writers of the same phase matters, but writers for + * different phases may be added in any order. + * + * Writers which do modify the BODY data written are expected to be of + * phases TRANSFER_DECODE or CONTENT_DECODE. The other phases are intended + * for monitoring writers. Which do *not* modify the data but gather + * statistics or update progress reporting. + */ + +/* Phase a writer operates at. */ +typedef enum { + CURL_CW_RAW, /* raw data written, before any decoding */ + CURL_CW_TRANSFER_DECODE, /* remove transfer-encodings */ + CURL_CW_PROTOCOL, /* after transfer, but before content decoding */ + CURL_CW_CONTENT_DECODE, /* remove content-encodings */ + CURL_CW_CLIENT /* data written to client */ +} Curl_cwriter_phase; + +/* Client Writer Type, provides the implementation */ +struct Curl_cwtype { + const char *name; /* writer name. */ + const char *alias; /* writer name alias, maybe NULL. */ + CURLcode (*do_init)(struct Curl_easy *data, + struct Curl_cwriter *writer); + CURLcode (*do_write)(struct Curl_easy *data, + struct Curl_cwriter *writer, int type, + const char *buf, size_t nbytes); + void (*do_close)(struct Curl_easy *data, + struct Curl_cwriter *writer); + size_t cwriter_size; /* sizeof() allocated struct Curl_cwriter */ }; -/* Content encoding writer. */ -struct content_encoding { - const char *name; /* Encoding name. */ - const char *alias; /* Encoding name alias. */ - CURLcode (*init_writer)(struct Curl_easy *data, - struct contenc_writer *writer); - CURLcode (*unencode_write)(struct Curl_easy *data, - struct contenc_writer *writer, - const char *buf, size_t nbytes); - void (*close_writer)(struct Curl_easy *data, - struct contenc_writer *writer); - size_t writersize; +/* Client writer instance */ +struct Curl_cwriter { + const struct Curl_cwtype *cwt; /* type implementation */ + struct Curl_cwriter *next; /* Downstream writer. */ + Curl_cwriter_phase phase; /* phase at which it operates */ }; +/** + * Create a new cwriter instance with given type and phase. Is not + * inserted into the writer chain by this call. + * Invokes `writer->do_init()`. + */ +CURLcode Curl_cwriter_create(struct Curl_cwriter **pwriter, + struct Curl_easy *data, + const struct Curl_cwtype *ce_handler, + Curl_cwriter_phase phase); -CURLcode Curl_client_create_writer(struct contenc_writer **pwriter, - struct Curl_easy *data, - const struct content_encoding *ce_handler, - int order); +/** + * Free a cwriter instance. + * Invokes `writer->do_close()`. + */ +void Curl_cwriter_free(struct Curl_easy *data, + struct Curl_cwriter *writer); -void Curl_client_free_writer(struct Curl_easy *data, - struct contenc_writer *writer); +/** + * Count the number of writers installed of the given phase. + */ +size_t Curl_cwriter_count(struct Curl_easy *data, Curl_cwriter_phase phase); -CURLcode Curl_client_add_writer(struct Curl_easy *data, - struct contenc_writer *writer); +/** + * Adds a writer to the transfer's writer chain. + * The writers `phase` determines where in the chain it is inserted. + */ +CURLcode Curl_cwriter_add(struct Curl_easy *data, + struct Curl_cwriter *writer); + +/** + * Convenience method for calling `writer->do_write()` that + * checks for NULL writer. + */ +CURLcode Curl_cwriter_write(struct Curl_easy *data, + struct Curl_cwriter *writer, int type, + const char *buf, size_t nbytes); + +/** + * Default implementations for do_init, do_write, do_close that + * do nothing and pass the data through. + */ +CURLcode Curl_cwriter_def_init(struct Curl_easy *data, + struct Curl_cwriter *writer); +CURLcode Curl_cwriter_def_write(struct Curl_easy *data, + struct Curl_cwriter *writer, int type, + const char *buf, size_t nbytes); +void Curl_cwriter_def_close(struct Curl_easy *data, + struct Curl_cwriter *writer); /* internal read-function, does plain socket, SSL and krb4 */ diff --git a/Utilities/cmcurl/lib/setopt.c b/Utilities/cmcurl/lib/setopt.c index 0d399ad..a08140c 100644 --- a/Utilities/cmcurl/lib/setopt.c +++ b/Utilities/cmcurl/lib/setopt.c @@ -50,6 +50,7 @@ #include "multiif.h" #include "altsvc.h" #include "hsts.h" +#include "tftp.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" @@ -171,7 +172,7 @@ static CURLcode protocol2num(const char *str, curl_prot_t *val) str = strchr(str, ','); tlen = str? (size_t) (str - token): strlen(token); if(tlen) { - const struct Curl_handler *h = Curl_builtin_scheme(token, tlen); + const struct Curl_handler *h = Curl_getn_scheme_handler(token, tlen); if(!h) return CURLE_UNSUPPORTED_PROTOCOL; @@ -261,43 +262,43 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * Set the absolute number of maximum simultaneous alive connection that * libcurl is allowed to have. */ - arg = va_arg(param, long); - if(arg < 0) + uarg = va_arg(param, unsigned long); + if(uarg > UINT_MAX) return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.maxconnects = arg; + data->set.maxconnects = (unsigned int)uarg; break; case CURLOPT_FORBID_REUSE: /* * When this transfer is done, it must not be left to be reused by a * subsequent transfer but shall be closed immediately. */ - data->set.reuse_forbid = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.reuse_forbid = (0 != va_arg(param, long)); break; case CURLOPT_FRESH_CONNECT: /* * This transfer shall not use a previously cached connection but * should be made with a fresh new connect! */ - data->set.reuse_fresh = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.reuse_fresh = (0 != va_arg(param, long)); break; case CURLOPT_VERBOSE: /* * Verbose means infof() calls that give a lot of information about * the connection and transfer procedures as well as internal choices. */ - data->set.verbose = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.verbose = (0 != va_arg(param, long)); break; case CURLOPT_HEADER: /* * Set to include the header in the general data output stream. */ - data->set.include_header = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.include_header = (0 != va_arg(param, long)); break; case CURLOPT_NOPROGRESS: /* * Shut off the internal supported progress meter */ - data->set.hide_progress = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.hide_progress = (0 != va_arg(param, long)); if(data->set.hide_progress) data->progress.flags |= PGRS_HIDE; else @@ -307,7 +308,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) /* * Do not include the body part in the output data stream. */ - data->set.opt_no_body = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.opt_no_body = (0 != va_arg(param, long)); #ifndef CURL_DISABLE_HTTP if(data->set.opt_no_body) /* in HTTP lingo, no body means using the HEAD request... */ @@ -321,11 +322,10 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * Don't output the >=400 error code HTML-page, but instead only * return error. */ - data->set.http_fail_on_error = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.http_fail_on_error = (0 != va_arg(param, long)); break; case CURLOPT_KEEP_SENDING_ON_ERROR: - data->set.http_keep_sending_on_error = (0 != va_arg(param, long)) ? - TRUE : FALSE; + data->set.http_keep_sending_on_error = (0 != va_arg(param, long)); break; case CURLOPT_UPLOAD: case CURLOPT_PUT: @@ -353,7 +353,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * Try to get the file time of the remote document. The time will * later (possibly) become available using curl_easy_getinfo(). */ - data->set.get_filetime = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.get_filetime = (0 != va_arg(param, long)); break; case CURLOPT_SERVER_RESPONSE_TIMEOUT: /* @@ -379,7 +379,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * TFTP option that specifies the block size to use for data transmission. */ arg = va_arg(param, long); - if(arg < 0) + if(arg > TFTP_BLKSIZE_MAX || arg < TFTP_BLKSIZE_MIN) return CURLE_BAD_FUNCTION_ARGUMENT; data->set.tftp_blksize = arg; break; @@ -409,7 +409,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * * Transfer using ASCII (instead of BINARY). */ - data->set.prefer_ascii = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.prefer_ascii = (0 != va_arg(param, long)); break; case CURLOPT_TIMECONDITION: /* @@ -577,7 +577,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) /* * Switch on automatic referer that gets set if curl follows locations. */ - data->set.http_auto_referer = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.http_auto_referer = (0 != va_arg(param, long)); break; case CURLOPT_ACCEPT_ENCODING: @@ -592,28 +592,23 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) */ argptr = va_arg(param, char *); if(argptr && !*argptr) { - argptr = Curl_all_content_encodings(); - if(!argptr) - result = CURLE_OUT_OF_MEMORY; - else { - result = Curl_setstropt(&data->set.str[STRING_ENCODING], argptr); - free(argptr); - } + char all[256]; + Curl_all_content_encodings(all, sizeof(all)); + result = Curl_setstropt(&data->set.str[STRING_ENCODING], all); } else result = Curl_setstropt(&data->set.str[STRING_ENCODING], argptr); break; case CURLOPT_TRANSFER_ENCODING: - data->set.http_transfer_encoding = (0 != va_arg(param, long)) ? - TRUE : FALSE; + data->set.http_transfer_encoding = (0 != va_arg(param, long)); break; case CURLOPT_FOLLOWLOCATION: /* * Follow Location: header hints on an HTTP-server. */ - data->set.http_follow_location = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.http_follow_location = (0 != va_arg(param, long)); break; case CURLOPT_UNRESTRICTED_AUTH: @@ -621,8 +616,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * Send authentication (user+password) when following locations, even when * hostname changed. */ - data->set.allow_auth_to_other_hosts = - (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.allow_auth_to_other_hosts = (0 != va_arg(param, long)); break; case CURLOPT_MAXREDIRS: @@ -736,7 +730,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * Set header option. */ arg = va_arg(param, long); - data->set.sep_headers = (bool)((arg & CURLHEADER_SEPARATE)? TRUE: FALSE); + data->set.sep_headers = !!(arg & CURLHEADER_SEPARATE); break; #if !defined(CURL_DISABLE_COOKIES) @@ -760,18 +754,18 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) return CURLE_BAD_FUNCTION_ARGUMENT; /* append the cookie file name to the list of file names, and deal with them later */ - cl = curl_slist_append(data->set.cookielist, argptr); + cl = curl_slist_append(data->state.cookielist, argptr); if(!cl) { - curl_slist_free_all(data->set.cookielist); - data->set.cookielist = NULL; + curl_slist_free_all(data->state.cookielist); + data->state.cookielist = NULL; return CURLE_OUT_OF_MEMORY; } - data->set.cookielist = cl; /* store the list for later use */ + data->state.cookielist = cl; /* store the list for later use */ } else { /* clear the list of cookie files */ - curl_slist_free_all(data->set.cookielist); - data->set.cookielist = NULL; + curl_slist_free_all(data->state.cookielist); + data->state.cookielist = NULL; if(!data->share || !data->share->cookies) { /* throw away all existing cookies if this isn't a shared cookie @@ -811,17 +805,8 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * prevent the forthcoming read-cookies-from-file actions to accept * cookies that are marked as being session cookies, as they belong to a * previous session. - * - * In the original Netscape cookie spec, "session cookies" are cookies - * with no expire date set. RFC2109 describes the same action if no - * 'Max-Age' is set and RFC2965 includes the RFC2109 description and adds - * a 'Discard' action that can enforce the discard even for cookies that - * have a Max-Age. - * - * We run mostly with the original cookie spec, as hardly anyone implements - * anything else. */ - data->set.cookiesession = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.cookiesession = (0 != va_arg(param, long)); break; case CURLOPT_COOKIELIST: @@ -956,7 +941,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) if(arg) return CURLE_BAD_FUNCTION_ARGUMENT; #else - data->set.http09_allowed = arg ? TRUE : FALSE; + data->set.http09_allowed = !!arg; #endif break; @@ -997,8 +982,9 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) break; case CURLOPT_MIME_OPTIONS: - data->set.mime_options = (unsigned int)va_arg(param, long); - break; + arg = va_arg(param, long); + data->set.mime_formescape = !!(arg & CURLMIMEOPT_FORMESCAPE); + break; # endif #endif @@ -1018,8 +1004,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) /* the DIGEST_IE bit is only used to set a special marker, for all the rest we need to handle it as normal DIGEST */ - data->state.authhost.iestyle = - (bool)((auth & CURLAUTH_DIGEST_IE) ? TRUE : FALSE); + data->state.authhost.iestyle = !!(auth & CURLAUTH_DIGEST_IE); if(auth & CURLAUTH_DIGEST_IE) { auth |= CURLAUTH_DIGEST; /* set standard digest bit */ @@ -1072,8 +1057,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) /* * Tunnel operations through the proxy instead of normal proxy use */ - data->set.tunnel_thru_httpproxy = (0 != va_arg(param, long)) ? - TRUE : FALSE; + data->set.tunnel_thru_httpproxy = (0 != va_arg(param, long)); break; case CURLOPT_PROXYPORT: @@ -1102,8 +1086,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) /* the DIGEST_IE bit is only used to set a special marker, for all the rest we need to handle it as normal DIGEST */ - data->state.authproxy.iestyle = - (bool)((auth & CURLAUTH_DIGEST_IE) ? TRUE : FALSE); + data->state.authproxy.iestyle = !!(auth & CURLAUTH_DIGEST_IE); if(auth & CURLAUTH_DIGEST_IE) { auth |= CURLAUTH_DIGEST; /* set standard digest bit */ @@ -1203,7 +1186,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) /* * Set flag for NEC SOCK5 support */ - data->set.socks5_gssapi_nec = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.socks5_gssapi_nec = (0 != va_arg(param, long)); break; #endif #ifndef CURL_DISABLE_PROXY @@ -1251,7 +1234,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * An option that changes the command to one that asks for a list only, no * file info details. Used for FTP, POP3 and SFTP. */ - data->set.list_only = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.list_only = (0 != va_arg(param, long)); break; #endif case CURLOPT_APPEND: @@ -1259,7 +1242,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * We want to upload and append to an existing file. Used for FTP and * SFTP. */ - data->set.remote_append = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.remote_append = (0 != va_arg(param, long)); break; #ifndef CURL_DISABLE_FTP @@ -1270,7 +1253,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) arg = va_arg(param, long); if((arg < CURLFTPMETHOD_DEFAULT) || (arg >= CURLFTPMETHOD_LAST)) return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.ftp_filemethod = (unsigned char)(curl_ftpfile)arg; + data->set.ftp_filemethod = (unsigned char)arg; break; case CURLOPT_FTPPORT: /* @@ -1278,26 +1261,26 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) */ result = Curl_setstropt(&data->set.str[STRING_FTPPORT], va_arg(param, char *)); - data->set.ftp_use_port = (data->set.str[STRING_FTPPORT]) ? TRUE : FALSE; + data->set.ftp_use_port = !!(data->set.str[STRING_FTPPORT]); break; case CURLOPT_FTP_USE_EPRT: - data->set.ftp_use_eprt = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.ftp_use_eprt = (0 != va_arg(param, long)); break; case CURLOPT_FTP_USE_EPSV: - data->set.ftp_use_epsv = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.ftp_use_epsv = (0 != va_arg(param, long)); break; case CURLOPT_FTP_USE_PRET: - data->set.ftp_use_pret = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.ftp_use_pret = (0 != va_arg(param, long)); break; case CURLOPT_FTP_SSL_CCC: arg = va_arg(param, long); if((arg < CURLFTPSSL_CCC_NONE) || (arg >= CURLFTPSSL_CCC_LAST)) return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.ftp_ccc = (unsigned char)(curl_ftpccc)arg; + data->set.ftp_ccc = (unsigned char)arg; break; case CURLOPT_FTP_SKIP_PASV_IP: @@ -1305,7 +1288,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * Enable or disable FTP_SKIP_PASV_IP, which will disable/enable the * bypass of the IP address in PASV responses. */ - data->set.ftp_skip_ip = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.ftp_skip_ip = (0 != va_arg(param, long)); break; case CURLOPT_FTP_ACCOUNT: @@ -1333,7 +1316,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) */ result = Curl_setstropt(&data->set.str[STRING_KRB_LEVEL], va_arg(param, char *)); - data->set.krb = (data->set.str[STRING_KRB_LEVEL]) ? TRUE : FALSE; + data->set.krb = !!(data->set.str[STRING_KRB_LEVEL]); break; #endif #if !defined(CURL_DISABLE_FTP) || defined(USE_SSH) @@ -1867,14 +1850,14 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) /* * Kludgy option to enable CRLF conversions. Subject for removal. */ - data->set.crlf = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.crlf = (0 != va_arg(param, long)); break; #ifndef CURL_DISABLE_PROXY case CURLOPT_HAPROXYPROTOCOL: /* * Set to send the HAProxy Proxy Protocol header */ - data->set.haproxyprotocol = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.haproxyprotocol = (0 != va_arg(param, long)); break; case CURLOPT_HAPROXY_CLIENT_IP: /* @@ -1926,22 +1909,17 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) /* * Enable peer SSL verifying. */ - data->set.ssl.primary.verifypeer = (0 != va_arg(param, long)) ? - TRUE : FALSE; + data->set.ssl.primary.verifypeer = (0 != va_arg(param, long)); /* Update the current connection ssl_config. */ - if(data->conn) { - data->conn->ssl_config.verifypeer = - data->set.ssl.primary.verifypeer; - } + Curl_ssl_conn_config_update(data, FALSE); break; #ifndef CURL_DISABLE_DOH case CURLOPT_DOH_SSL_VERIFYPEER: /* * Enable peer SSL verifying for DoH. */ - data->set.doh_verifypeer = (0 != va_arg(param, long)) ? - TRUE : FALSE; + data->set.doh_verifypeer = (0 != va_arg(param, long)); break; #endif #ifndef CURL_DISABLE_PROXY @@ -1953,10 +1931,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) (0 != va_arg(param, long))?TRUE:FALSE; /* Update the current connection proxy_ssl_config. */ - if(data->conn) { - data->conn->proxy_ssl_config.verifypeer = - data->set.proxy_ssl.primary.verifypeer; - } + Curl_ssl_conn_config_update(data, TRUE); break; #endif case CURLOPT_SSL_VERIFYHOST: @@ -1968,13 +1943,10 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) /* Obviously people are not reading documentation and too many thought this argument took a boolean when it wasn't and misused it. Treat 1 and 2 the same */ - data->set.ssl.primary.verifyhost = (bool)((arg & 3) ? TRUE : FALSE); + data->set.ssl.primary.verifyhost = !!(arg & 3); /* Update the current connection ssl_config. */ - if(data->conn) { - data->conn->ssl_config.verifyhost = - data->set.ssl.primary.verifyhost; - } + Curl_ssl_conn_config_update(data, FALSE); break; #ifndef CURL_DISABLE_DOH case CURLOPT_DOH_SSL_VERIFYHOST: @@ -1984,7 +1956,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) arg = va_arg(param, long); /* Treat both 1 and 2 as TRUE */ - data->set.doh_verifyhost = (bool)((arg & 3) ? TRUE : FALSE); + data->set.doh_verifyhost = !!(arg & 3); break; #endif #ifndef CURL_DISABLE_PROXY @@ -1996,12 +1968,8 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) /* Treat both 1 and 2 as TRUE */ data->set.proxy_ssl.primary.verifyhost = (bool)((arg & 3)?TRUE:FALSE); - /* Update the current connection proxy_ssl_config. */ - if(data->conn) { - data->conn->proxy_ssl_config.verifyhost = - data->set.proxy_ssl.primary.verifyhost; - } + Curl_ssl_conn_config_update(data, TRUE); break; #endif case CURLOPT_SSL_VERIFYSTATUS: @@ -2013,14 +1981,10 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) break; } - data->set.ssl.primary.verifystatus = (0 != va_arg(param, long)) ? - TRUE : FALSE; + data->set.ssl.primary.verifystatus = (0 != va_arg(param, long)); /* Update the current connection ssl_config. */ - if(data->conn) { - data->conn->ssl_config.verifystatus = - data->set.ssl.primary.verifystatus; - } + Curl_ssl_conn_config_update(data, FALSE); break; #ifndef CURL_DISABLE_DOH case CURLOPT_DOH_SSL_VERIFYSTATUS: @@ -2032,8 +1996,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) break; } - data->set.doh_verifystatus = (0 != va_arg(param, long)) ? - TRUE : FALSE; + data->set.doh_verifystatus = (0 != va_arg(param, long)); break; #endif case CURLOPT_SSL_CTX_FUNCTION: @@ -2067,12 +2030,12 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) break; } - data->set.ssl.falsestart = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.ssl.falsestart = (0 != va_arg(param, long)); break; case CURLOPT_CERTINFO: #ifdef USE_SSL if(Curl_ssl_supports(data, SSLSUPP_CERTINFO)) - data->set.ssl.certinfo = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.ssl.certinfo = (0 != va_arg(param, long)); else #endif result = CURLE_NOT_BUILT_IN; @@ -2118,14 +2081,14 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * Specify entire PEM of the CA certificate */ #ifdef USE_SSL - if(Curl_ssl_supports(data, SSLSUPP_CAINFO_BLOB)) + if(Curl_ssl_supports(data, SSLSUPP_CAINFO_BLOB)) { result = Curl_setblobopt(&data->set.blobs[BLOB_CAINFO], va_arg(param, struct curl_blob *)); + break; + } else #endif return CURLE_NOT_BUILT_IN; - - break; #ifndef CURL_DISABLE_PROXY case CURLOPT_PROXY_CAINFO: /* @@ -2141,13 +2104,14 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * Specify entire PEM of the CA certificate */ #ifdef USE_SSL - if(Curl_ssl_supports(data, SSLSUPP_CAINFO_BLOB)) + if(Curl_ssl_supports(data, SSLSUPP_CAINFO_BLOB)) { result = Curl_setblobopt(&data->set.blobs[BLOB_CAINFO_PROXY], va_arg(param, struct curl_blob *)); + break; + } else #endif return CURLE_NOT_BUILT_IN; - break; #endif case CURLOPT_CAPATH: /* @@ -2278,7 +2242,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * The application asks not to set any signal() or alarm() handlers, * even when using a timeout. */ - data->set.no_signal = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.no_signal = (0 != va_arg(param, long)); break; case CURLOPT_SHARE: @@ -2453,11 +2417,11 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * Enable or disable TCP_NODELAY, which will disable/enable the Nagle * algorithm */ - data->set.tcp_nodelay = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.tcp_nodelay = (0 != va_arg(param, long)); break; case CURLOPT_IGNORE_CONTENT_LENGTH: - data->set.ignorecl = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.ignorecl = (0 != va_arg(param, long)); break; case CURLOPT_CONNECT_ONLY: @@ -2532,8 +2496,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) break; case CURLOPT_SSL_SESSIONID_CACHE: - data->set.ssl.primary.sessionid = (0 != va_arg(param, long)) ? - TRUE : FALSE; + data->set.ssl.primary.sessionid = (0 != va_arg(param, long)); #ifndef CURL_DISABLE_PROXY data->set.proxy_ssl.primary.sessionid = data->set.ssl.primary.sessionid; #endif @@ -2622,7 +2585,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * disable libcurl transfer encoding is used */ #ifndef USE_HYPER - data->set.http_te_skip = (0 == va_arg(param, long)) ? TRUE : FALSE; + data->set.http_te_skip = (0 == va_arg(param, long)); break; #else return CURLE_NOT_BUILT_IN; /* hyper doesn't support */ @@ -2632,7 +2595,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) /* * raw data passed to the application when content encoding is used */ - data->set.http_ce_skip = (0 == va_arg(param, long)) ? TRUE : FALSE; + data->set.http_ce_skip = (0 == va_arg(param, long)); break; #if !defined(CURL_DISABLE_FTP) || defined(USE_SSH) @@ -2733,7 +2696,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) break; case CURLOPT_MAIL_RCPT_ALLOWFAILS: /* allow RCPT TO command to fail for some recipients */ - data->set.mail_rcpt_allowfails = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.mail_rcpt_allowfails = (0 != va_arg(param, long)); break; #endif @@ -2745,7 +2708,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) case CURLOPT_SASL_IR: /* Enable/disable SASL initial response */ - data->set.sasl_ir = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.sasl_ir = (0 != va_arg(param, long)); break; #ifndef CURL_DISABLE_RTSP case CURLOPT_RTSP_REQUEST: @@ -2859,7 +2822,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) #endif #ifndef CURL_DISABLE_FTP case CURLOPT_WILDCARDMATCH: - data->set.wildcard_enabled = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.wildcard_enabled = (0 != va_arg(param, long)); break; case CURLOPT_CHUNK_BGN_FUNCTION: data->set.chunk_bgn = va_arg(param, curl_chunk_bgn_callback); @@ -2942,7 +2905,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) break; #endif case CURLOPT_TCP_KEEPALIVE: - data->set.tcp_keepalive = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.tcp_keepalive = (0 != va_arg(param, long)); break; case CURLOPT_TCP_KEEPIDLE: arg = va_arg(param, long); @@ -2971,7 +2934,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) case CURLOPT_SSL_ENABLE_NPN: break; case CURLOPT_SSL_ENABLE_ALPN: - data->set.ssl_enable_alpn = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.ssl_enable_alpn = (0 != va_arg(param, long)); break; #ifdef USE_UNIX_SOCKETS case CURLOPT_UNIX_SOCKET_PATH: @@ -2987,10 +2950,10 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) #endif case CURLOPT_PATH_AS_IS: - data->set.path_as_is = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.path_as_is = (0 != va_arg(param, long)); break; case CURLOPT_PIPEWAIT: - data->set.pipewait = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.pipewait = (0 != va_arg(param, long)); break; case CURLOPT_STREAM_WEIGHT: #if defined(USE_HTTP2) || defined(USE_HTTP3) @@ -3025,12 +2988,11 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) break; #ifndef CURL_DISABLE_SHUFFLE_DNS case CURLOPT_DNS_SHUFFLE_ADDRESSES: - data->set.dns_shuffle_addresses = (0 != va_arg(param, long)) ? TRUE:FALSE; + data->set.dns_shuffle_addresses = (0 != va_arg(param, long)); break; #endif case CURLOPT_DISALLOW_USERNAME_IN_URL: - data->set.disallow_username_in_url = - (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.disallow_username_in_url = (0 != va_arg(param, long)); break; #ifndef CURL_DISABLE_DOH case CURLOPT_DOH_URL: @@ -3095,18 +3057,18 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) /* this needs to build a list of file names to read from, so that it can read them later, as we might get a shared HSTS handle to load them into */ - h = curl_slist_append(data->set.hstslist, argptr); + h = curl_slist_append(data->state.hstslist, argptr); if(!h) { - curl_slist_free_all(data->set.hstslist); - data->set.hstslist = NULL; + curl_slist_free_all(data->state.hstslist); + data->state.hstslist = NULL; return CURLE_OUT_OF_MEMORY; } - data->set.hstslist = h; /* store the list for later use */ + data->state.hstslist = h; /* store the list for later use */ } else { /* clear the list of HSTS files */ - curl_slist_free_all(data->set.hstslist); - data->set.hstslist = NULL; + curl_slist_free_all(data->state.hstslist); + data->state.hstslist = NULL; if(!data->share || !data->share->hsts) /* throw away the HSTS cache unless shared */ Curl_hsts_cleanup(&data->hsts); diff --git a/Utilities/cmcurl/lib/setup-win32.h b/Utilities/cmcurl/lib/setup-win32.h index 1394838..4e034d4 100644 --- a/Utilities/cmcurl/lib/setup-win32.h +++ b/Utilities/cmcurl/lib/setup-win32.h @@ -53,14 +53,14 @@ # ifndef NOGDI # define NOGDI # endif -# include <winerror.h> -# include <windows.h> # ifdef HAVE_WINSOCK2_H # include <winsock2.h> # ifdef HAVE_WS2TCPIP_H # include <ws2tcpip.h> # endif # endif +# include <windows.h> +# include <winerror.h> # include <tchar.h> # ifdef UNICODE typedef wchar_t *(*curl_wcsdup_callback)(const wchar_t *str); @@ -96,18 +96,12 @@ #ifndef _WIN32_WINNT_WS03 #define _WIN32_WINNT_WS03 0x0502 /* Windows Server 2003 */ #endif -#ifndef _WIN32_WINNT_WIN6 -#define _WIN32_WINNT_WIN6 0x0600 /* Windows Vista */ -#endif #ifndef _WIN32_WINNT_VISTA #define _WIN32_WINNT_VISTA 0x0600 /* Windows Vista */ #endif #ifndef _WIN32_WINNT_WS08 #define _WIN32_WINNT_WS08 0x0600 /* Windows Server 2008 */ #endif -#ifndef _WIN32_WINNT_LONGHORN -#define _WIN32_WINNT_LONGHORN 0x0600 /* Windows Vista */ -#endif #ifndef _WIN32_WINNT_WIN7 #define _WIN32_WINNT_WIN7 0x0601 /* Windows 7 */ #endif @@ -117,9 +111,6 @@ #ifndef _WIN32_WINNT_WINBLUE #define _WIN32_WINNT_WINBLUE 0x0603 /* Windows 8.1 */ #endif -#ifndef _WIN32_WINNT_WINTHRESHOLD -#define _WIN32_WINNT_WINTHRESHOLD 0x0A00 /* Windows 10 */ -#endif #ifndef _WIN32_WINNT_WIN10 #define _WIN32_WINNT_WIN10 0x0A00 /* Windows 10 */ #endif diff --git a/Utilities/cmcurl/lib/share.h b/Utilities/cmcurl/lib/share.h index 7f55aac..632d919 100644 --- a/Utilities/cmcurl/lib/share.h +++ b/Utilities/cmcurl/lib/share.h @@ -31,14 +31,6 @@ #include "urldata.h" #include "conncache.h" -/* SalfordC says "A structure member may not be volatile". Hence: - */ -#ifdef __SALFORDC__ -#define CURL_VOLATILE -#else -#define CURL_VOLATILE volatile -#endif - #define CURL_GOOD_SHARE 0x7e117a1e #define GOOD_SHARE_HANDLE(x) ((x) && (x)->magic == CURL_GOOD_SHARE) @@ -46,7 +38,7 @@ struct Curl_share { unsigned int magic; /* CURL_GOOD_SHARE */ unsigned int specifier; - CURL_VOLATILE unsigned int dirty; + volatile unsigned int dirty; curl_lock_function lockfunc; curl_unlock_function unlockfunc; diff --git a/Utilities/cmcurl/lib/smb.c b/Utilities/cmcurl/lib/smb.c index 32c5137..6c8a47c 100644 --- a/Utilities/cmcurl/lib/smb.c +++ b/Utilities/cmcurl/lib/smb.c @@ -27,7 +27,7 @@ #if !defined(CURL_DISABLE_SMB) && defined(USE_CURL_NTLM_CORE) -#ifdef WIN32 +#ifdef _WIN32 #define getpid GetCurrentProcessId #endif @@ -1047,14 +1047,7 @@ static CURLcode smb_request_state(struct Curl_easy *data, bool *done) break; } } - data->req.bytecount += len; data->req.offset += len; - result = Curl_pgrsSetDownloadCounter(data, data->req.bytecount); - if(result) { - req->result = result; - next_state = SMB_CLOSE; - break; - } next_state = (len < MAX_PAYLOAD_SIZE) ? SMB_CLOSE : SMB_DOWNLOAD; break; diff --git a/Utilities/cmcurl/lib/smtp.c b/Utilities/cmcurl/lib/smtp.c index 81a17e3..65fbc5b 100644 --- a/Utilities/cmcurl/lib/smtp.c +++ b/Utilities/cmcurl/lib/smtp.c @@ -1320,7 +1320,7 @@ static CURLcode smtp_init(struct Curl_easy *data) CURLcode result = CURLE_OK; struct SMTP *smtp; - smtp = data->req.p.smtp = calloc(sizeof(struct SMTP), 1); + smtp = data->req.p.smtp = calloc(1, sizeof(struct SMTP)); if(!smtp) result = CURLE_OUT_OF_MEMORY; diff --git a/Utilities/cmcurl/lib/socketpair.c b/Utilities/cmcurl/lib/socketpair.c index 963e140..e3d40ff 100644 --- a/Utilities/cmcurl/lib/socketpair.c +++ b/Utilities/cmcurl/lib/socketpair.c @@ -28,7 +28,7 @@ #include "rand.h" #if !defined(HAVE_SOCKETPAIR) && !defined(CURL_DISABLE_SOCKETPAIR) -#ifdef WIN32 +#ifdef _WIN32 /* * This is a socketpair() implementation for Windows. */ @@ -50,7 +50,7 @@ #ifndef INADDR_LOOPBACK #define INADDR_LOOPBACK 0x7f000001 #endif /* !INADDR_LOOPBACK */ -#endif /* !WIN32 */ +#endif /* !_WIN32 */ #include "nonblock.h" /* for curlx_nonblock */ #include "timeval.h" /* needed before select.h */ @@ -87,7 +87,7 @@ int Curl_socketpair(int domain, int type, int protocol, socks[0] = socks[1] = CURL_SOCKET_BAD; -#if defined(WIN32) || defined(__CYGWIN__) +#if defined(_WIN32) || defined(__CYGWIN__) /* don't set SO_REUSEADDR on Windows */ (void)reuse; #ifdef SO_EXCLUSIVEADDRUSE diff --git a/Utilities/cmcurl/lib/socketpair.h b/Utilities/cmcurl/lib/socketpair.h index 306ab5d..bd499ab 100644 --- a/Utilities/cmcurl/lib/socketpair.h +++ b/Utilities/cmcurl/lib/socketpair.h @@ -25,6 +25,23 @@ ***************************************************************************/ #include "curl_setup.h" + +#ifdef HAVE_PIPE + +#define wakeup_write write +#define wakeup_read read +#define wakeup_close close +#define wakeup_create pipe + +#else /* HAVE_PIPE */ + +#define wakeup_write swrite +#define wakeup_read sread +#define wakeup_close sclose +#define wakeup_create(p) Curl_socketpair(AF_UNIX, SOCK_STREAM, 0, p) + +#endif /* HAVE_PIPE */ + #ifndef HAVE_SOCKETPAIR #include <curl/curl.h> diff --git a/Utilities/cmcurl/lib/socks.c b/Utilities/cmcurl/lib/socks.c index a7b5ab0..3a396de 100644 --- a/Utilities/cmcurl/lib/socks.c +++ b/Utilities/cmcurl/lib/socks.c @@ -339,8 +339,8 @@ static CURLproxycode do_SOCKS4(struct Curl_cfilter *cf, if(dns) { #ifdef CURLRES_ASYNCH - data->state.async.dns = dns; - data->state.async.done = TRUE; + conn->resolve_async.dns = dns; + conn->resolve_async.done = TRUE; #endif infof(data, "Hostname '%s' was found", sx->hostname); sxstate(sx, data, CONNECT_RESOLVED); @@ -402,8 +402,11 @@ CONNECT_REQ_INIT: socksreq[8] = 0; /* ensure empty userid is NUL-terminated */ if(sx->proxy_user) { size_t plen = strlen(sx->proxy_user); - if(plen >= (size_t)data->set.buffer_size - 8) { - failf(data, "Too long SOCKS proxy user name, can't use"); + if(plen > 255) { + /* there is no real size limit to this field in the protocol, but + SOCKS5 limits the proxy user field to 255 bytes and it seems likely + that a longer field is either a mistake or malicious input */ + failf(data, "Too long SOCKS proxy user name"); return CURLPX_LONG_USER; } /* copy the proxy name WITH trailing zero */ @@ -426,7 +429,8 @@ CONNECT_REQ_INIT: socksreq[7] = 1; /* append hostname */ hostnamelen = strlen(sx->hostname) + 1; /* length including NUL */ - if(hostnamelen <= 255) + if((hostnamelen <= 255) && + (packetsize + hostnamelen < data->set.buffer_size)) strcpy((char *)socksreq + packetsize, sx->hostname); else { failf(data, "SOCKS4: too long host name"); @@ -802,8 +806,8 @@ CONNECT_REQ_INIT: if(dns) { #ifdef CURLRES_ASYNCH - data->state.async.dns = dns; - data->state.async.done = TRUE; + conn->resolve_async.dns = dns; + conn->resolve_async.done = TRUE; #endif infof(data, "SOCKS5: hostname '%s' found", sx->hostname); } @@ -819,10 +823,19 @@ CONNECT_REQ_INIT: /* FALLTHROUGH */ CONNECT_RESOLVED: case CONNECT_RESOLVED: { - char dest[MAX_IPADR_LEN] = "unknown"; /* printable address */ + char dest[MAX_IPADR_LEN]; /* printable address */ struct Curl_addrinfo *hp = NULL; if(dns) hp = dns->addr; +#ifdef ENABLE_IPV6 + if(data->set.ipver != CURL_IPRESOLVE_WHATEVER) { + int wanted_family = data->set.ipver == CURL_IPRESOLVE_V4 ? + AF_INET : AF_INET6; + /* scan for the first proper address */ + while(hp && (hp->ai_family != wanted_family)) + hp = hp->ai_next; + } +#endif if(!hp) { failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.", sx->hostname); @@ -1119,7 +1132,7 @@ static CURLcode socks_proxy_cf_connect(struct Curl_cfilter *cf, return result; if(!sx) { - sx = calloc(sizeof(*sx), 1); + sx = calloc(1, sizeof(*sx)); if(!sx) return CURLE_OUT_OF_MEMORY; cf->ctx = sx; @@ -1157,32 +1170,29 @@ static CURLcode socks_proxy_cf_connect(struct Curl_cfilter *cf, return result; } -static int socks_cf_get_select_socks(struct Curl_cfilter *cf, +static void socks_cf_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data, - curl_socket_t *socks) + struct easy_pollset *ps) { struct socks_state *sx = cf->ctx; - int fds; - fds = cf->next->cft->get_select_socks(cf->next, data, socks); - if(!fds && cf->next->connected && !cf->connected && sx) { + if(!cf->connected && sx) { /* If we are not connected, the filter below is and has nothing * to wait on, we determine what to wait for. */ - socks[0] = Curl_conn_cf_get_socket(cf, data); + curl_socket_t sock = Curl_conn_cf_get_socket(cf, data); switch(sx->state) { case CONNECT_RESOLVING: case CONNECT_SOCKS_READ: case CONNECT_AUTH_READ: case CONNECT_REQ_READ: case CONNECT_REQ_READ_MORE: - fds = GETSOCK_READSOCK(0); + Curl_pollset_set_in_only(data, ps, sock); break; default: - fds = GETSOCK_WRITESOCK(0); + Curl_pollset_set_out_only(data, ps, sock); break; } } - return fds; } static void socks_proxy_cf_close(struct Curl_cfilter *cf, @@ -1227,7 +1237,7 @@ struct Curl_cftype Curl_cft_socks_proxy = { socks_proxy_cf_connect, socks_proxy_cf_close, socks_cf_get_host, - socks_cf_get_select_socks, + socks_cf_adjust_pollset, Curl_cf_def_data_pending, Curl_cf_def_send, Curl_cf_def_recv, diff --git a/Utilities/cmcurl/lib/strdup.c b/Utilities/cmcurl/lib/strdup.c index 07a6139..2578441 100644 --- a/Utilities/cmcurl/lib/strdup.c +++ b/Utilities/cmcurl/lib/strdup.c @@ -26,7 +26,7 @@ #include <curl/curl.h> -#ifdef WIN32 +#ifdef _WIN32 #include <wchar.h> #endif @@ -56,7 +56,7 @@ char *Curl_strdup(const char *str) } #endif -#ifdef WIN32 +#ifdef _WIN32 /*************************************************************************** * * Curl_wcsdup(source) @@ -101,6 +101,30 @@ void *Curl_memdup(const void *src, size_t length) /*************************************************************************** * + * Curl_strndup(source, length) + * + * Copies the 'source' string to a newly allocated buffer (that is returned). + * Copies not more than 'length' bytes (up to a null terminator) then adds a + * null terminator. + * + * Returns the new pointer or NULL on failure. + * + ***************************************************************************/ +void *Curl_strndup(const char *src, size_t length) +{ + char *buf = memchr(src, '\0', length); + if(buf) + length = buf - src; + buf = malloc(length + 1); + if(!buf) + return NULL; + memcpy(buf, src, length); + buf[length] = 0; + return buf; +} + +/*************************************************************************** + * * Curl_saferealloc(ptr, size) * * Does a normal realloc(), but will free the data pointer if the realloc diff --git a/Utilities/cmcurl/lib/strdup.h b/Utilities/cmcurl/lib/strdup.h index c3430b5..9f12b25 100644 --- a/Utilities/cmcurl/lib/strdup.h +++ b/Utilities/cmcurl/lib/strdup.h @@ -28,10 +28,11 @@ #ifndef HAVE_STRDUP char *Curl_strdup(const char *str); #endif -#ifdef WIN32 +#ifdef _WIN32 wchar_t* Curl_wcsdup(const wchar_t* src); #endif void *Curl_memdup(const void *src, size_t buffer_length); void *Curl_saferealloc(void *ptr, size_t size); +void *Curl_strndup(const char *src, size_t length); #endif /* HEADER_CURL_STRDUP_H */ diff --git a/Utilities/cmcurl/lib/strerror.c b/Utilities/cmcurl/lib/strerror.c index be41914..0d5f927 100644 --- a/Utilities/cmcurl/lib/strerror.c +++ b/Utilities/cmcurl/lib/strerror.c @@ -48,7 +48,7 @@ #include "curl_memory.h" #include "memdebug.h" -#if defined(WIN32) || defined(_WIN32_WCE) +#if defined(_WIN32) || defined(_WIN32_WCE) #define PRESERVE_WINDOWS_ERROR_CODE #endif @@ -762,7 +762,7 @@ get_winsock_error (int err, char *buf, size_t len) } #endif /* USE_WINSOCK */ -#if defined(WIN32) || defined(_WIN32_WCE) +#if defined(_WIN32) || defined(_WIN32_WCE) /* This is a helper function for Curl_strerror that converts Windows API error * codes (GetLastError) to error messages. * Returns NULL if no error message was found for error code. @@ -804,7 +804,7 @@ get_winapi_error(int err, char *buf, size_t buflen) return (*buf ? buf : NULL); } -#endif /* WIN32 || _WIN32_WCE */ +#endif /* _WIN32 || _WIN32_WCE */ /* * Our thread-safe and smart strerror() replacement. @@ -837,15 +837,15 @@ const char *Curl_strerror(int err, char *buf, size_t buflen) if(!buflen) return NULL; -#ifndef WIN32 +#ifndef _WIN32 DEBUGASSERT(err >= 0); #endif max = buflen - 1; *buf = '\0'; -#if defined(WIN32) || defined(_WIN32_WCE) -#if defined(WIN32) +#if defined(_WIN32) || defined(_WIN32_WCE) +#if defined(_WIN32) /* 'sys_nerr' is the maximum errno number, it is not widely portable */ if(err >= 0 && err < sys_nerr) strncpy(buf, sys_errlist[err], max); @@ -923,7 +923,7 @@ const char *Curl_strerror(int err, char *buf, size_t buflen) * Curl_winapi_strerror: * Variant of Curl_strerror if the error code is definitely Windows API. */ -#if defined(WIN32) || defined(_WIN32_WCE) +#if defined(_WIN32) || defined(_WIN32_WCE) const char *Curl_winapi_strerror(DWORD err, char *buf, size_t buflen) { #ifdef PRESERVE_WINDOWS_ERROR_CODE @@ -958,7 +958,7 @@ const char *Curl_winapi_strerror(DWORD err, char *buf, size_t buflen) return buf; } -#endif /* WIN32 || _WIN32_WCE */ +#endif /* _WIN32 || _WIN32_WCE */ #ifdef USE_WINDOWS_SSPI /* @@ -986,6 +986,10 @@ const char *Curl_sspi_strerror(int err, char *buf, size_t buflen) break; #define SEC2TXT(sec) case sec: txt = #sec; break SEC2TXT(CRYPT_E_REVOKED); + SEC2TXT(CRYPT_E_NO_REVOCATION_DLL); + SEC2TXT(CRYPT_E_NO_REVOCATION_CHECK); + SEC2TXT(CRYPT_E_REVOCATION_OFFLINE); + SEC2TXT(CRYPT_E_NOT_IN_REVOCATION_DATABASE); SEC2TXT(SEC_E_ALGORITHM_MISMATCH); SEC2TXT(SEC_E_BAD_BINDINGS); SEC2TXT(SEC_E_BAD_PKGID); diff --git a/Utilities/cmcurl/lib/strerror.h b/Utilities/cmcurl/lib/strerror.h index 399712f..6806867 100644 --- a/Utilities/cmcurl/lib/strerror.h +++ b/Utilities/cmcurl/lib/strerror.h @@ -29,7 +29,7 @@ #define STRERROR_LEN 256 /* a suitable length */ const char *Curl_strerror(int err, char *buf, size_t buflen); -#if defined(WIN32) || defined(_WIN32_WCE) +#if defined(_WIN32) || defined(_WIN32_WCE) const char *Curl_winapi_strerror(DWORD err, char *buf, size_t buflen); #endif #ifdef USE_WINDOWS_SSPI diff --git a/Utilities/cmcurl/lib/system_win32.c b/Utilities/cmcurl/lib/system_win32.c index 0cdaf3b..9408d02 100644 --- a/Utilities/cmcurl/lib/system_win32.c +++ b/Utilities/cmcurl/lib/system_win32.c @@ -24,7 +24,7 @@ #include "curl_setup.h" -#if defined(WIN32) +#if defined(_WIN32) #include <curl/curl.h> #include "system_win32.h" @@ -238,4 +238,4 @@ HMODULE Curl_load_library(LPCTSTR filename) #endif } -#endif /* WIN32 */ +#endif /* _WIN32 */ diff --git a/Utilities/cmcurl/lib/system_win32.h b/Utilities/cmcurl/lib/system_win32.h index 6482643..2566766 100644 --- a/Utilities/cmcurl/lib/system_win32.h +++ b/Utilities/cmcurl/lib/system_win32.h @@ -26,7 +26,7 @@ #include "curl_setup.h" -#if defined(WIN32) +#if defined(_WIN32) extern LARGE_INTEGER Curl_freq; extern bool Curl_isVistaOrGreater; @@ -42,8 +42,8 @@ extern IF_NAMETOINDEX_FN Curl_if_nametoindex; /* This is used to dynamically load DLLs */ HMODULE Curl_load_library(LPCTSTR filename); -#else /* WIN32 */ +#else /* _WIN32 */ #define Curl_win32_init(x) CURLE_OK -#endif /* !WIN32 */ +#endif /* !_WIN32 */ #endif /* HEADER_CURL_SYSTEM_WIN32_H */ diff --git a/Utilities/cmcurl/lib/tftp.c b/Utilities/cmcurl/lib/tftp.c index e78140d..6630155 100644 --- a/Utilities/cmcurl/lib/tftp.c +++ b/Utilities/cmcurl/lib/tftp.c @@ -70,8 +70,6 @@ /* RFC2348 allows the block size to be negotiated */ #define TFTP_BLKSIZE_DEFAULT 512 -#define TFTP_BLKSIZE_MIN 8 -#define TFTP_BLKSIZE_MAX 65464 #define TFTP_OPTION_BLKSIZE "blksize" /* from RFC2349: */ @@ -978,11 +976,9 @@ static CURLcode tftp_connect(struct Curl_easy *data, bool *done) return CURLE_OUT_OF_MEMORY; /* alloc pkt buffers based on specified blksize */ - if(data->set.tftp_blksize) { + if(data->set.tftp_blksize) + /* range checked when set */ blksize = (int)data->set.tftp_blksize; - if(blksize > TFTP_BLKSIZE_MAX || blksize < TFTP_BLKSIZE_MIN) - return CURLE_TFTP_ILLEGAL; - } need_blksize = blksize; /* default size is the fallback when no OACK is received */ @@ -1107,7 +1103,6 @@ static CURLcode tftp_receive_packet(struct Curl_easy *data) CURLcode result = CURLE_OK; struct connectdata *conn = data->conn; struct tftp_state_data *state = conn->proto.tftpc; - struct SingleRequest *k = &data->req; /* Receive the packet */ fromlen = sizeof(fromaddr); @@ -1141,11 +1136,6 @@ static CURLcode tftp_receive_packet(struct Curl_easy *data) result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)state->rpacket.data + 4, state->rbytes-4); - if(!result) { - k->bytecount += state->rbytes-4; - result = Curl_pgrsSetDownloadCounter(data, - (curl_off_t) k->bytecount); - } if(result) { tftp_state_machine(state, TFTP_EVENT_ERROR); return result; diff --git a/Utilities/cmcurl/lib/tftp.h b/Utilities/cmcurl/lib/tftp.h index 5d2d5da..12404bf 100644 --- a/Utilities/cmcurl/lib/tftp.h +++ b/Utilities/cmcurl/lib/tftp.h @@ -25,6 +25,9 @@ ***************************************************************************/ #ifndef CURL_DISABLE_TFTP extern const struct Curl_handler Curl_handler_tftp; + +#define TFTP_BLKSIZE_MIN 8 +#define TFTP_BLKSIZE_MAX 65464 #endif #endif /* HEADER_CURL_TFTP_H */ diff --git a/Utilities/cmcurl/lib/timediff.c b/Utilities/cmcurl/lib/timediff.c index 1b762bb..d0824d1 100644 --- a/Utilities/cmcurl/lib/timediff.c +++ b/Utilities/cmcurl/lib/timediff.c @@ -53,7 +53,7 @@ struct timeval *curlx_mstotv(struct timeval *tv, timediff_t ms) #endif tv->tv_sec = (time_t)tv_sec; tv->tv_usec = (suseconds_t)tv_usec; -#elif defined(WIN32) /* maybe also others in the future */ +#elif defined(_WIN32) /* maybe also others in the future */ #if TIMEDIFF_T_MAX > LONG_MAX /* tv_sec overflow check on Windows there we know it is long */ if(tv_sec > LONG_MAX) diff --git a/Utilities/cmcurl/lib/timeval.c b/Utilities/cmcurl/lib/timeval.c index 026d9d1..5a6727c 100644 --- a/Utilities/cmcurl/lib/timeval.c +++ b/Utilities/cmcurl/lib/timeval.c @@ -24,11 +24,10 @@ #include "timeval.h" -#if defined(WIN32) && !defined(MSDOS) +#if defined(_WIN32) -/* set in win32_init() */ -extern LARGE_INTEGER Curl_freq; -extern bool Curl_isVistaOrGreater; +#include <curl/curl.h> +#include "system_win32.h" /* In case of bug fix this function has a counterpart in tool_util.c */ struct curltime Curl_now(void) diff --git a/Utilities/cmcurl/lib/transfer.c b/Utilities/cmcurl/lib/transfer.c index 6886764..96f1fde 100644 --- a/Utilities/cmcurl/lib/transfer.c +++ b/Utilities/cmcurl/lib/transfer.c @@ -163,9 +163,9 @@ CURLcode Curl_fillreadbuffer(struct Curl_easy *data, size_t bytes, { size_t buffersize = bytes; size_t nread; - curl_read_callback readfunc = NULL; void *extra_data = NULL; + int eof_index = 0; #ifndef CURL_DISABLE_HTTP if(data->state.trailers_state == TRAILERS_INITIALIZED) { @@ -223,6 +223,7 @@ CURLcode Curl_fillreadbuffer(struct Curl_easy *data, size_t bytes, */ readfunc = trailers_read; extra_data = (void *)data; + eof_index = 1; } else #endif @@ -231,10 +232,15 @@ CURLcode Curl_fillreadbuffer(struct Curl_easy *data, size_t bytes, extra_data = data->state.in; } - Curl_set_in_callback(data, true); - nread = readfunc(data->req.upload_fromhere, 1, - buffersize, extra_data); - Curl_set_in_callback(data, false); + if(!data->req.fread_eof[eof_index]) { + Curl_set_in_callback(data, true); + nread = readfunc(data->req.upload_fromhere, 1, buffersize, extra_data); + Curl_set_in_callback(data, false); + /* make sure the callback is not called again after EOF */ + data->req.fread_eof[eof_index] = !nread; + } + else + nread = 0; if(nread == CURL_READFUNC_ABORT) { failf(data, "operation aborted by callback"); @@ -422,16 +428,15 @@ static CURLcode readwrite_data(struct Curl_easy *data, bool *comeback) { CURLcode result = CURLE_OK; - ssize_t nread; /* number of bytes read */ - size_t excess = 0; /* excess bytes read */ - bool readmore = FALSE; /* used by RTP to signal for more data */ + char *buf; + size_t blen; + size_t consumed; int maxloops = 100; curl_off_t max_recv = data->set.max_recv_speed? data->set.max_recv_speed : CURL_OFF_T_MAX; - char *buf = data->state.buffer; bool data_eof_handled = FALSE; - DEBUGASSERT(buf); + DEBUGASSERT(data->state.buffer); *done = FALSE; *comeback = FALSE; @@ -439,8 +444,7 @@ static CURLcode readwrite_data(struct Curl_easy *data, read or we get a CURLE_AGAIN */ do { bool is_empty_data = FALSE; - size_t buffersize = data->set.buffer_size; - size_t bytestoread = buffersize; + size_t bytestoread = data->set.buffer_size; /* For HTTP/2 and HTTP/3, read data without caring about the content length. This is safe because body in HTTP/2 is always segmented thanks to its framing layer. Meanwhile, we have to call Curl_read @@ -449,31 +453,38 @@ static CURLcode readwrite_data(struct Curl_easy *data, bool is_http3 = Curl_conn_is_http3(data, conn, FIRSTSOCKET); data_eof_handled = is_http3 || Curl_conn_is_http2(data, conn, FIRSTSOCKET); - if(!data_eof_handled && k->size != -1 && !k->header) { - /* make sure we don't read too much */ + /* Each loop iteration starts with a fresh buffer and handles + * all data read into it. */ + buf = data->state.buffer; + blen = 0; + + /* If we are reading BODY data and the connection does NOT handle EOF + * and we know the size of the BODY data, limit the read amount */ + if(!k->header && !data_eof_handled && k->size != -1) { curl_off_t totalleft = k->size - k->bytecount; - if(totalleft < (curl_off_t)bytestoread) + if(totalleft <= 0) + bytestoread = 0; + else if(totalleft < (curl_off_t)bytestoread) bytestoread = (size_t)totalleft; } if(bytestoread) { /* receive data from the network! */ + ssize_t nread; /* number of bytes read */ result = Curl_read(data, conn->sockfd, buf, bytestoread, &nread); - - /* read would've blocked */ if(CURLE_AGAIN == result) { result = CURLE_OK; break; /* get out of loop */ } - - if(result>0) + else if(result) goto out; + DEBUGASSERT(nread >= 0); + blen = (size_t)nread; } else { /* read nothing but since we wanted nothing we consider this an OK situation to proceed from */ DEBUGF(infof(data, "readwrite_data: we're done")); - nread = 0; } if(!k->bytecount) { @@ -485,12 +496,17 @@ static CURLcode readwrite_data(struct Curl_easy *data, *didwhat |= KEEP_RECV; /* indicates data of zero size, i.e. empty file */ - is_empty_data = ((nread == 0) && (k->bodywrites == 0)) ? TRUE : FALSE; - - if(0 < nread || is_empty_data) { - buf[nread] = 0; + is_empty_data = ((blen == 0) && (k->bodywrites == 0)) ? TRUE : FALSE; + + if(0 < blen || is_empty_data) { + /* data->state.buffer is allocated 1 byte larger than + * data->set.buffer_size admits. *wink* */ + /* TODO: we should really not rely on this being 0-terminated, since + * the actual data read might contain 0s. */ + buf[blen] = 0; } - if(!nread) { + + if(!blen) { /* if we receive 0 or less here, either the data transfer is done or the server closed the connection and we bail out from this! */ if(data_eof_handled) @@ -502,48 +518,70 @@ static CURLcode readwrite_data(struct Curl_easy *data, break; } - /* Default buffer to use when we write the buffer, it may be changed - in the flow below before the actual storing is done. */ - k->str = buf; - if(conn->handler->readwrite) { - result = conn->handler->readwrite(data, conn, &nread, &readmore); + bool readmore = FALSE; /* indicates data is incomplete, need more */ + consumed = 0; + result = conn->handler->readwrite(data, conn, buf, blen, + &consumed, &readmore); if(result) goto out; if(readmore) break; + buf += consumed; + blen -= consumed; + if(k->download_done) { + /* We've stopped dealing with input, get out of the do-while loop */ + if(blen > 0) { + infof(data, + "Excess found:" + " excess = %zu" + " url = %s (zero-length body)", + blen, data->state.up.path); + } + + /* we make sure that this socket isn't read more now */ + k->keepon &= ~KEEP_RECV; + break; + } } #ifndef CURL_DISABLE_HTTP /* Since this is a two-state thing, we check if we are parsing headers at the moment or not. */ if(k->header) { - /* we are in parse-the-header-mode */ - bool stop_reading = FALSE; - result = Curl_http_readwrite_headers(data, conn, &nread, &stop_reading); + consumed = 0; + result = Curl_http_readwrite_headers(data, conn, buf, blen, &consumed); if(result) goto out; + buf += consumed; + blen -= consumed; if(conn->handler->readwrite && - (k->maxdownload <= 0 && nread > 0)) { - result = conn->handler->readwrite(data, conn, &nread, &readmore); + (k->maxdownload <= 0 && blen > 0)) { + bool readmore = FALSE; /* indicates data is incomplete, need more */ + consumed = 0; + result = conn->handler->readwrite(data, conn, buf, blen, + &consumed, &readmore); if(result) goto out; if(readmore) break; + buf += consumed; + blen -= consumed; } - if(stop_reading) { + if(k->download_done) { /* We've stopped dealing with input, get out of the do-while loop */ - - if(nread > 0) { + if(blen > 0) { infof(data, "Excess found:" - " excess = %zd" + " excess = %zu" " url = %s (zero-length body)", - nread, data->state.up.path); + blen, data->state.up.path); } + /* we make sure that this socket isn't read more now */ + k->keepon &= ~KEEP_RECV; break; } } @@ -553,11 +591,13 @@ static CURLcode readwrite_data(struct Curl_easy *data, /* This is not an 'else if' since it may be a rest from the header parsing, where the beginning of the buffer is headers and the end is non-headers. */ - if(!k->header && (nread > 0 || is_empty_data)) { + if(!k->header && (blen > 0 || is_empty_data)) { - if(data->req.no_body) { + if(data->req.no_body && blen > 0) { /* data arrives although we want none, bail out */ streamclose(conn, "ignoring body"); + DEBUGF(infof(data, "did not want a BODY, but seeing %zu bytes", + blen)); *done = TRUE; result = CURLE_WEIRD_SERVER_REPLY; goto out; @@ -576,34 +616,18 @@ static CURLcode readwrite_data(struct Curl_easy *data, } /* this is the first time we write a body part */ #endif /* CURL_DISABLE_HTTP */ - k->bodywrites++; - - /* pass data to the debug function before it gets "dechunked" */ - if(data->set.verbose) { - if(k->badheader) { - Curl_debug(data, CURLINFO_DATA_IN, - Curl_dyn_ptr(&data->state.headerb), - Curl_dyn_len(&data->state.headerb)); - if(k->badheader == HEADER_PARTHEADER) - Curl_debug(data, CURLINFO_DATA_IN, - k->str, (size_t)nread); - } - else - Curl_debug(data, CURLINFO_DATA_IN, - k->str, (size_t)nread); - } - #ifndef CURL_DISABLE_HTTP if(k->chunk) { /* * Here comes a chunked transfer flying and we need to decode this * properly. While the name says read, this function both reads - * and writes away the data. The returned 'nread' holds the number - * of actual data it wrote to the client. + * and writes away the data. */ CURLcode extra; - CHUNKcode res = - Curl_httpchunk_read(data, k->str, nread, &nread, &extra); + CHUNKcode res; + + consumed = 0; + res = Curl_httpchunk_read(data, buf, blen, &consumed, &extra); if(CHUNKE_OK < res) { if(CHUNKE_PASSTHRU_ERROR == res) { @@ -615,9 +639,14 @@ static CURLcode readwrite_data(struct Curl_easy *data, result = CURLE_RECV_ERROR; goto out; } - if(CHUNKE_STOP == res) { + + buf += consumed; + blen -= consumed; + if(CHUNKE_STOP == res) { /* we're done reading chunks! */ k->keepon &= ~KEEP_RECV; /* read no more */ + /* chunks read successfully, download is complete */ + k->download_done = TRUE; /* N number of bytes at the end of the str buffer that weren't written to the client. */ @@ -631,117 +660,57 @@ static CURLcode readwrite_data(struct Curl_easy *data, } #endif /* CURL_DISABLE_HTTP */ - /* Account for body content stored in the header buffer */ - if((k->badheader == HEADER_PARTHEADER) && !k->ignorebody) { - size_t headlen = Curl_dyn_len(&data->state.headerb); - DEBUGF(infof(data, "Increasing bytecount by %zu", headlen)); - k->bytecount += headlen; - } - - if((-1 != k->maxdownload) && - (k->bytecount + nread >= k->maxdownload)) { + max_recv -= blen; - excess = (size_t)(k->bytecount + nread - k->maxdownload); - if(excess > 0 && !k->ignorebody) { - infof(data, - "Excess found in a read:" - " excess = %zu" - ", size = %" CURL_FORMAT_CURL_OFF_T - ", maxdownload = %" CURL_FORMAT_CURL_OFF_T - ", bytecount = %" CURL_FORMAT_CURL_OFF_T, - excess, k->size, k->maxdownload, k->bytecount); - connclose(conn, "excess found in a read"); - } - - nread = (ssize_t) (k->maxdownload - k->bytecount); - if(nread < 0) /* this should be unusual */ - nread = 0; - - /* HTTP/3 over QUIC should keep reading until QUIC connection - is closed. In contrast to HTTP/2 which can stop reading - from TCP connection, HTTP/3 over QUIC needs ACK from server - to ensure stream closure. It should keep reading. */ - if(!is_http3) { - k->keepon &= ~KEEP_RECV; /* we're done reading */ - } - } - - k->bytecount += nread; - max_recv -= nread; - - result = Curl_pgrsSetDownloadCounter(data, k->bytecount); - if(result) - goto out; - - if(!k->chunk && (nread || k->badheader || is_empty_data)) { + if(!k->chunk && (blen || k->badheader || is_empty_data)) { /* If this is chunky transfer, it was already written */ - if(k->badheader && !k->ignorebody) { + if(k->badheader) { /* we parsed a piece of data wrongly assuming it was a header and now we output it as body instead */ size_t headlen = Curl_dyn_len(&data->state.headerb); /* Don't let excess data pollute body writes */ - if(k->maxdownload == -1 || (curl_off_t)headlen <= k->maxdownload) - result = Curl_client_write(data, CLIENTWRITE_BODY, - Curl_dyn_ptr(&data->state.headerb), - headlen); - else - result = Curl_client_write(data, CLIENTWRITE_BODY, - Curl_dyn_ptr(&data->state.headerb), - (size_t)k->maxdownload); + if(k->maxdownload != -1 && (curl_off_t)headlen > k->maxdownload) + headlen = (size_t)k->maxdownload; + result = Curl_client_write(data, CLIENTWRITE_BODY, + Curl_dyn_ptr(&data->state.headerb), + headlen); if(result) goto out; } - if(k->badheader < HEADER_ALLBAD) { - /* This switch handles various content encodings. If there's an - error here, be sure to check over the almost identical code - in http_chunks.c. - Make sure that ALL_CONTENT_ENCODINGS contains all the - encodings handled here. */ - if(!k->ignorebody && nread) { + + if(blen) { #ifndef CURL_DISABLE_POP3 - if(conn->handler->protocol & PROTO_FAMILY_POP3) - result = Curl_pop3_write(data, k->str, nread); - else -#endif /* CURL_DISABLE_POP3 */ - result = Curl_client_write(data, CLIENTWRITE_BODY, k->str, - nread); + if(conn->handler->protocol & PROTO_FAMILY_POP3) { + result = k->ignorebody? CURLE_OK : + Curl_pop3_write(data, buf, blen); } + else +#endif /* CURL_DISABLE_POP3 */ + result = Curl_client_write(data, CLIENTWRITE_BODY, buf, blen); } - k->badheader = HEADER_NORMAL; /* taken care of now */ + k->badheader = FALSE; /* taken care of now */ if(result) goto out; } - } /* if(!header and data to read) */ - - if(conn->handler->readwrite && excess) { - /* Parse the excess data */ - k->str += nread; - - if(&k->str[excess] > &buf[data->set.buffer_size]) { - /* the excess amount was too excessive(!), make sure - it doesn't read out of buffer */ - excess = &buf[data->set.buffer_size] - k->str; + if(k->download_done && !is_http3) { + /* HTTP/3 over QUIC should keep reading until QUIC connection + is closed. In contrast to HTTP/2 which can stop reading + from TCP connection, HTTP/3 over QUIC needs ACK from server + to ensure stream closure. It should keep reading. */ + k->keepon &= ~KEEP_RECV; /* we're done reading */ } - nread = (ssize_t)excess; - - result = conn->handler->readwrite(data, conn, &nread, &readmore); - if(result) - goto out; - - if(readmore) - k->keepon |= KEEP_RECV; /* we're not done reading */ - break; - } + } /* if(!header and data to read) */ if(is_empty_data) { /* if we received nothing, the server closed the connection and we are done */ k->keepon &= ~KEEP_RECV; + k->download_done = TRUE; } if((k->keepon & KEEP_RECV_PAUSE) || !(k->keepon & KEEP_RECV)) { @@ -764,6 +733,7 @@ static CURLcode readwrite_data(struct Curl_easy *data, on from our side, we need to stop that immediately. */ infof(data, "we are done reading and this is set to close, stop send"); k->keepon &= ~KEEP_SEND; /* no writing anymore either */ + k->keepon &= ~KEEP_SEND_PAUSE; /* no pausing anymore either */ } out: @@ -783,7 +753,7 @@ CURLcode Curl_done_sending(struct Curl_easy *data, return CURLE_OK; } -#if defined(WIN32) && defined(USE_WINSOCK) +#if defined(_WIN32) && defined(USE_WINSOCK) #ifndef SIO_IDEAL_SEND_BACKLOG_QUERY #define SIO_IDEAL_SEND_BACKLOG_QUERY 0x4004747B #endif @@ -977,7 +947,7 @@ static CURLcode readwrite_upload(struct Curl_easy *data, if(result) return result; -#if defined(WIN32) && defined(USE_WINSOCK) +#if defined(_WIN32) && defined(USE_WINSOCK) { struct curltime n = Curl_now(); if(Curl_timediff(n, k->last_sndbuf_update) > 1000) { @@ -1430,8 +1400,7 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) return CURLE_OUT_OF_MEMORY; } wc = data->wildcard; - if((wc->state < CURLWC_INIT) || - (wc->state >= CURLWC_CLEAN)) { + if(wc->state < CURLWC_INIT) { if(wc->ftpwc) wc->dtor(wc->ftpwc); Curl_safefree(wc->pattern); @@ -1635,7 +1604,7 @@ CURLcode Curl_follow(struct Curl_easy *data, return Curl_uc_to_curlcode(uc); } - p = Curl_builtin_scheme(scheme, CURL_ZERO_TERMINATED); + p = Curl_get_scheme_handler(scheme); if(p && (p->protocol != data->info.conn_protocol)) { infof(data, "Clear auth, redirects scheme from %s to %s", data->info.conn_scheme, scheme); diff --git a/Utilities/cmcurl/lib/url.c b/Utilities/cmcurl/lib/url.c index 61dad44..b81785f 100644 --- a/Utilities/cmcurl/lib/url.c +++ b/Utilities/cmcurl/lib/url.c @@ -168,130 +168,6 @@ static curl_prot_t get_protocol_family(const struct Curl_handler *h) return h->family; } - -/* - * Protocol table. Schemes (roughly) in 2019 popularity order: - * - * HTTPS, HTTP, FTP, FTPS, SFTP, FILE, SCP, SMTP, LDAP, IMAPS, TELNET, IMAP, - * LDAPS, SMTPS, TFTP, SMB, POP3, GOPHER POP3S, RTSP, RTMP, SMBS, DICT - */ -static const struct Curl_handler * const protocols[] = { - -#if defined(USE_SSL) && !defined(CURL_DISABLE_HTTP) - &Curl_handler_https, -#endif - -#ifndef CURL_DISABLE_HTTP - &Curl_handler_http, -#endif - -#ifdef USE_WEBSOCKETS -#if defined(USE_SSL) && !defined(CURL_DISABLE_HTTP) - &Curl_handler_wss, -#endif - -#ifndef CURL_DISABLE_HTTP - &Curl_handler_ws, -#endif -#endif - -#ifndef CURL_DISABLE_FTP - &Curl_handler_ftp, -#endif - -#if defined(USE_SSL) && !defined(CURL_DISABLE_FTP) - &Curl_handler_ftps, -#endif - -#if defined(USE_SSH) - &Curl_handler_sftp, -#endif - -#ifndef CURL_DISABLE_FILE - &Curl_handler_file, -#endif - -#if defined(USE_SSH) && !defined(USE_WOLFSSH) - &Curl_handler_scp, -#endif - -#ifndef CURL_DISABLE_SMTP - &Curl_handler_smtp, -#ifdef USE_SSL - &Curl_handler_smtps, -#endif -#endif - -#ifndef CURL_DISABLE_LDAP - &Curl_handler_ldap, -#if !defined(CURL_DISABLE_LDAPS) && \ - ((defined(USE_OPENLDAP) && defined(USE_SSL)) || \ - (!defined(USE_OPENLDAP) && defined(HAVE_LDAP_SSL))) - &Curl_handler_ldaps, -#endif -#endif - -#ifndef CURL_DISABLE_IMAP - &Curl_handler_imap, -#ifdef USE_SSL - &Curl_handler_imaps, -#endif -#endif - -#ifndef CURL_DISABLE_TELNET - &Curl_handler_telnet, -#endif - -#ifndef CURL_DISABLE_TFTP - &Curl_handler_tftp, -#endif - -#ifndef CURL_DISABLE_POP3 - &Curl_handler_pop3, -#ifdef USE_SSL - &Curl_handler_pop3s, -#endif -#endif - -#if !defined(CURL_DISABLE_SMB) && defined(USE_CURL_NTLM_CORE) && \ - (SIZEOF_CURL_OFF_T > 4) - &Curl_handler_smb, -#ifdef USE_SSL - &Curl_handler_smbs, -#endif -#endif - -#ifndef CURL_DISABLE_RTSP - &Curl_handler_rtsp, -#endif - -#ifndef CURL_DISABLE_MQTT - &Curl_handler_mqtt, -#endif - -#ifndef CURL_DISABLE_GOPHER - &Curl_handler_gopher, -#ifdef USE_SSL - &Curl_handler_gophers, -#endif -#endif - -#ifdef USE_LIBRTMP - &Curl_handler_rtmp, - &Curl_handler_rtmpt, - &Curl_handler_rtmpe, - &Curl_handler_rtmpte, - &Curl_handler_rtmps, - &Curl_handler_rtmpts, -#endif - -#ifndef CURL_DISABLE_DICT - &Curl_handler_dict, -#endif - - (struct Curl_handler *) NULL -}; - void Curl_freeset(struct Curl_easy *data) { /* Free all dynamic strings stored in the data->set substructure. */ @@ -320,8 +196,8 @@ void Curl_freeset(struct Curl_easy *data) Curl_mime_cleanpart(&data->set.mimepost); #ifndef CURL_DISABLE_COOKIES - curl_slist_free_all(data->set.cookielist); - data->set.cookielist = NULL; + curl_slist_free_all(data->state.cookielist); + data->state.cookielist = NULL; #endif } @@ -363,16 +239,18 @@ CURLcode Curl_close(struct Curl_easy **datap) /* Detach connection if any is left. This should not be normal, but can be the case for example with CONNECT_ONLY + recv/send (test 556) */ Curl_detach_connection(data); - if(data->multi) - /* This handle is still part of a multi handle, take care of this first - and detach this handle from there. */ - curl_multi_remove_handle(data->multi, data); + if(!data->state.internal) { + if(data->multi) + /* This handle is still part of a multi handle, take care of this first + and detach this handle from there. */ + curl_multi_remove_handle(data->multi, data); - if(data->multi_easy) { - /* when curl_easy_perform() is used, it creates its own multi handle to - use and this is the one */ - curl_multi_cleanup(data->multi_easy); - data->multi_easy = NULL; + if(data->multi_easy) { + /* when curl_easy_perform() is used, it creates its own multi handle to + use and this is the one */ + curl_multi_cleanup(data->multi_easy); + data->multi_easy = NULL; + } } data->magic = 0; /* force a clear AFTER the possibly enforced removal from @@ -412,7 +290,7 @@ CURLcode Curl_close(struct Curl_easy **datap) #ifndef CURL_DISABLE_HSTS if(!data->share || !data->share->hsts) Curl_hsts_cleanup(&data->hsts); - curl_slist_free_all(data->set.hstslist); /* clean up list */ + curl_slist_free_all(data->state.hstslist); /* clean up list */ #endif #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_DIGEST_AUTH) Curl_http_auth_cleanup_digest(data); @@ -420,10 +298,6 @@ CURLcode Curl_close(struct Curl_easy **datap) Curl_safefree(data->info.contenttype); Curl_safefree(data->info.wouldredirect); - /* this destroys the channel and we cannot use it anymore after this */ - Curl_resolver_cancel(data); - Curl_resolver_cleanup(data->state.async.resolver); - data_priority_cleanup(data); /* No longer a dirty share, if it exists */ @@ -457,8 +331,8 @@ CURLcode Curl_close(struct Curl_easy **datap) } #endif - Curl_mime_cleanpart(data->state.formp); #ifndef CURL_DISABLE_HTTP + Curl_mime_cleanpart(data->state.formp); Curl_safefree(data->state.formp); #endif @@ -530,26 +404,16 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data) Curl_mime_initpart(&set->mimepost); - /* - * libcurl 7.10 introduced SSL verification *by default*! This needs to be - * switched off unless wanted. - */ + Curl_ssl_easy_config_init(data); #ifndef CURL_DISABLE_DOH set->doh_verifyhost = TRUE; set->doh_verifypeer = TRUE; #endif - set->ssl.primary.verifypeer = TRUE; - set->ssl.primary.verifyhost = TRUE; #ifdef USE_SSH /* defaults to any auth type */ set->ssh_auth_types = CURLSSH_AUTH_DEFAULT; set->new_directory_perms = 0755; /* Default permissions */ #endif - set->ssl.primary.sessionid = TRUE; /* session ID caching enabled by - default */ -#ifndef CURL_DISABLE_PROXY - set->proxy_ssl = set->ssl; -#endif set->new_file_perms = 0644; /* Default permissions */ set->allowed_protocols = (curl_prot_t) CURLPROTO_ALL; @@ -650,13 +514,6 @@ CURLcode Curl_open(struct Curl_easy **curl) data->magic = CURLEASY_MAGIC_NUMBER; - result = Curl_resolver_init(data, &data->state.async.resolver); - if(result) { - DEBUGF(fprintf(stderr, "Error: resolver_init failed\n")); - free(data); - return result; - } - result = Curl_init_userdefined(data); if(!result) { Curl_dyn_init(&data->state.headerb, CURL_MAX_HTTP_HEADER); @@ -673,7 +530,6 @@ CURLcode Curl_open(struct Curl_easy **curl) } if(result) { - Curl_resolver_cleanup(data->state.async.resolver); Curl_dyn_free(&data->state.headerb); Curl_freeset(data); free(data); @@ -707,6 +563,7 @@ static void conn_free(struct Curl_easy *data, struct connectdata *conn) Curl_conn_cf_discard_all(data, conn, (int)i); } + Curl_resolver_cleanup(conn->resolve_async.resolver); Curl_free_idnconverted_hostname(&conn->host); Curl_free_idnconverted_hostname(&conn->conn_to_host); #ifndef CURL_DISABLE_PROXY @@ -718,7 +575,6 @@ static void conn_free(struct Curl_easy *data, struct connectdata *conn) Curl_safefree(conn->socks_proxy.passwd); Curl_safefree(conn->http_proxy.host.rawalloc); /* http proxy name buffer */ Curl_safefree(conn->socks_proxy.host.rawalloc); /* socks proxy name buffer */ - Curl_free_primary_ssl_config(&conn->proxy_ssl_config); #endif Curl_safefree(conn->user); Curl_safefree(conn->passwd); @@ -733,7 +589,7 @@ static void conn_free(struct Curl_easy *data, struct connectdata *conn) Curl_safefree(conn->hostname_resolve); Curl_safefree(conn->secondaryhostname); Curl_safefree(conn->localdev); - Curl_free_primary_ssl_config(&conn->ssl_config); + Curl_ssl_conn_config_cleanup(conn); #ifdef USE_UNIX_SOCKETS Curl_safefree(conn->unix_domain_socket); @@ -807,6 +663,7 @@ void Curl_disconnect(struct Curl_easy *data, conn->handler->disconnect(data, conn, dead_connection); conn_shutdown(data); + Curl_resolver_cancel(data); /* detach it again */ Curl_detach_connection(data); @@ -1059,11 +916,11 @@ ConnectionExists(struct Curl_easy *data, bool *force_reuse, bool *waitpipe) { - struct connectdata *check; - struct connectdata *chosen = 0; + struct connectdata *chosen = NULL; bool foundPendingCandidate = FALSE; - bool canmultiplex = IsMultiplexingPossible(data, needle); + bool canmultiplex = FALSE; struct connectbundle *bundle; + struct Curl_llist_element *curr; #ifdef USE_NTLM bool wantNTLMhttp = ((data->state.authhost.want & @@ -1082,395 +939,368 @@ ConnectionExists(struct Curl_easy *data, bool h2upgrade = (data->state.httpwant == CURL_HTTP_VERSION_2_0) && (needle->handler->protocol & CURLPROTO_HTTP); + *usethis = NULL; *force_reuse = FALSE; *waitpipe = FALSE; /* Look up the bundle with all the connections to this particular host. Locks the connection cache, beware of early returns! */ bundle = Curl_conncache_find_bundle(data, needle, data->state.conn_cache); - if(bundle) { - /* Max pipe length is zero (unlimited) for multiplexed connections */ - struct Curl_llist_element *curr; - - infof(data, "Found bundle for host: %p [%s]", - (void *)bundle, (bundle->multiuse == BUNDLE_MULTIPLEX ? - "can multiplex" : "serially")); - - /* We can't multiplex if we don't know anything about the server */ - if(canmultiplex) { - if(bundle->multiuse == BUNDLE_UNKNOWN) { - if(data->set.pipewait) { - infof(data, "Server doesn't support multiplex yet, wait"); - *waitpipe = TRUE; - CONNCACHE_UNLOCK(data); - return FALSE; /* no reuse */ - } - - infof(data, "Server doesn't support multiplex (yet)"); - canmultiplex = FALSE; + if(!bundle) { + CONNCACHE_UNLOCK(data); + return FALSE; + } + infof(data, "Found bundle for host: %p [%s]", + (void *)bundle, (bundle->multiuse == BUNDLE_MULTIPLEX ? + "can multiplex" : "serially")); + + /* We can only multiplex iff the transfer allows it AND we know + * that the server we want to talk to supports it as well. */ + canmultiplex = FALSE; + if(IsMultiplexingPossible(data, needle)) { + if(bundle->multiuse == BUNDLE_UNKNOWN) { + if(data->set.pipewait) { + infof(data, "Server doesn't support multiplex yet, wait"); + *waitpipe = TRUE; + CONNCACHE_UNLOCK(data); + return FALSE; /* no reuse */ } - if((bundle->multiuse == BUNDLE_MULTIPLEX) && - !Curl_multiplex_wanted(data->multi)) { + infof(data, "Server doesn't support multiplex (yet)"); + } + else if(bundle->multiuse == BUNDLE_MULTIPLEX) { + if(Curl_multiplex_wanted(data->multi)) + canmultiplex = TRUE; + else infof(data, "Could multiplex, but not asked to"); - canmultiplex = FALSE; - } - if(bundle->multiuse == BUNDLE_NO_MULTIUSE) { - infof(data, "Can not multiplex, even if we wanted to"); - canmultiplex = FALSE; - } } + else if(bundle->multiuse == BUNDLE_NO_MULTIUSE) { + infof(data, "Can not multiplex, even if we wanted to"); + } + } - curr = bundle->conn_list.head; - while(curr) { - bool match = FALSE; - size_t multiplexed = 0; + curr = bundle->conn_list.head; + while(curr) { + struct connectdata *check = curr->ptr; + /* Get next node now. We might remove a dead `check` connection which + * would invalidate `curr` as well. */ + curr = curr->next; - /* - * Note that if we use an HTTP proxy in normal mode (no tunneling), we - * check connections to that proxy and not to the actual remote server. - */ - check = curr->ptr; - curr = curr->next; + /* Note that if we use an HTTP proxy in normal mode (no tunneling), we + * check connections to that proxy and not to the actual remote server. + */ + if(check->connect_only || check->bits.close) + /* connect-only or to-be-closed connections will not be reused */ + continue; - if(check->connect_only || check->bits.close) - /* connect-only or to-be-closed connections will not be reused */ - continue; + if(data->set.ipver != CURL_IPRESOLVE_WHATEVER + && data->set.ipver != check->ip_version) { + /* skip because the connection is not via the requested IP version */ + continue; + } - if(extract_if_dead(check, data)) { - /* disconnect it */ - Curl_disconnect(data, check, TRUE); + if(!canmultiplex) { + if(Curl_resolver_asynch() && + /* primary_ip[0] is NUL only if the resolving of the name hasn't + completed yet and until then we don't reuse this connection */ + !check->primary_ip[0]) continue; - } + } - if(data->set.ipver != CURL_IPRESOLVE_WHATEVER - && data->set.ipver != check->ip_version) { - /* skip because the connection is not via the requested IP version */ + if(CONN_INUSE(check)) { + if(!canmultiplex) { + /* transfer can't be multiplexed and check is in use */ continue; } - - if(bundle->multiuse == BUNDLE_MULTIPLEX) - multiplexed = CONN_INUSE(check); - - if(!canmultiplex) { - if(multiplexed) { - /* can only happen within multi handles, and means that another easy - handle is using this connection */ - continue; - } - - if(Curl_resolver_asynch() && - /* primary_ip[0] is NUL only if the resolving of the name hasn't - completed yet and until then we don't reuse this connection */ - !check->primary_ip[0]) + else { + /* Could multiplex, but not when check belongs to another multi */ + struct Curl_llist_element *e = check->easyq.head; + struct Curl_easy *entry = e->ptr; + if(entry->multi != data->multi) continue; } + } - if(!Curl_conn_is_connected(check, FIRSTSOCKET)) { - foundPendingCandidate = TRUE; - /* Don't pick a connection that hasn't connected yet */ - infof(data, "Connection #%" CURL_FORMAT_CURL_OFF_T - " isn't open enough, can't reuse", check->connection_id); - continue; - } + if(!Curl_conn_is_connected(check, FIRSTSOCKET)) { + foundPendingCandidate = TRUE; + /* Don't pick a connection that hasn't connected yet */ + infof(data, "Connection #%" CURL_FORMAT_CURL_OFF_T + " isn't open enough, can't reuse", check->connection_id); + continue; + } + + /* `check` is connected. if it is in use and does not support multiplex, + * we cannot use it. */ + if(!check->bits.multiplex && CONN_INUSE(check)) + continue; #ifdef USE_UNIX_SOCKETS - if(needle->unix_domain_socket) { - if(!check->unix_domain_socket) - continue; - if(strcmp(needle->unix_domain_socket, check->unix_domain_socket)) - continue; - if(needle->bits.abstract_unix_socket != - check->bits.abstract_unix_socket) - continue; - } - else if(check->unix_domain_socket) + if(needle->unix_domain_socket) { + if(!check->unix_domain_socket) continue; -#endif - - if((needle->handler->flags&PROTOPT_SSL) != - (check->handler->flags&PROTOPT_SSL)) - /* don't do mixed SSL and non-SSL connections */ - if(get_protocol_family(check->handler) != - needle->handler->protocol || !check->bits.tls_upgraded) - /* except protocols that have been upgraded via TLS */ - continue; - -#ifndef CURL_DISABLE_PROXY - if(needle->bits.httpproxy != check->bits.httpproxy || - needle->bits.socksproxy != check->bits.socksproxy) + if(strcmp(needle->unix_domain_socket, check->unix_domain_socket)) continue; - - if(needle->bits.socksproxy && - !socks_proxy_info_matches(&needle->socks_proxy, - &check->socks_proxy)) + if(needle->bits.abstract_unix_socket != + check->bits.abstract_unix_socket) continue; + } + else if(check->unix_domain_socket) + continue; #endif - if(needle->bits.conn_to_host != check->bits.conn_to_host) - /* don't mix connections that use the "connect to host" feature and - * connections that don't use this feature */ - continue; - if(needle->bits.conn_to_port != check->bits.conn_to_port) - /* don't mix connections that use the "connect to port" feature and - * connections that don't use this feature */ + if((needle->handler->flags&PROTOPT_SSL) != + (check->handler->flags&PROTOPT_SSL)) + /* don't do mixed SSL and non-SSL connections */ + if(get_protocol_family(check->handler) != + needle->handler->protocol || !check->bits.tls_upgraded) + /* except protocols that have been upgraded via TLS */ continue; -#ifndef CURL_DISABLE_PROXY - if(needle->bits.httpproxy) { - if(!proxy_info_matches(&needle->http_proxy, &check->http_proxy)) - continue; + if(needle->bits.conn_to_host != check->bits.conn_to_host) + /* don't mix connections that use the "connect to host" feature and + * connections that don't use this feature */ + continue; - if(needle->bits.tunnel_proxy != check->bits.tunnel_proxy) - continue; + if(needle->bits.conn_to_port != check->bits.conn_to_port) + /* don't mix connections that use the "connect to port" feature and + * connections that don't use this feature */ + continue; - if(IS_HTTPS_PROXY(needle->http_proxy.proxytype)) { - /* use https proxy */ - if(needle->http_proxy.proxytype != - check->http_proxy.proxytype) - continue; - else if(needle->handler->flags&PROTOPT_SSL) { - /* use double layer ssl */ - if(!Curl_ssl_config_matches(&needle->proxy_ssl_config, - &check->proxy_ssl_config)) - continue; - } - else if(!Curl_ssl_config_matches(&needle->ssl_config, - &check->ssl_config)) - continue; - } - } -#endif +#ifndef CURL_DISABLE_PROXY + if(needle->bits.httpproxy != check->bits.httpproxy || + needle->bits.socksproxy != check->bits.socksproxy) + continue; - if(h2upgrade && !check->httpversion && canmultiplex) { - if(data->set.pipewait) { - infof(data, "Server upgrade doesn't support multiplex yet, wait"); - *waitpipe = TRUE; - CONNCACHE_UNLOCK(data); - return FALSE; /* no reuse */ - } - infof(data, "Server upgrade cannot be used"); - continue; /* can't be used atm */ - } + if(needle->bits.socksproxy && + !socks_proxy_info_matches(&needle->socks_proxy, + &check->socks_proxy)) + continue; - if(!canmultiplex && CONN_INUSE(check)) - /* this request can't be multiplexed but the checked connection is - already in use so we skip it */ + if(needle->bits.httpproxy) { + if(needle->bits.tunnel_proxy != check->bits.tunnel_proxy) continue; - if(CONN_INUSE(check)) { - /* Subject for multiplex use if 'checks' belongs to the same multi - handle as 'data' is. */ - struct Curl_llist_element *e = check->easyq.head; - struct Curl_easy *entry = e->ptr; - if(entry->multi != data->multi) - continue; - } + if(!proxy_info_matches(&needle->http_proxy, &check->http_proxy)) + continue; - if(needle->localdev || needle->localport) { - /* If we are bound to a specific local end (IP+port), we must not - reuse a random other one, although if we didn't ask for a - particular one we can reuse one that was bound. - - This comparison is a bit rough and too strict. Since the input - parameters can be specified in numerous ways and still end up the - same it would take a lot of processing to make it really accurate. - Instead, this matching will assume that reuses of bound connections - will most likely also reuse the exact same binding parameters and - missing out a few edge cases shouldn't hurt anyone very much. - */ - if((check->localport != needle->localport) || - (check->localportrange != needle->localportrange) || - (needle->localdev && - (!check->localdev || strcmp(check->localdev, needle->localdev)))) + if(IS_HTTPS_PROXY(needle->http_proxy.proxytype)) { + /* https proxies come in different types, http/1.1, h2, ... */ + if(needle->http_proxy.proxytype != check->http_proxy.proxytype) continue; - } - - if(!(needle->handler->flags & PROTOPT_CREDSPERREQUEST)) { - /* This protocol requires credentials per connection, - so verify that we're using the same name and password as well */ - if(Curl_timestrcmp(needle->user, check->user) || - Curl_timestrcmp(needle->passwd, check->passwd) || - Curl_timestrcmp(needle->sasl_authzid, check->sasl_authzid) || - Curl_timestrcmp(needle->oauth_bearer, check->oauth_bearer)) { - /* one of them was different */ + /* match SSL config to proxy */ + if(!Curl_ssl_conn_config_match(data, check, TRUE)) { + DEBUGF(infof(data, + "Connection #%" CURL_FORMAT_CURL_OFF_T + " has different SSL proxy parameters, can't reuse", + check->connection_id)); continue; } + /* the SSL config to the server, which may apply here is checked + * further below */ } + } +#endif - /* GSS delegation differences do not actually affect every connection - and auth method, but this check takes precaution before efficiency */ - if(needle->gssapi_delegation != check->gssapi_delegation) + if(h2upgrade && !check->httpversion && canmultiplex) { + if(data->set.pipewait) { + infof(data, "Server upgrade doesn't support multiplex yet, wait"); + *waitpipe = TRUE; + CONNCACHE_UNLOCK(data); + return FALSE; /* no reuse */ + } + infof(data, "Server upgrade cannot be used"); + continue; /* can't be used atm */ + } + + if(needle->localdev || needle->localport) { + /* If we are bound to a specific local end (IP+port), we must not + reuse a random other one, although if we didn't ask for a + particular one we can reuse one that was bound. + + This comparison is a bit rough and too strict. Since the input + parameters can be specified in numerous ways and still end up the + same it would take a lot of processing to make it really accurate. + Instead, this matching will assume that reuses of bound connections + will most likely also reuse the exact same binding parameters and + missing out a few edge cases shouldn't hurt anyone very much. + */ + if((check->localport != needle->localport) || + (check->localportrange != needle->localportrange) || + (needle->localdev && + (!check->localdev || strcmp(check->localdev, needle->localdev)))) continue; + } - /* If multiplexing isn't enabled on the h2 connection and h1 is - explicitly requested, handle it: */ - if((needle->handler->protocol & PROTO_FAMILY_HTTP) && - (((check->httpversion >= 20) && - (data->state.httpwant < CURL_HTTP_VERSION_2_0)) - || ((check->httpversion >= 30) && - (data->state.httpwant < CURL_HTTP_VERSION_3)))) + if(!(needle->handler->flags & PROTOPT_CREDSPERREQUEST)) { + /* This protocol requires credentials per connection, + so verify that we're using the same name and password as well */ + if(Curl_timestrcmp(needle->user, check->user) || + Curl_timestrcmp(needle->passwd, check->passwd) || + Curl_timestrcmp(needle->sasl_authzid, check->sasl_authzid) || + Curl_timestrcmp(needle->oauth_bearer, check->oauth_bearer)) { + /* one of them was different */ continue; -#ifdef USE_SSH - else if(get_protocol_family(needle->handler) & PROTO_FAMILY_SSH) { - if(!ssh_config_matches(needle, check)) - continue; } + } + + /* GSS delegation differences do not actually affect every connection + and auth method, but this check takes precaution before efficiency */ + if(needle->gssapi_delegation != check->gssapi_delegation) + continue; + + /* If looking for HTTP and the HTTP version we want is less + * than the HTTP version of the check connection, continue looking */ + if((needle->handler->protocol & PROTO_FAMILY_HTTP) && + (((check->httpversion >= 20) && + (data->state.httpwant < CURL_HTTP_VERSION_2_0)) + || ((check->httpversion >= 30) && + (data->state.httpwant < CURL_HTTP_VERSION_3)))) + continue; +#ifdef USE_SSH + else if(get_protocol_family(needle->handler) & PROTO_FAMILY_SSH) { + if(!ssh_config_matches(needle, check)) + continue; + } #endif #ifndef CURL_DISABLE_FTP - else if(get_protocol_family(needle->handler) & PROTO_FAMILY_FTP) { - /* Also match ACCOUNT, ALTERNATIVE-TO-USER, USE_SSL and CCC options */ - if(Curl_timestrcmp(needle->proto.ftpc.account, - check->proto.ftpc.account) || - Curl_timestrcmp(needle->proto.ftpc.alternative_to_user, - check->proto.ftpc.alternative_to_user) || - (needle->proto.ftpc.use_ssl != check->proto.ftpc.use_ssl) || - (needle->proto.ftpc.ccc != check->proto.ftpc.ccc)) - continue; - } + else if(get_protocol_family(needle->handler) & PROTO_FAMILY_FTP) { + /* Also match ACCOUNT, ALTERNATIVE-TO-USER, USE_SSL and CCC options */ + if(Curl_timestrcmp(needle->proto.ftpc.account, + check->proto.ftpc.account) || + Curl_timestrcmp(needle->proto.ftpc.alternative_to_user, + check->proto.ftpc.alternative_to_user) || + (needle->proto.ftpc.use_ssl != check->proto.ftpc.use_ssl) || + (needle->proto.ftpc.ccc != check->proto.ftpc.ccc)) + continue; + } #endif - if((needle->handler->flags&PROTOPT_SSL) + /* Additional match requirements if talking TLS OR + * not talking to a HTTP proxy OR using a tunnel through a proxy */ + if((needle->handler->flags&PROTOPT_SSL) #ifndef CURL_DISABLE_PROXY - || !needle->bits.httpproxy || needle->bits.tunnel_proxy -#endif - ) { - /* The requested connection does not use an HTTP proxy or it uses SSL - or it is a non-SSL protocol tunneled or it is a non-SSL protocol - which is allowed to be upgraded via TLS */ - - if((strcasecompare(needle->handler->scheme, check->handler->scheme) || - (get_protocol_family(check->handler) == - needle->handler->protocol && check->bits.tls_upgraded)) && - (!needle->bits.conn_to_host || strcasecompare( - needle->conn_to_host.name, check->conn_to_host.name)) && - (!needle->bits.conn_to_port || - needle->conn_to_port == check->conn_to_port) && - strcasecompare(needle->host.name, check->host.name) && - needle->remote_port == check->remote_port) { - /* The schemes match or the protocol family is the same and the - previous connection was TLS upgraded, and the hostname and host - port match */ - if(needle->handler->flags & PROTOPT_SSL) { - /* This is a SSL connection so verify that we're using the same - SSL options as well */ - if(!Curl_ssl_config_matches(&needle->ssl_config, - &check->ssl_config)) { - DEBUGF(infof(data, - "Connection #%" CURL_FORMAT_CURL_OFF_T - " has different SSL parameters, can't reuse", - check->connection_id)); - continue; - } - } - match = TRUE; - } - } - else { - /* The requested connection is using the same HTTP proxy in normal - mode (no tunneling) */ - match = TRUE; + || !needle->bits.httpproxy || needle->bits.tunnel_proxy +#endif + ) { + /* Talking the same protocol scheme or a TLS upgraded protocol in the + * same protocol family? */ + if(!strcasecompare(needle->handler->scheme, check->handler->scheme) && + (get_protocol_family(check->handler) != + needle->handler->protocol || !check->bits.tls_upgraded)) + continue; + + /* If needle has "conn_to_*" set, check must match this */ + if((needle->bits.conn_to_host && !strcasecompare( + needle->conn_to_host.name, check->conn_to_host.name)) || + (needle->bits.conn_to_port && + needle->conn_to_port != check->conn_to_port)) + continue; + + /* hostname and port must match */ + if(!strcasecompare(needle->host.name, check->host.name) || + needle->remote_port != check->remote_port) + continue; + + /* If talking TLS, check needs to use the same SSL options. */ + if((needle->handler->flags & PROTOPT_SSL) && + !Curl_ssl_conn_config_match(data, check, FALSE)) { + DEBUGF(infof(data, + "Connection #%" CURL_FORMAT_CURL_OFF_T + " has different SSL parameters, can't reuse", + check->connection_id)); + continue; } + } - if(match) { #if defined(USE_NTLM) - /* If we are looking for an HTTP+NTLM connection, check if this is - already authenticating with the right credentials. If not, keep - looking so that we can reuse NTLM connections if - possible. (Especially we must not reuse the same connection if - partway through a handshake!) */ - if(wantNTLMhttp) { - if(Curl_timestrcmp(needle->user, check->user) || - Curl_timestrcmp(needle->passwd, check->passwd)) { - - /* we prefer a credential match, but this is at least a connection - that can be reused and "upgraded" to NTLM */ - if(check->http_ntlm_state == NTLMSTATE_NONE) - chosen = check; - continue; - } - } - else if(check->http_ntlm_state != NTLMSTATE_NONE) { - /* Connection is using NTLM auth but we don't want NTLM */ - continue; - } - -#ifndef CURL_DISABLE_PROXY - /* Same for Proxy NTLM authentication */ - if(wantProxyNTLMhttp) { - /* Both check->http_proxy.user and check->http_proxy.passwd can be - * NULL */ - if(!check->http_proxy.user || !check->http_proxy.passwd) - continue; - - if(Curl_timestrcmp(needle->http_proxy.user, - check->http_proxy.user) || - Curl_timestrcmp(needle->http_proxy.passwd, - check->http_proxy.passwd)) - continue; - } - else if(check->proxy_ntlm_state != NTLMSTATE_NONE) { - /* Proxy connection is using NTLM auth but we don't want NTLM */ - continue; - } -#endif - if(wantNTLMhttp || wantProxyNTLMhttp) { - /* Credentials are already checked, we can use this connection */ + /* If we are looking for an HTTP+NTLM connection, check if this is + already authenticating with the right credentials. If not, keep + looking so that we can reuse NTLM connections if + possible. (Especially we must not reuse the same connection if + partway through a handshake!) */ + if(wantNTLMhttp) { + if(Curl_timestrcmp(needle->user, check->user) || + Curl_timestrcmp(needle->passwd, check->passwd)) { + + /* we prefer a credential match, but this is at least a connection + that can be reused and "upgraded" to NTLM */ + if(check->http_ntlm_state == NTLMSTATE_NONE) chosen = check; + continue; + } + } + else if(check->http_ntlm_state != NTLMSTATE_NONE) { + /* Connection is using NTLM auth but we don't want NTLM */ + continue; + } - if((wantNTLMhttp && - (check->http_ntlm_state != NTLMSTATE_NONE)) || - (wantProxyNTLMhttp && - (check->proxy_ntlm_state != NTLMSTATE_NONE))) { - /* We must use this connection, no other */ - *force_reuse = TRUE; - break; - } +#ifndef CURL_DISABLE_PROXY + /* Same for Proxy NTLM authentication */ + if(wantProxyNTLMhttp) { + /* Both check->http_proxy.user and check->http_proxy.passwd can be + * NULL */ + if(!check->http_proxy.user || !check->http_proxy.passwd) + continue; - /* Continue look up for a better connection */ - continue; - } + if(Curl_timestrcmp(needle->http_proxy.user, + check->http_proxy.user) || + Curl_timestrcmp(needle->http_proxy.passwd, + check->http_proxy.passwd)) + continue; + } + else if(check->proxy_ntlm_state != NTLMSTATE_NONE) { + /* Proxy connection is using NTLM auth but we don't want NTLM */ + continue; + } +#endif + if(wantNTLMhttp || wantProxyNTLMhttp) { + /* Credentials are already checked, we may use this connection. + * With NTLM being weird as it is, we MUST use a + * connection where it has already been fully negotiated. + * If it has not, we keep on looking for a better one. */ + chosen = check; + + if((wantNTLMhttp && + (check->http_ntlm_state != NTLMSTATE_NONE)) || + (wantProxyNTLMhttp && + (check->proxy_ntlm_state != NTLMSTATE_NONE))) { + /* We must use this connection, no other */ + *force_reuse = TRUE; + break; + } + /* Continue look up for a better connection */ + continue; + } #endif - if(canmultiplex) { - /* We can multiplex if we want to. Let's continue looking for - the optimal connection to use. */ - - if(!multiplexed) { - /* We have the optimal connection. Let's stop looking. */ - chosen = check; - break; - } -#ifdef USE_NGHTTP2 - /* If multiplexed, make sure we don't go over concurrency limit */ - if(check->bits.multiplex) { - if(multiplexed >= Curl_conn_get_max_concurrent(data, check, - FIRSTSOCKET)) { - infof(data, "MAX_CONCURRENT_STREAMS reached, skip (%zu)", - multiplexed); - continue; - } - else if(multiplexed >= - Curl_multi_max_concurrent_streams(data->multi)) { - infof(data, "client side MAX_CONCURRENT_STREAMS reached" - ", skip (%zu)", - multiplexed); - continue; - } - } -#endif - /* When not multiplexed, we have a match here! */ - chosen = check; - infof(data, "Multiplexed connection found"); - break; - } - else { - /* We have found a connection. Let's stop searching. */ - chosen = check; - break; - } + if(CONN_INUSE(check)) { + DEBUGASSERT(canmultiplex); + DEBUGASSERT(check->bits.multiplex); + /* If multiplexed, make sure we don't go over concurrency limit */ + if(CONN_INUSE(check) >= + Curl_multi_max_concurrent_streams(data->multi)) { + infof(data, "client side MAX_CONCURRENT_STREAMS reached" + ", skip (%zu)", CONN_INUSE(check)); + continue; } + if(CONN_INUSE(check) >= + Curl_conn_get_max_concurrent(data, check, FIRSTSOCKET)) { + infof(data, "MAX_CONCURRENT_STREAMS reached, skip (%zu)", + CONN_INUSE(check)); + continue; + } + /* When not multiplexed, we have a match here! */ + infof(data, "Multiplexed connection found"); + } + else if(extract_if_dead(check, data)) { + /* disconnect it */ + Curl_disconnect(data, check, TRUE); + continue; } - } + + /* We have found a connection. Let's stop searching. */ + chosen = check; + break; + } /* loop over connection bundle */ if(chosen) { /* mark it as used before releasing the lock */ @@ -1561,17 +1391,6 @@ static struct connectdata *allocate_conn(struct Curl_easy *data) conn->bits.ftp_use_epsv = data->set.ftp_use_epsv; conn->bits.ftp_use_eprt = data->set.ftp_use_eprt; #endif - conn->ssl_config.verifystatus = data->set.ssl.primary.verifystatus; - conn->ssl_config.verifypeer = data->set.ssl.primary.verifypeer; - conn->ssl_config.verifyhost = data->set.ssl.primary.verifyhost; - conn->ssl_config.ssl_options = data->set.ssl.primary.ssl_options; -#ifndef CURL_DISABLE_PROXY - conn->proxy_ssl_config.verifystatus = - data->set.proxy_ssl.primary.verifystatus; - conn->proxy_ssl_config.verifypeer = data->set.proxy_ssl.primary.verifypeer; - conn->proxy_ssl_config.verifyhost = data->set.proxy_ssl.primary.verifyhost; - conn->proxy_ssl_config.ssl_options = data->set.proxy_ssl.primary.ssl_options; -#endif conn->ip_version = data->set.ipver; conn->connect_only = data->set.connect_only; conn->transport = TRNSPRT_TCP; /* most of them are TCP streams */ @@ -1615,30 +1434,231 @@ error: return NULL; } -/* returns the handler if the given scheme is built-in */ -const struct Curl_handler *Curl_builtin_scheme(const char *scheme, - size_t schemelen) +const struct Curl_handler *Curl_get_scheme_handler(const char *scheme) { - const struct Curl_handler * const *pp; - const struct Curl_handler *p; - /* Scan protocol handler table and match against 'scheme'. The handler may - be changed later when the protocol specific setup function is called. */ - if(schemelen == CURL_ZERO_TERMINATED) - schemelen = strlen(scheme); - for(pp = protocols; (p = *pp) != NULL; pp++) - if(strncasecompare(p->scheme, scheme, schemelen) && !p->scheme[schemelen]) - /* Protocol found in table. */ - return p; - return NULL; /* not found */ + return Curl_getn_scheme_handler(scheme, strlen(scheme)); } +/* returns the handler if the given scheme is built-in */ +const struct Curl_handler *Curl_getn_scheme_handler(const char *scheme, + size_t len) +{ + /* table generated by schemetable.c: + 1. gcc schemetable.c && ./a.out + 2. check how small the table gets + 3. tweak the hash algorithm, then rerun from 1 + 4. when the table is good enough + 5. copy the table into this source code + 6. make sure this function uses the same hash function that worked for + schemetable.c + 7. if needed, adjust the #ifdefs in schemetable.c and rerun + */ + static const struct Curl_handler * const protocols[67] = { +#ifndef CURL_DISABLE_FILE + &Curl_handler_file, +#else + NULL, +#endif + NULL, NULL, +#if defined(USE_SSL) && !defined(CURL_DISABLE_GOPHER) + &Curl_handler_gophers, +#else + NULL, +#endif + NULL, +#ifdef USE_LIBRTMP + &Curl_handler_rtmpe, +#else + NULL, +#endif +#ifndef CURL_DISABLE_SMTP + &Curl_handler_smtp, +#else + NULL, +#endif +#if defined(USE_SSH) + &Curl_handler_sftp, +#else + NULL, +#endif +#if !defined(CURL_DISABLE_SMB) && defined(USE_CURL_NTLM_CORE) && \ + (SIZEOF_CURL_OFF_T > 4) + &Curl_handler_smb, +#else + NULL, +#endif +#if defined(USE_SSL) && !defined(CURL_DISABLE_SMTP) + &Curl_handler_smtps, +#else + NULL, +#endif +#ifndef CURL_DISABLE_TELNET + &Curl_handler_telnet, +#else + NULL, +#endif +#ifndef CURL_DISABLE_GOPHER + &Curl_handler_gopher, +#else + NULL, +#endif +#ifndef CURL_DISABLE_TFTP + &Curl_handler_tftp, +#else + NULL, +#endif + NULL, NULL, NULL, +#if defined(USE_SSL) && !defined(CURL_DISABLE_FTP) + &Curl_handler_ftps, +#else + NULL, +#endif +#ifndef CURL_DISABLE_HTTP + &Curl_handler_http, +#else + NULL, +#endif +#ifndef CURL_DISABLE_IMAP + &Curl_handler_imap, +#else + NULL, +#endif +#ifdef USE_LIBRTMP + &Curl_handler_rtmps, +#else + NULL, +#endif +#ifdef USE_LIBRTMP + &Curl_handler_rtmpt, +#else + NULL, +#endif + NULL, NULL, NULL, +#if !defined(CURL_DISABLE_LDAP) && \ + !defined(CURL_DISABLE_LDAPS) && \ + ((defined(USE_OPENLDAP) && defined(USE_SSL)) || \ + (!defined(USE_OPENLDAP) && defined(HAVE_LDAP_SSL))) + &Curl_handler_ldaps, +#else + NULL, +#endif +#if defined(USE_WEBSOCKETS) && \ + defined(USE_SSL) && !defined(CURL_DISABLE_HTTP) + &Curl_handler_wss, +#else + NULL, +#endif +#if defined(USE_SSL) && !defined(CURL_DISABLE_HTTP) + &Curl_handler_https, +#else + NULL, +#endif + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +#ifndef CURL_DISABLE_RTSP + &Curl_handler_rtsp, +#else + NULL, +#endif +#if defined(USE_SSL) && !defined(CURL_DISABLE_SMB) && \ + defined(USE_CURL_NTLM_CORE) && (SIZEOF_CURL_OFF_T > 4) + &Curl_handler_smbs, +#else + NULL, +#endif +#if defined(USE_SSH) && !defined(USE_WOLFSSH) + &Curl_handler_scp, +#else + NULL, +#endif + NULL, NULL, NULL, +#ifndef CURL_DISABLE_POP3 + &Curl_handler_pop3, +#else + NULL, +#endif + NULL, NULL, +#ifdef USE_LIBRTMP + &Curl_handler_rtmp, +#else + NULL, +#endif + NULL, NULL, NULL, +#ifdef USE_LIBRTMP + &Curl_handler_rtmpte, +#else + NULL, +#endif + NULL, NULL, NULL, +#ifndef CURL_DISABLE_DICT + &Curl_handler_dict, +#else + NULL, +#endif + NULL, NULL, NULL, +#ifndef CURL_DISABLE_MQTT + &Curl_handler_mqtt, +#else + NULL, +#endif +#if defined(USE_SSL) && !defined(CURL_DISABLE_POP3) + &Curl_handler_pop3s, +#else + NULL, +#endif +#if defined(USE_SSL) && !defined(CURL_DISABLE_IMAP) + &Curl_handler_imaps, +#else + NULL, +#endif + NULL, +#if defined(USE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP) + &Curl_handler_ws, +#else + NULL, +#endif + NULL, +#ifdef USE_LIBRTMP + &Curl_handler_rtmpts, +#else + NULL, +#endif +#ifndef CURL_DISABLE_LDAP + &Curl_handler_ldap, +#else + NULL, +#endif + NULL, NULL, +#ifndef CURL_DISABLE_FTP + &Curl_handler_ftp, +#else + NULL, +#endif + }; + + if(len && (len <= 7)) { + const char *s = scheme; + size_t l = len; + const struct Curl_handler *h; + unsigned int c = 978; + while(l) { + c <<= 5; + c += Curl_raw_tolower(*s); + s++; + l--; + } + + h = protocols[c % 67]; + if(h && strncasecompare(scheme, h->scheme, len) && !h->scheme[len]) + return h; + } + return NULL; +} static CURLcode findprotocol(struct Curl_easy *data, struct connectdata *conn, const char *protostr) { - const struct Curl_handler *p = Curl_builtin_scheme(protostr, - CURL_ZERO_TERMINATED); + const struct Curl_handler *p = Curl_get_scheme_handler(protostr); if(p && /* Protocol found in table. Check if allowed */ (data->set.allowed_protocols & p->protocol)) { @@ -1652,7 +1672,6 @@ static CURLcode findprotocol(struct Curl_easy *data, else { /* Perform setup complement if some. */ conn->handler = conn->given = p; - /* 'port' and 'remote_port' are set in setup_connection_internals() */ return CURLE_OK; } @@ -1705,14 +1724,14 @@ static void zonefrom_url(CURLU *uh, struct Curl_easy *data, conn->scope_id = (unsigned int)scope; #if defined(HAVE_IF_NAMETOINDEX) else { -#elif defined(WIN32) +#elif defined(_WIN32) else if(Curl_if_nametoindex) { #endif -#if defined(HAVE_IF_NAMETOINDEX) || defined(WIN32) +#if defined(HAVE_IF_NAMETOINDEX) || defined(_WIN32) /* Zone identifier is not numeric */ unsigned int scopeidx = 0; -#if defined(WIN32) +#if defined(_WIN32) scopeidx = Curl_if_nametoindex(zoneid); #else scopeidx = if_nametoindex(zoneid); @@ -1727,7 +1746,7 @@ static void zonefrom_url(CURLU *uh, struct Curl_easy *data, else conn->scope_id = scopeidx; } -#endif /* HAVE_IF_NAMETOINDEX || WIN32 */ +#endif /* HAVE_IF_NAMETOINDEX || _WIN32 */ free(zoneid); } @@ -3596,85 +3615,10 @@ static CURLcode create_conn(struct Curl_easy *data, conn->send[SECONDARYSOCKET] = Curl_conn_send; conn->bits.tcp_fastopen = data->set.tcp_fastopen; - /* Get a cloned copy of the SSL config situation stored in the - connection struct. But to get this going nicely, we must first make - sure that the strings in the master copy are pointing to the correct - strings in the session handle strings array! - - Keep in mind that the pointers in the master copy are pointing to strings - that will be freed as part of the Curl_easy struct, but all cloned - copies will be separately allocated. - */ - data->set.ssl.primary.CApath = data->set.str[STRING_SSL_CAPATH]; - data->set.ssl.primary.CAfile = data->set.str[STRING_SSL_CAFILE]; - data->set.ssl.primary.issuercert = data->set.str[STRING_SSL_ISSUERCERT]; - data->set.ssl.primary.issuercert_blob = data->set.blobs[BLOB_SSL_ISSUERCERT]; - data->set.ssl.primary.cipher_list = - data->set.str[STRING_SSL_CIPHER_LIST]; - data->set.ssl.primary.cipher_list13 = - data->set.str[STRING_SSL_CIPHER13_LIST]; - data->set.ssl.primary.pinned_key = - data->set.str[STRING_SSL_PINNEDPUBLICKEY]; - data->set.ssl.primary.cert_blob = data->set.blobs[BLOB_CERT]; - data->set.ssl.primary.ca_info_blob = data->set.blobs[BLOB_CAINFO]; - data->set.ssl.primary.curves = data->set.str[STRING_SSL_EC_CURVES]; - -#ifndef CURL_DISABLE_PROXY - data->set.proxy_ssl.primary.CApath = data->set.str[STRING_SSL_CAPATH_PROXY]; - data->set.proxy_ssl.primary.CAfile = data->set.str[STRING_SSL_CAFILE_PROXY]; - data->set.proxy_ssl.primary.cipher_list = - data->set.str[STRING_SSL_CIPHER_LIST_PROXY]; - data->set.proxy_ssl.primary.cipher_list13 = - data->set.str[STRING_SSL_CIPHER13_LIST_PROXY]; - data->set.proxy_ssl.primary.pinned_key = - data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY]; - data->set.proxy_ssl.primary.cert_blob = data->set.blobs[BLOB_CERT_PROXY]; - data->set.proxy_ssl.primary.ca_info_blob = - data->set.blobs[BLOB_CAINFO_PROXY]; - data->set.proxy_ssl.primary.issuercert = - data->set.str[STRING_SSL_ISSUERCERT_PROXY]; - data->set.proxy_ssl.primary.issuercert_blob = - data->set.blobs[BLOB_SSL_ISSUERCERT_PROXY]; - data->set.proxy_ssl.primary.CRLfile = - data->set.str[STRING_SSL_CRLFILE_PROXY]; - data->set.proxy_ssl.cert_type = data->set.str[STRING_CERT_TYPE_PROXY]; - data->set.proxy_ssl.key = data->set.str[STRING_KEY_PROXY]; - data->set.proxy_ssl.key_type = data->set.str[STRING_KEY_TYPE_PROXY]; - data->set.proxy_ssl.key_passwd = data->set.str[STRING_KEY_PASSWD_PROXY]; - data->set.proxy_ssl.primary.clientcert = data->set.str[STRING_CERT_PROXY]; - data->set.proxy_ssl.key_blob = data->set.blobs[BLOB_KEY_PROXY]; -#endif - data->set.ssl.primary.CRLfile = data->set.str[STRING_SSL_CRLFILE]; - data->set.ssl.cert_type = data->set.str[STRING_CERT_TYPE]; - data->set.ssl.key = data->set.str[STRING_KEY]; - data->set.ssl.key_type = data->set.str[STRING_KEY_TYPE]; - data->set.ssl.key_passwd = data->set.str[STRING_KEY_PASSWD]; - data->set.ssl.primary.clientcert = data->set.str[STRING_CERT]; -#ifdef USE_TLS_SRP - data->set.ssl.primary.username = data->set.str[STRING_TLSAUTH_USERNAME]; - data->set.ssl.primary.password = data->set.str[STRING_TLSAUTH_PASSWORD]; -#ifndef CURL_DISABLE_PROXY - data->set.proxy_ssl.primary.username = - data->set.str[STRING_TLSAUTH_USERNAME_PROXY]; - data->set.proxy_ssl.primary.password = - data->set.str[STRING_TLSAUTH_PASSWORD_PROXY]; -#endif -#endif - data->set.ssl.key_blob = data->set.blobs[BLOB_KEY]; - - if(!Curl_clone_primary_ssl_config(&data->set.ssl.primary, - &conn->ssl_config)) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - -#ifndef CURL_DISABLE_PROXY - if(!Curl_clone_primary_ssl_config(&data->set.proxy_ssl.primary, - &conn->proxy_ssl_config)) { - result = CURLE_OUT_OF_MEMORY; + /* Complete the easy's SSL configuration for connection cache matching */ + result = Curl_ssl_easy_config_complete(data); + if(result) goto out; - } -#endif prune_dead_connections(data); @@ -3789,7 +3733,41 @@ static CURLcode create_conn(struct Curl_easy *data, * This is a brand new connection, so let's store it in the connection * cache of ours! */ + result = Curl_ssl_conn_config_init(data, conn); + if(result) { + DEBUGF(fprintf(stderr, "Error: init connection ssl config\n")); + goto out; + } + + result = Curl_resolver_init(data, &conn->resolve_async.resolver); + if(result) { + DEBUGF(fprintf(stderr, "Error: resolver_init failed\n")); + goto out; + } + Curl_attach_connection(data, conn); + +#ifdef USE_ARES + result = Curl_set_dns_servers(data, data->set.str[STRING_DNS_SERVERS]); + if(result && result != CURLE_NOT_BUILT_IN) + goto out; + + result = Curl_set_dns_interface(data, + data->set.str[STRING_DNS_INTERFACE]); + if(result && result != CURLE_NOT_BUILT_IN) + goto out; + + result = Curl_set_dns_local_ip4(data, + data->set.str[STRING_DNS_LOCAL_IP4]); + if(result && result != CURLE_NOT_BUILT_IN) + goto out; + + result = Curl_set_dns_local_ip6(data, + data->set.str[STRING_DNS_LOCAL_IP6]); + if(result && result != CURLE_NOT_BUILT_IN) + goto out; +#endif /* USE_ARES */ + result = Curl_conncache_add_conn(data); if(result) goto out; @@ -3976,6 +3954,7 @@ CURLcode Curl_init_do(struct Curl_easy *data, struct connectdata *conn) k->bytecount = 0; k->ignorebody = FALSE; + Curl_client_cleanup(data); Curl_speedinit(data); Curl_pgrsSetUploadCounter(data, 0); Curl_pgrsSetDownloadCounter(data, 0); diff --git a/Utilities/cmcurl/lib/url.h b/Utilities/cmcurl/lib/url.h index f6a5b25..7c1a29b 100644 --- a/Utilities/cmcurl/lib/url.h +++ b/Utilities/cmcurl/lib/url.h @@ -46,8 +46,13 @@ CURLcode Curl_parse_login_details(const char *login, const size_t len, char **userptr, char **passwdptr, char **optionsptr); -const struct Curl_handler *Curl_builtin_scheme(const char *scheme, - size_t schemelen); +/* Get protocol handler for a URI scheme + * @param scheme URI scheme, case-insensitive + * @return NULL of handler not found + */ +const struct Curl_handler *Curl_get_scheme_handler(const char *scheme); +const struct Curl_handler *Curl_getn_scheme_handler(const char *scheme, + size_t len); #define CURL_DEFAULT_PROXY_PORT 1080 /* default proxy port unless specified */ #define CURL_DEFAULT_HTTPS_PROXY_PORT 443 /* default https proxy port unless diff --git a/Utilities/cmcurl/lib/urlapi.c b/Utilities/cmcurl/lib/urlapi.c index 4efab61..0d11e48 100644 --- a/Utilities/cmcurl/lib/urlapi.c +++ b/Utilities/cmcurl/lib/urlapi.c @@ -206,7 +206,7 @@ size_t Curl_is_absolute_url(const char *url, char *buf, size_t buflen, (void)buflen; /* only used in debug-builds */ if(buf) buf[0] = 0; /* always leave a defined value in buf */ -#ifdef WIN32 +#ifdef _WIN32 if(guess_scheme && STARTS_WITH_DRIVE_PREFIX(url)) return 0; #endif @@ -446,7 +446,7 @@ static CURLUcode parse_hostname_login(struct Curl_URL *u, /* if this is a known scheme, get some details */ if(u->scheme) - h = Curl_builtin_scheme(u->scheme, CURL_ZERO_TERMINATED); + h = Curl_get_scheme_handler(u->scheme); /* We could use the login information in the URL so extract it. Only parse options if the handler says we should. Note that 'h' might be NULL! */ @@ -1056,7 +1056,7 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags) ptr += 9; /* now points to the slash after the host */ } else { -#if defined(WIN32) +#if defined(_WIN32) size_t len; /* the host name, NetBIOS computer name, can not contain disallowed @@ -1095,7 +1095,7 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags) /* no host for file: URLs by default */ Curl_dyn_reset(&host); -#if !defined(MSDOS) && !defined(WIN32) && !defined(__CYGWIN__) +#if !defined(_WIN32) && !defined(MSDOS) && !defined(__CYGWIN__) /* Don't allow Windows drive letters when not in Windows. * This catches both "file:/c:" and "file:c:" */ if(('/' == path[0] && STARTS_WITH_URL_DRIVE_PREFIX(&path[1])) || @@ -1129,7 +1129,7 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags) } schemep = schemebuf; - if(!Curl_builtin_scheme(schemep, CURL_ZERO_TERMINATED) && + if(!Curl_get_scheme_handler(schemep) && !(flags & CURLU_NON_SUPPORT_SCHEME)) { result = CURLUE_UNSUPPORTED_SCHEME; goto fail; @@ -1224,14 +1224,14 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags) if(flags & CURLU_URLENCODE) { struct dynbuf enc; Curl_dyn_init(&enc, CURL_MAX_INPUT_LENGTH); - if(urlencode_str(&enc, fragment + 1, fraglen, TRUE, FALSE)) { + if(urlencode_str(&enc, fragment + 1, fraglen - 1, TRUE, FALSE)) { result = CURLUE_OUT_OF_MEMORY; goto fail; } u->fragment = Curl_dyn_ptr(&enc); } else { - u->fragment = Curl_memdup(fragment + 1, fraglen); + u->fragment = Curl_strndup(fragment + 1, fraglen - 1); if(!u->fragment) { result = CURLUE_OUT_OF_MEMORY; goto fail; @@ -1260,12 +1260,11 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags) u->query = Curl_dyn_ptr(&enc); } else { - u->query = Curl_memdup(query + 1, qlen); + u->query = Curl_strndup(query + 1, qlen - 1); if(!u->query) { result = CURLUE_OUT_OF_MEMORY; goto fail; } - u->query[qlen - 1] = 0; } } else { @@ -1295,12 +1294,11 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags) } else { if(!u->path) { - u->path = Curl_memdup(path, pathlen + 1); + u->path = Curl_strndup(path, pathlen); if(!u->path) { result = CURLUE_OUT_OF_MEMORY; goto fail; } - u->path[pathlen] = 0; path = u->path; } else if(flags & CURLU_URLENCODE) @@ -1352,7 +1350,7 @@ static CURLUcode parseurl_and_replace(const char *url, CURLU *u, */ CURLU *curl_url(void) { - return calloc(sizeof(struct Curl_URL), 1); + return calloc(1, sizeof(struct Curl_URL)); } void curl_url_cleanup(CURLU *u) @@ -1374,7 +1372,7 @@ void curl_url_cleanup(CURLU *u) CURLU *curl_url_dup(const CURLU *in) { - struct Curl_URL *u = calloc(sizeof(struct Curl_URL), 1); + struct Curl_URL *u = calloc(1, sizeof(struct Curl_URL)); if(u) { DUP(u, in, scheme); DUP(u, in, user); @@ -1447,8 +1445,7 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what, if(!ptr && (flags & CURLU_DEFAULT_PORT) && u->scheme) { /* there's no stored port number, but asked to deliver a default one for the scheme */ - const struct Curl_handler *h = - Curl_builtin_scheme(u->scheme, CURL_ZERO_TERMINATED); + const struct Curl_handler *h = Curl_get_scheme_handler(u->scheme); if(h) { msnprintf(portbuf, sizeof(portbuf), "%u", h->defport); ptr = portbuf; @@ -1457,8 +1454,7 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what, else if(ptr && u->scheme) { /* there is a stored port number, but ask to inhibit if it matches the default one for the scheme */ - const struct Curl_handler *h = - Curl_builtin_scheme(u->scheme, CURL_ZERO_TERMINATED); + const struct Curl_handler *h = Curl_get_scheme_handler(u->scheme); if(h && (h->defport == u->portnum) && (flags & CURLU_NO_DEFAULT_PORT)) ptr = NULL; @@ -1503,7 +1499,7 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what, else return CURLUE_NO_SCHEME; - h = Curl_builtin_scheme(scheme, CURL_ZERO_TERMINATED); + h = Curl_get_scheme_handler(scheme); if(!port && (flags & CURLU_DEFAULT_PORT)) { /* there's no stored port number, but asked to deliver a default one for the scheme */ @@ -1596,7 +1592,7 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what, if(ptr) { size_t partlen = strlen(ptr); size_t i = 0; - *part = Curl_memdup(ptr, partlen + 1); + *part = Curl_strndup(ptr, partlen); if(!*part) return CURLUE_OUT_OF_MEMORY; if(plusdecode) { @@ -1743,9 +1739,8 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, if((plen > MAX_SCHEME_LEN) || (plen < 1)) /* too long or too short */ return CURLUE_BAD_SCHEME; - if(!(flags & CURLU_NON_SUPPORT_SCHEME) && - /* verify that it is a fine scheme */ - !Curl_builtin_scheme(part, CURL_ZERO_TERMINATED)) + /* verify that it is a fine scheme */ + if(!(flags & CURLU_NON_SUPPORT_SCHEME) && !Curl_get_scheme_handler(part)) return CURLUE_UNSUPPORTED_SCHEME; storep = &u->scheme; urlencode = FALSE; /* never */ @@ -1905,7 +1900,7 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, } newp = Curl_dyn_ptr(&enc); - if(appendquery) { + if(appendquery && newp) { /* Append the 'newp' string onto the old query. Add a '&' separator if none is present at the end of the existing query already */ @@ -1934,8 +1929,8 @@ nomem: } } - if(what == CURLUPART_HOST) { - size_t n = strlen(newp); + else if(what == CURLUPART_HOST) { + size_t n = Curl_dyn_len(&enc); if(!n && (flags & CURLU_NO_AUTHORITY)) { /* Skip hostname check, it's allowed to be empty. */ } diff --git a/Utilities/cmcurl/lib/urldata.h b/Utilities/cmcurl/lib/urldata.h index dff26e6..ff66148 100644 --- a/Utilities/cmcurl/lib/urldata.h +++ b/Utilities/cmcurl/lib/urldata.h @@ -266,6 +266,13 @@ typedef enum { /* SSL backend-specific data; declared differently by each SSL backend */ struct ssl_backend_data; +struct ssl_peer { + char *hostname; /* hostname for verification */ + char *dispname; /* display version of hostname */ + char *sni; /* SNI version of hostname or NULL if not usable */ + BIT(is_ip_address); /* if hostname is an IPv4|6 address */ +}; + struct ssl_primary_config { char *CApath; /* certificate dir (doesn't work on windows) */ char *CAfile; /* certificate to verify peer against */ @@ -571,6 +578,13 @@ struct hostname { #define KEEP_RECVBITS (KEEP_RECV | KEEP_RECV_HOLD | KEEP_RECV_PAUSE) #define KEEP_SENDBITS (KEEP_SEND | KEEP_SEND_HOLD | KEEP_SEND_PAUSE) +/* transfer wants to send is not PAUSE or HOLD */ +#define CURL_WANT_SEND(data) \ + (((data)->req.keepon & KEEP_SENDBITS) == KEEP_SEND) +/* transfer receive is not on PAUSE or HOLD */ +#define CURL_WANT_RECV(data) \ + (!((data)->req.keepon & (KEEP_RECV_PAUSE|KEEP_RECV_HOLD))) + #if defined(CURLRES_ASYNCH) || !defined(CURL_DISABLE_DOH) #define USE_CURL_ASYNC struct Curl_async { @@ -589,6 +603,15 @@ struct Curl_async { #define FIRSTSOCKET 0 #define SECONDARYSOCKET 1 +/* Polling requested by an easy handle. + * `action` is CURL_POLL_IN, CURL_POLL_OUT or CURL_POLL_INOUT. + */ +struct easy_pollset { + curl_socket_t sockets[MAX_SOCKSPEREASYHANDLE]; + unsigned int num; + unsigned char actions[MAX_SOCKSPEREASYHANDLE]; +}; + enum expect100 { EXP100_SEND_DATA, /* enough waiting, just send the body now */ EXP100_AWAITING_CONTINUE, /* waiting for the 100 Continue header */ @@ -649,16 +672,8 @@ struct SingleRequest { counter to make only a 100 reply (without a following second response code) result in a CURLE_GOT_NOTHING error code */ - enum { - HEADER_NORMAL, /* no bad header at all */ - HEADER_PARTHEADER, /* part of the chunk is a bad header, the rest - is normal data */ - HEADER_ALLBAD /* all was believed to be header */ - } badheader; /* the header was deemed bad and will be - written as body */ int headerline; /* counts header lines to better track the first one */ - char *str; /* within buf */ curl_off_t offset; /* possible resume offset read from the Content-Range: header */ int httpcode; /* error code from the 'HTTP/1.? XXX' or @@ -669,7 +684,7 @@ struct SingleRequest { enum upgrade101 upgr101; /* 101 upgrade state */ /* Content unencoding stack. See sec 3.5, RFC2616. */ - struct contenc_writer *writer_stack; + struct Curl_cwriter *writer_stack; time_t timeofdoc; long bodywrites; char *location; /* This points to an allocated version of the Location: @@ -706,16 +721,20 @@ struct SingleRequest { #ifndef CURL_DISABLE_DOH struct dohdata *doh; /* DoH specific data for this request */ #endif -#if defined(WIN32) && defined(USE_WINSOCK) +#if defined(_WIN32) && defined(USE_WINSOCK) struct curltime last_sndbuf_update; /* last time readwrite_upload called win_update_buffer_size */ #endif + char fread_eof[2]; /* the body read callback (index 0) returned EOF or + the trailer read callback (index 1) returned EOF */ #ifndef CURL_DISABLE_COOKIES unsigned char setcookies; #endif unsigned char writer_stack_depth; /* Unencoding stack depth. */ BIT(header); /* incoming data has HTTP header */ + BIT(badheader); /* header parsing found sth not a header */ BIT(content_range); /* set TRUE if Content-Range: was found */ + BIT(download_done); /* set to TRUE when download is complete */ BIT(upload_done); /* set to TRUE when doing chunked transfer-encoding upload and we're uploading the last chunk */ BIT(ignorebody); /* we read a response-body but we ignore it! */ @@ -799,7 +818,8 @@ struct Curl_handler { /* If used, this function gets called from transfer.c:readwrite_data() to allow the protocol to do extra reads/writes */ CURLcode (*readwrite)(struct Curl_easy *data, struct connectdata *conn, - ssize_t *nread, bool *readmore); + const char *buf, size_t blen, + size_t *pconsumed, bool *readmore); /* This function can perform various checks on the connection. See CONNCHECK_* for more information about the checks that can be performed, @@ -901,6 +921,9 @@ struct connectdata { multi_done(). This entry will be NULL if the connection is reused as then there is no name resolve done. */ struct Curl_dns_entry *dns_entry; +#ifdef USE_CURL_ASYNC + struct Curl_async resolve_async; /* asynchronous name resolver data */ +#endif /* 'remote_addr' is the particular IP we connected to. it is owned, set * and NULLed by the connected socket filter (if there is one). */ @@ -1325,7 +1348,8 @@ struct UrlState { curl_off_t recent_conn_id; /* The most recent connection used, might no * longer exist */ struct dynbuf headerb; /* buffer to store headers in */ - + struct curl_slist *hstslist; /* list of HSTS files set by + curl_easy_setopt(HSTS) calls */ char *buffer; /* download buffer */ char *ulbuf; /* allocated upload buffer or NULL */ curl_off_t current_speed; /* the ProgressShow() function sets this, @@ -1358,9 +1382,6 @@ struct UrlState { #endif struct auth authhost; /* auth details for host */ struct auth authproxy; /* auth details for proxy */ -#ifdef USE_CURL_ASYNC - struct Curl_async async; /* asynchronous name resolver data */ -#endif #if defined(USE_OPENSSL) /* void instead of ENGINE to avoid bleeding OpenSSL into this header */ @@ -1373,7 +1394,7 @@ struct UrlState { /* a place to store the most recently set (S)FTP entrypath */ char *most_recent_ftp_entrypath; -#if !defined(WIN32) && !defined(MSDOS) && !defined(__EMX__) +#if !defined(_WIN32) && !defined(MSDOS) && !defined(__EMX__) /* do FTP line-end conversions on most platforms */ #define CURL_DO_LINEEND_CONV /* for FTP downloads: track CRLF sequences that span blocks */ @@ -1411,7 +1432,7 @@ struct UrlState { this should be dealt with in pretransfer */ #ifndef CURL_DISABLE_HTTP curl_mimepart *mimepost; - curl_mimepart *formp; /* storage for old API form-posting, alloced on + curl_mimepart *formp; /* storage for old API form-posting, allocated on demand */ size_t trailers_bytes_sent; struct dynbuf trailers_buf; /* a buffer containing the compiled trailing @@ -1422,6 +1443,10 @@ struct UrlState { trailers_state trailers_state; /* whether we are sending trailers and what stage are we at */ #endif +#ifndef CURL_DISABLE_COOKIES + struct curl_slist *cookielist; /* list of cookie files set by + curl_easy_setopt(COOKIEFILE) calls */ +#endif #ifdef USE_HYPER bool hconnect; /* set if a CONNECT request */ CURLcode hresult; /* used to pass return codes back from hyper callbacks */ @@ -1498,6 +1523,9 @@ struct UrlState { though it will be discarded. We must call the data rewind callback before trying to send again. */ BIT(upload); /* upload request */ + BIT(internal); /* internal: true if this easy handle was created for + internal use and the user does not have ownership of the + handle. */ }; /* @@ -1674,13 +1702,7 @@ struct UserDefined { void *prereq_userp; /* pre-initial request user data */ void *seek_client; /* pointer to pass to the seek callback */ -#ifndef CURL_DISABLE_COOKIES - struct curl_slist *cookielist; /* list of cookie files set by - curl_easy_setopt(COOKIEFILE) calls */ -#endif #ifndef CURL_DISABLE_HSTS - struct curl_slist *hstslist; /* list of HSTS files set by - curl_easy_setopt(HSTS) calls */ curl_hstsread_callback hsts_read; void *hsts_read_userp; curl_hstswrite_callback hsts_write; @@ -1780,9 +1802,6 @@ struct UserDefined { #endif curl_prot_t allowed_protocols; curl_prot_t redir_protocols; -#ifndef CURL_DISABLE_MIME - unsigned int mime_options; /* Mime option flags. */ -#endif #ifndef CURL_DISABLE_RTSP void *rtp_out; /* write RTP to this if non-NULL */ /* Common RTSP header options */ @@ -1805,8 +1824,6 @@ struct UserDefined { int tcp_keepidle; /* seconds in idle before sending keepalive probe */ int tcp_keepintvl; /* seconds between TCP keepalive probes */ - size_t maxconnects; /* Max idle connections in the connection cache */ - long expect_100_timeout; /* in milliseconds */ #if defined(USE_HTTP2) || defined(USE_HTTP3) struct Curl_data_priority priority; @@ -1831,10 +1848,14 @@ struct UserDefined { BIT(mail_rcpt_allowfails); /* allow RCPT TO command to fail for some recipients */ #endif + unsigned int maxconnects; /* Max idle connections in the connection cache */ unsigned char use_ssl; /* if AUTH TLS is to be attempted etc, for FTP or IMAP or POP3 or others! (type: curl_usessl)*/ unsigned char connect_only; /* make connection/request, then let application use the socket */ +#ifndef CURL_DISABLE_MIME + BIT(mime_formescape); +#endif BIT(is_fread_set); /* has read callback been set to non-NULL? */ #ifndef CURL_DISABLE_TFTP BIT(tftp_no_options); /* do not send TFTP options requests */ @@ -1971,10 +1992,7 @@ struct Curl_easy { particular order. Note that all sockets are added to the sockhash, where the state etc are also kept. This array is mostly used to detect when a socket is to be removed from the hash. See singlesocket(). */ - curl_socket_t sockets[MAX_SOCKSPEREASYHANDLE]; - unsigned char actions[MAX_SOCKSPEREASYHANDLE]; /* action for each socket in - sockets[] */ - int numsocks; + struct easy_pollset last_poll; struct Names dns; struct Curl_multi *multi; /* if non-NULL, points to the multi handle @@ -2013,10 +2031,6 @@ struct Curl_easy { #ifdef USE_HYPER struct hyptransfer hyp; #endif - - /* internal: true if this easy handle was created for internal use and the - user does not have ownership of the handle. */ - bool internal; }; #define LIBCURL_NAME "libcurl" diff --git a/Utilities/cmcurl/lib/vauth/digest.c b/Utilities/cmcurl/lib/vauth/digest.c index 12c6f7d..416da0f 100644 --- a/Utilities/cmcurl/lib/vauth/digest.c +++ b/Utilities/cmcurl/lib/vauth/digest.c @@ -125,7 +125,6 @@ bool Curl_auth_digest_get_pair(const char *str, char *value, char *content, } else return FALSE; - break; } } diff --git a/Utilities/cmcurl/lib/version.c b/Utilities/cmcurl/lib/version.c index f99dd38..31bd0a4 100644 --- a/Utilities/cmcurl/lib/version.c +++ b/Utilities/cmcurl/lib/version.c @@ -39,7 +39,7 @@ #ifdef USE_ARES # if defined(CURL_STATICLIB) && !defined(CARES_STATICLIB) && \ - defined(WIN32) + defined(_WIN32) # define CARES_STATICLIB # endif # include <ares.h> @@ -409,7 +409,8 @@ static int idn_present(curl_version_info_data *info) #define idn_present NULL #endif -#if defined(USE_SSL) && !defined(CURL_DISABLE_PROXY) +#if defined(USE_SSL) && !defined(CURL_DISABLE_PROXY) && \ + !defined(CURL_DISABLE_HTTP) static int https_proxy_present(curl_version_info_data *info) { (void) info; @@ -454,13 +455,14 @@ static const struct feat features_table[] = { #ifndef CURL_DISABLE_HSTS FEATURE("HSTS", NULL, CURL_VERSION_HSTS), #endif -#if defined(USE_NGHTTP2) || defined(USE_HYPER) +#if defined(USE_NGHTTP2) FEATURE("HTTP2", NULL, CURL_VERSION_HTTP2), #endif #if defined(ENABLE_QUIC) FEATURE("HTTP3", NULL, CURL_VERSION_HTTP3), #endif -#if defined(USE_SSL) && !defined(CURL_DISABLE_PROXY) +#if defined(USE_SSL) && !defined(CURL_DISABLE_PROXY) && \ + !defined(CURL_DISABLE_HTTP) FEATURE("HTTPS-proxy", https_proxy_present, CURL_VERSION_HTTPS_PROXY), #endif #if defined(USE_LIBIDN2) || defined(USE_WIN32_IDN) @@ -510,7 +512,7 @@ static const struct feat features_table[] = { #ifdef CURLDEBUG FEATURE("TrackMemory", NULL, CURL_VERSION_CURLDEBUG), #endif -#if defined(WIN32) && defined(UNICODE) && defined(_UNICODE) +#if defined(_WIN32) && defined(UNICODE) && defined(_UNICODE) FEATURE("Unicode", NULL, CURL_VERSION_UNICODE), #endif #ifdef USE_UNIX_SOCKETS diff --git a/Utilities/cmcurl/lib/version_win32.c b/Utilities/cmcurl/lib/version_win32.c index 872d5b4..e0f239e 100644 --- a/Utilities/cmcurl/lib/version_win32.c +++ b/Utilities/cmcurl/lib/version_win32.c @@ -24,7 +24,7 @@ #include "curl_setup.h" -#if defined(WIN32) +#if defined(_WIN32) #include <curl/curl.h> #include "version_win32.h" @@ -316,4 +316,4 @@ bool curlx_verify_windows_version(const unsigned int majorVersion, return matched; } -#endif /* WIN32 */ +#endif /* _WIN32 */ diff --git a/Utilities/cmcurl/lib/version_win32.h b/Utilities/cmcurl/lib/version_win32.h index 3899174..95c0661 100644 --- a/Utilities/cmcurl/lib/version_win32.h +++ b/Utilities/cmcurl/lib/version_win32.h @@ -26,7 +26,7 @@ #include "curl_setup.h" -#if defined(WIN32) +#if defined(_WIN32) /* Version condition */ typedef enum { @@ -51,6 +51,6 @@ bool curlx_verify_windows_version(const unsigned int majorVersion, const PlatformIdentifier platform, const VersionCondition condition); -#endif /* WIN32 */ +#endif /* _WIN32 */ #endif /* HEADER_CURL_VERSION_WIN32_H */ diff --git a/Utilities/cmcurl/lib/vquic/curl_msh3.c b/Utilities/cmcurl/lib/vquic/curl_msh3.c index 6bd0d23..8ae3672 100644 --- a/Utilities/cmcurl/lib/vquic/curl_msh3.c +++ b/Utilities/cmcurl/lib/vquic/curl_msh3.c @@ -38,6 +38,7 @@ #include "http1.h" #include "curl_msh3.h" #include "socketpair.h" +#include "vtls/vtls.h" #include "vquic/vquic.h" /* The last 3 #include files should be in this order */ @@ -45,6 +46,10 @@ #include "curl_memory.h" #include "memdebug.h" +#ifdef CURL_DISABLE_SOCKETPAIR +#error "MSH3 cannot be build with CURL_DISABLE_SOCKETPAIR set" +#endif + #define H3_STREAM_WINDOW_SIZE (128 * 1024) #define H3_STREAM_CHUNK_SIZE (16 * 1024) #define H3_STREAM_RECV_CHUNKS \ @@ -672,31 +677,25 @@ out: return nwritten; } -static int cf_msh3_get_select_socks(struct Curl_cfilter *cf, - struct Curl_easy *data, - curl_socket_t *socks) +static void cf_msh3_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) { struct cf_msh3_ctx *ctx = cf->ctx; struct stream_ctx *stream = H3_STREAM_CTX(data); - int bitmap = GETSOCK_BLANK; struct cf_call_data save; CF_DATA_SAVE(save, cf, data); if(stream && ctx->sock[SP_LOCAL] != CURL_SOCKET_BAD) { - socks[0] = ctx->sock[SP_LOCAL]; - if(stream->recv_error) { - bitmap |= GETSOCK_READSOCK(0); + Curl_pollset_add_in(data, ps, ctx->sock[SP_LOCAL]); drain_stream(cf, data); } else if(stream->req) { - bitmap |= GETSOCK_READSOCK(0); + Curl_pollset_add_out(data, ps, ctx->sock[SP_LOCAL]); drain_stream(cf, data); } } - CURL_TRC_CF(data, cf, "select_sock -> %d", bitmap); - CF_DATA_RESTORE(cf, save); - return bitmap; } static bool cf_msh3_data_pending(struct Curl_cfilter *cf, @@ -802,14 +801,20 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, struct Curl_easy *data) { struct cf_msh3_ctx *ctx = cf->ctx; - bool verify = !!cf->conn->ssl_config.verifypeer; + struct ssl_primary_config *conn_config; MSH3_ADDR addr = {0}; CURLcode result; + bool verify; + + conn_config = Curl_ssl_cf_get_primary_config(cf); + if(!conn_config) + return CURLE_FAILED_INIT; + verify = !!conn_config->verifypeer; memcpy(&addr, &ctx->addr.sa_addr, ctx->addr.addrlen); MSH3_SET_PORT(&addr, (uint16_t)cf->conn->remote_port); - if(verify && (cf->conn->ssl_config.CAfile || cf->conn->ssl_config.CApath)) { + if(verify && (conn_config->CAfile || conn_config->CApath)) { /* TODO: need a way to provide trust anchors to MSH3 */ #ifdef DEBUGBUILD /* we need this for our test cases to run */ @@ -1025,7 +1030,7 @@ struct Curl_cftype Curl_cft_http3 = { cf_msh3_connect, cf_msh3_close, Curl_cf_def_get_host, - cf_msh3_get_select_socks, + cf_msh3_adjust_pollset, cf_msh3_data_pending, cf_msh3_send, cf_msh3_recv, @@ -1047,7 +1052,7 @@ CURLcode Curl_cf_msh3_create(struct Curl_cfilter **pcf, (void)data; (void)conn; (void)ai; /* TODO: msh3 resolves itself? */ - ctx = calloc(sizeof(*ctx), 1); + ctx = calloc(1, sizeof(*ctx)); if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; diff --git a/Utilities/cmcurl/lib/vquic/curl_ngtcp2.c b/Utilities/cmcurl/lib/vquic/curl_ngtcp2.c index 7d681e5..f09b10b 100644 --- a/Utilities/cmcurl/lib/vquic/curl_ngtcp2.c +++ b/Utilities/cmcurl/lib/vquic/curl_ngtcp2.c @@ -78,7 +78,6 @@ #define QUIC_MAX_STREAMS (256*1024) #define QUIC_MAX_DATA (1*1024*1024) -#define QUIC_IDLE_TIMEOUT (60*NGTCP2_SECONDS) #define QUIC_HANDSHAKE_TIMEOUT (10*NGTCP2_SECONDS) /* A stream window is the maximum amount we need to buffer for @@ -134,6 +133,7 @@ void Curl_ngtcp2_ver(char *p, size_t len) struct cf_ngtcp2_ctx { struct cf_quic_ctx q; + struct ssl_peer peer; ngtcp2_path connected_path; ngtcp2_conn *qconn; ngtcp2_cid dcid; @@ -161,6 +161,7 @@ struct cf_ngtcp2_ctx { struct curltime reconnect_at; /* time the next attempt should start */ struct bufc_pool stream_bufcp; /* chunk pool for streams */ size_t max_stream_window; /* max flow window for one stream */ + uint64_t max_idle_ms; /* max idle time for QUIC connection */ int qlogfd; BIT(got_first_byte); /* if first byte was received */ #ifdef USE_OPENSSL @@ -191,6 +192,7 @@ struct h3_stream_ctx { bool closed; /* TRUE on stream close */ bool reset; /* TRUE on stream reset */ bool send_closed; /* stream is local closed */ + BIT(quic_flow_blocked); /* stream is blocked by QUIC flow control */ }; #define H3_STREAM_CTX(d) ((struct h3_stream_ctx *)(((d) && (d)->req.p.http)? \ @@ -236,11 +238,21 @@ static CURLcode h3_data_setup(struct Curl_cfilter *cf, static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) { + struct cf_ngtcp2_ctx *ctx = cf->ctx; struct h3_stream_ctx *stream = H3_STREAM_CTX(data); (void)cf; if(stream) { CURL_TRC_CF(data, cf, "[%"PRId64"] easy handle is done", stream->id); + if(ctx->h3conn && !stream->closed) { + nghttp3_conn_shutdown_stream_read(ctx->h3conn, stream->id); + nghttp3_conn_close_stream(ctx->h3conn, stream->id, + NGHTTP3_H3_REQUEST_CANCELLED); + nghttp3_conn_set_stream_user_data(ctx->h3conn, stream->id, NULL); + ngtcp2_conn_set_stream_user_data(ctx->qconn, stream->id, NULL); + stream->closed = TRUE; + } + Curl_bufq_free(&stream->sendbuf); Curl_bufq_free(&stream->recvbuf); Curl_h1_req_parse_free(&stream->h1); @@ -249,6 +261,43 @@ static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) } } +static struct Curl_easy *get_stream_easy(struct Curl_cfilter *cf, + struct Curl_easy *data, + int64_t stream_id) +{ + struct Curl_easy *sdata; + + (void)cf; + if(H3_STREAM_ID(data) == stream_id) { + return data; + } + else { + DEBUGASSERT(data->multi); + for(sdata = data->multi->easyp; sdata; sdata = sdata->next) { + if((sdata->conn == data->conn) && H3_STREAM_ID(sdata) == stream_id) { + return sdata; + } + } + } + return NULL; +} + +static void h3_drain_stream(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct h3_stream_ctx *stream = H3_STREAM_CTX(data); + unsigned char bits; + + (void)cf; + bits = CURL_CSELECT_IN; + if(stream && stream->upload_left && !stream->send_closed) + bits |= CURL_CSELECT_OUT; + if(data->state.dselect_bits != bits) { + data->state.dselect_bits = bits; + Curl_expire(data, 0, EXPIRE_RUN_NOW); + } +} + /* ngtcp2 default congestion controller does not perform pacing. Limit the maximum packet burst to MAX_PKT_BURST packets. */ #define MAX_PKT_BURST 10 @@ -261,10 +310,14 @@ struct pkt_io_ctx { ngtcp2_path_storage ps; }; -static ngtcp2_tstamp timestamp(void) +static void pktx_update_time(struct pkt_io_ctx *pktx, + struct Curl_cfilter *cf) { - struct curltime ct = Curl_now(); - return ct.tv_sec * NGTCP2_SECONDS + ct.tv_usec * NGTCP2_MICROSECONDS; + struct cf_ngtcp2_ctx *ctx = cf->ctx; + + vquic_ctx_update_time(&ctx->q); + pktx->ts = ctx->q.last_op.tv_sec * NGTCP2_SECONDS + + ctx->q.last_op.tv_usec * NGTCP2_MICROSECONDS; } static void pktx_init(struct pkt_io_ctx *pktx, @@ -273,9 +326,9 @@ static void pktx_init(struct pkt_io_ctx *pktx, { pktx->cf = cf; pktx->data = data; - pktx->ts = timestamp(); pktx->pkt_count = 0; ngtcp2_path_storage_zero(&pktx->ps); + pktx_update_time(pktx, cf); } static CURLcode cf_progress_ingress(struct Curl_cfilter *cf, @@ -354,7 +407,7 @@ static void quic_settings(struct cf_ngtcp2_ctx *ctx, t->initial_max_stream_data_uni = ctx->max_stream_window; t->initial_max_streams_bidi = QUIC_MAX_STREAMS; t->initial_max_streams_uni = QUIC_MAX_STREAMS; - t->max_idle_timeout = QUIC_IDLE_TIMEOUT; + t->max_idle_timeout = (ctx->max_idle_ms * NGTCP2_MILLISECONDS); if(ctx->qlogfd != -1) { s->qlog_write = qlog_callback; } @@ -398,14 +451,19 @@ static CURLcode quic_ssl_ctx(SSL_CTX **pssl_ctx, struct Curl_cfilter *cf, struct Curl_easy *data) { struct cf_ngtcp2_ctx *ctx = cf->ctx; - struct connectdata *conn = cf->conn; + struct ssl_primary_config *conn_config; CURLcode result = CURLE_FAILED_INIT; - SSL_CTX *ssl_ctx = SSL_CTX_new(TLS_method()); + SSL_CTX *ssl_ctx = SSL_CTX_new(TLS_method()); if(!ssl_ctx) { result = CURLE_OUT_OF_MEMORY; goto out; } + conn_config = Curl_ssl_cf_get_primary_config(cf); + if(!conn_config) { + result = CURLE_FAILED_INIT; + goto out; + } #if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC) if(ngtcp2_crypto_boringssl_configure_client_context(ssl_ctx) != 0) { @@ -422,8 +480,8 @@ static CURLcode quic_ssl_ctx(SSL_CTX **pssl_ctx, SSL_CTX_set_default_verify_paths(ssl_ctx); { - const char *curves = conn->ssl_config.curves ? - conn->ssl_config.curves : QUIC_GROUPS; + const char *curves = conn_config->curves ? + conn_config->curves : QUIC_GROUPS; if(!SSL_CTX_set1_curves_list(ssl_ctx, curves)) { failf(data, "failed setting curves list for QUIC: '%s'", curves); return CURLE_SSL_CIPHER; @@ -432,8 +490,8 @@ static CURLcode quic_ssl_ctx(SSL_CTX **pssl_ctx, #ifndef OPENSSL_IS_BORINGSSL { - const char *ciphers13 = conn->ssl_config.cipher_list13 ? - conn->ssl_config.cipher_list13 : QUIC_CIPHERS; + const char *ciphers13 = conn_config->cipher_list13 ? + conn_config->cipher_list13 : QUIC_CIPHERS; if(SSL_CTX_set_ciphersuites(ssl_ctx, ciphers13) != 1) { failf(data, "failed setting QUIC cipher suite: %s", ciphers13); return CURLE_SSL_CIPHER; @@ -452,7 +510,7 @@ static CURLcode quic_ssl_ctx(SSL_CTX **pssl_ctx, * fail to connect if the verification fails, or if it should continue * anyway. In the latter case the result of the verification is checked with * SSL_get_verify_result() below. */ - SSL_CTX_set_verify(ssl_ctx, conn->ssl_config.verifypeer ? + SSL_CTX_set_verify(ssl_ctx, conn_config->verifypeer ? SSL_VERIFY_PEER : SSL_VERIFY_NONE, NULL); /* give application a chance to interfere with SSL set up. */ @@ -491,7 +549,7 @@ static CURLcode quic_set_client_cert(struct Curl_cfilter *cf, SSL_CTX *ssl_ctx = ctx->sslctx; const struct ssl_config_data *ssl_config; - ssl_config = Curl_ssl_get_config(data, FIRSTSOCKET); + ssl_config = Curl_ssl_cf_get_config(cf, data); DEBUGASSERT(ssl_config); if(ssl_config->primary.clientcert || ssl_config->primary.cert_blob @@ -514,7 +572,6 @@ static CURLcode quic_init_ssl(struct Curl_cfilter *cf, struct cf_ngtcp2_ctx *ctx = cf->ctx; const uint8_t *alpn = NULL; size_t alpnlen = 0; - unsigned char checkip[16]; DEBUGASSERT(!ctx->ssl); ctx->ssl = SSL_new(ctx->sslctx); @@ -529,13 +586,8 @@ static CURLcode quic_init_ssl(struct Curl_cfilter *cf, SSL_set_alpn_protos(ctx->ssl, alpn, (int)alpnlen); /* set SNI */ - if((0 == Curl_inet_pton(AF_INET, cf->conn->host.name, checkip)) -#ifdef ENABLE_IPV6 - && (0 == Curl_inet_pton(AF_INET6, cf->conn->host.name, checkip)) -#endif - ) { - char *snihost = Curl_ssl_snihost(data, cf->conn->host.name, NULL); - if(!snihost || !SSL_set_tlsext_host_name(ctx->ssl, snihost)) { + if(ctx->peer.sni) { + if(!SSL_set_tlsext_host_name(ctx->ssl, ctx->peer.sni)) { failf(data, "Failed set SNI"); SSL_free(ctx->ssl); ctx->ssl = NULL; @@ -549,20 +601,24 @@ static CURLcode quic_init_ssl(struct Curl_cfilter *cf, struct Curl_easy *data) { struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct ssl_primary_config *conn_config; CURLcode result; gnutls_datum_t alpn[2]; /* this will need some attention when HTTPS proxy over QUIC get fixed */ - const char * const hostname = cf->conn->host.name; long * const pverifyresult = &data->set.ssl.certverifyresult; int rc; + conn_config = Curl_ssl_cf_get_primary_config(cf); + if(!conn_config) + return CURLE_FAILED_INIT; + DEBUGASSERT(ctx->gtls == NULL); ctx->gtls = calloc(1, sizeof(*(ctx->gtls))); if(!ctx->gtls) return CURLE_OUT_OF_MEMORY; - result = gtls_client_init(data, &cf->conn->ssl_config, &data->set.ssl, - hostname, ctx->gtls, pverifyresult); + result = gtls_client_init(data, conn_config, &data->set.ssl, + &ctx->peer, ctx->gtls, pverifyresult); if(result) return result; @@ -602,10 +658,17 @@ static CURLcode quic_init_ssl(struct Curl_cfilter *cf, static CURLcode quic_ssl_ctx(WOLFSSL_CTX **pssl_ctx, struct Curl_cfilter *cf, struct Curl_easy *data) { - struct connectdata *conn = cf->conn; CURLcode result = CURLE_FAILED_INIT; - WOLFSSL_CTX *ssl_ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method()); + struct ssl_primary_config *conn_config; + WOLFSSL_CTX *ssl_ctx = NULL; + conn_config = Curl_ssl_cf_get_primary_config(cf); + if(!conn_config) { + result = CURLE_FAILED_INIT; + goto out; + } + + ssl_ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method()); if(!ssl_ctx) { result = CURLE_OUT_OF_MEMORY; goto out; @@ -613,13 +676,14 @@ static CURLcode quic_ssl_ctx(WOLFSSL_CTX **pssl_ctx, if(ngtcp2_crypto_wolfssl_configure_client_context(ssl_ctx) != 0) { failf(data, "ngtcp2_crypto_wolfssl_configure_client_context failed"); + result = CURLE_FAILED_INIT; goto out; } wolfSSL_CTX_set_default_verify_paths(ssl_ctx); - if(wolfSSL_CTX_set_cipher_list(ssl_ctx, conn->ssl_config.cipher_list13 ? - conn->ssl_config.cipher_list13 : + if(wolfSSL_CTX_set_cipher_list(ssl_ctx, conn_config->cipher_list13 ? + conn_config->cipher_list13 : QUIC_CIPHERS) != 1) { char error_buffer[256]; ERR_error_string_n(ERR_get_error(), error_buffer, sizeof(error_buffer)); @@ -627,8 +691,8 @@ static CURLcode quic_ssl_ctx(WOLFSSL_CTX **pssl_ctx, goto out; } - if(wolfSSL_CTX_set1_groups_list(ssl_ctx, conn->ssl_config.curves ? - conn->ssl_config.curves : + if(wolfSSL_CTX_set1_groups_list(ssl_ctx, conn_config->curves ? + conn_config->curves : (char *)QUIC_GROUPS) != 1) { failf(data, "wolfSSL failed to set curves"); goto out; @@ -645,9 +709,9 @@ static CURLcode quic_ssl_ctx(WOLFSSL_CTX **pssl_ctx, #endif } - if(conn->ssl_config.verifypeer) { - const char * const ssl_cafile = conn->ssl_config.CAfile; - const char * const ssl_capath = conn->ssl_config.CApath; + if(conn_config->verifypeer) { + const char * const ssl_cafile = conn_config->CAfile; + const char * const ssl_capath = conn_config->CApath; wolfSSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL); if(ssl_cafile || ssl_capath) { @@ -786,6 +850,12 @@ static int cb_recv_stream_data(ngtcp2_conn *tconn, uint32_t flags, CURL_TRC_CF(data, cf, "[%" PRId64 "] read_stream(len=%zu) -> %zd", stream_id, buflen, nconsumed); if(nconsumed < 0) { + if(!data) { + struct Curl_easy *cdata = CF_DATA_CURRENT(cf); + CURL_TRC_CF(cdata, cf, "[%" PRId64 "] nghttp3 error on stream not " + "used by us, ignored", stream_id); + return 0; + } ngtcp2_ccerr_set_application_error( &ctx->last_error, nghttp3_err_infer_quic_app_error_code((int)nconsumed), NULL, 0); @@ -816,7 +886,7 @@ cb_acked_stream_data_offset(ngtcp2_conn *tconn, int64_t stream_id, (void)stream_user_data; rv = nghttp3_conn_add_ack_offset(ctx->h3conn, stream_id, datalen); - if(rv) { + if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { return NGTCP2_ERR_CALLBACK_FAILURE; } @@ -844,7 +914,7 @@ static int cb_stream_close(ngtcp2_conn *tconn, uint32_t flags, app_error_code); CURL_TRC_CF(data, cf, "[%" PRId64 "] quic close(err=%" PRIu64 ") -> %d", stream3_id, app_error_code, rv); - if(rv) { + if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { ngtcp2_ccerr_set_application_error( &ctx->last_error, nghttp3_err_infer_quic_app_error_code(rv), NULL, 0); return NGTCP2_ERR_CALLBACK_FAILURE; @@ -868,7 +938,7 @@ static int cb_stream_reset(ngtcp2_conn *tconn, int64_t stream_id, rv = nghttp3_conn_shutdown_stream_read(ctx->h3conn, stream_id); CURL_TRC_CF(data, cf, "[%" PRId64 "] reset -> %d", stream_id, rv); - if(rv) { + if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { return NGTCP2_ERR_CALLBACK_FAILURE; } @@ -887,7 +957,7 @@ static int cb_stream_stop_sending(ngtcp2_conn *tconn, int64_t stream_id, (void)stream_user_data; rv = nghttp3_conn_shutdown_stream_read(ctx->h3conn, stream_id); - if(rv) { + if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { return NGTCP2_ERR_CALLBACK_FAILURE; } @@ -911,16 +981,25 @@ static int cb_extend_max_stream_data(ngtcp2_conn *tconn, int64_t stream_id, { struct Curl_cfilter *cf = user_data; struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct Curl_easy *data = CF_DATA_CURRENT(cf); + struct Curl_easy *s_data; + struct h3_stream_ctx *stream; int rv; (void)tconn; (void)max_data; (void)stream_user_data; rv = nghttp3_conn_unblock_stream(ctx->h3conn, stream_id); - if(rv) { + if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { return NGTCP2_ERR_CALLBACK_FAILURE; } - + s_data = get_stream_easy(cf, data, stream_id); + stream = H3_STREAM_CTX(s_data); + if(stream && stream->quic_flow_blocked) { + CURL_TRC_CF(data, cf, "[%" PRId64 "] unblock quic flow", stream_id); + stream->quic_flow_blocked = FALSE; + h3_drain_stream(cf, data); + } return 0; } @@ -1038,7 +1117,7 @@ static CURLcode check_and_set_expiry(struct Curl_cfilter *cf, pktx = &local_pktx; } else { - pktx->ts = timestamp(); + pktx_update_time(pktx, cf); } expiry = ngtcp2_conn_get_expiry(ctx->qconn); @@ -1073,46 +1152,29 @@ static CURLcode check_and_set_expiry(struct Curl_cfilter *cf, return CURLE_OK; } -static int cf_ngtcp2_get_select_socks(struct Curl_cfilter *cf, +static void cf_ngtcp2_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data, - curl_socket_t *socks) + struct easy_pollset *ps) { struct cf_ngtcp2_ctx *ctx = cf->ctx; - struct SingleRequest *k = &data->req; - int rv = GETSOCK_BLANK; - struct h3_stream_ctx *stream = H3_STREAM_CTX(data); - struct cf_call_data save; - - CF_DATA_SAVE(save, cf, data); - socks[0] = ctx->q.sockfd; + bool want_recv = CURL_WANT_RECV(data); + bool want_send = CURL_WANT_SEND(data); - /* in HTTP/3 we can always get a frame, so check read */ - rv |= GETSOCK_READSOCK(0); - - /* we're still uploading or the HTTP/2 layer wants to send data */ - if((k->keepon & KEEP_SENDBITS) == KEEP_SEND && - ngtcp2_conn_get_cwnd_left(ctx->qconn) && - ngtcp2_conn_get_max_data_left(ctx->qconn) && - stream && nghttp3_conn_is_stream_writable(ctx->h3conn, stream->id)) - rv |= GETSOCK_WRITESOCK(0); - - CF_DATA_RESTORE(cf, save); - return rv; -} - -static void h3_drain_stream(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct h3_stream_ctx *stream = H3_STREAM_CTX(data); - unsigned char bits; + if(ctx->qconn && (want_recv || want_send)) { + struct h3_stream_ctx *stream = H3_STREAM_CTX(data); + struct cf_call_data save; + bool c_exhaust, s_exhaust; - (void)cf; - bits = CURL_CSELECT_IN; - if(stream && stream->upload_left && !stream->send_closed) - bits |= CURL_CSELECT_OUT; - if(data->state.dselect_bits != bits) { - data->state.dselect_bits = bits; - Curl_expire(data, 0, EXPIRE_RUN_NOW); + CF_DATA_SAVE(save, cf, data); + c_exhaust = !ngtcp2_conn_get_cwnd_left(ctx->qconn) || + !ngtcp2_conn_get_max_data_left(ctx->qconn); + s_exhaust = stream && stream->id >= 0 && stream->quic_flow_blocked; + want_recv = (want_recv || c_exhaust || s_exhaust); + want_send = (!s_exhaust && want_send) || + !Curl_bufq_is_empty(&ctx->q.sendbuf); + + Curl_pollset_set(data, ps, ctx->q.sockfd, want_recv, want_send); + CF_DATA_RESTORE(cf, save); } } @@ -1141,7 +1203,6 @@ static int cb_h3_stream_close(nghttp3_conn *conn, int64_t stream_id, else { CURL_TRC_CF(data, cf, "[%" PRId64 "] CLOSED", stream->id); } - data->req.keepon &= ~KEEP_SEND_HOLD; h3_drain_stream(cf, data); return 0; } @@ -1570,15 +1631,9 @@ static int cb_h3_acked_req_body(nghttp3_conn *conn, int64_t stream_id, /* Everything ACKed, we resume upload processing */ if(!stream->sendbuf_len_in_flight) { int rv = nghttp3_conn_resume_stream(conn, stream_id); - if(rv) { + if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { return NGTCP2_ERR_CALLBACK_FAILURE; } - if((data->req.keepon & KEEP_SEND_HOLD) && - (data->req.keepon & KEEP_SEND)) { - data->req.keepon &= ~KEEP_SEND_HOLD; - h3_drain_stream(cf, data); - CURL_TRC_CF(data, cf, "[%" PRId64 "] unpausing acks", stream_id); - } } return 0; } @@ -1676,6 +1731,10 @@ static ssize_t h3_stream_open(struct Curl_cfilter *cf, goto out; stream = H3_STREAM_CTX(data); DEBUGASSERT(stream); + if(!stream) { + *err = CURLE_FAILED_INIT; + goto out; + } nwritten = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL, 0, err); if(nwritten < 0) @@ -1711,7 +1770,7 @@ static ssize_t h3_stream_open(struct Curl_cfilter *cf, nva[i].flags = NGHTTP3_NV_FLAG_NONE; } - rc = ngtcp2_conn_open_bidi_stream(ctx->qconn, &stream->id, NULL); + rc = ngtcp2_conn_open_bidi_stream(ctx->qconn, &stream->id, data); if(rc) { failf(data, "can get bidi streams"); *err = CURLE_SEND_ERROR; @@ -1860,15 +1919,13 @@ static ssize_t cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data, if(stream && sent > 0 && stream->sendbuf_len_in_flight) { /* We have unacknowledged DATA and cannot report success to our * caller. Instead we EAGAIN and remember how much we have already - * "written" into our various internal connection buffers. - * We put the stream upload on HOLD, until this gets ACKed. */ + * "written" into our various internal connection buffers. */ stream->upload_blocked_len = sent; CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send(len=%zu), " "%zu bytes in flight -> EGAIN", stream->id, len, stream->sendbuf_len_in_flight); *err = CURLE_AGAIN; sent = -1; - data->req.keepon |= KEEP_SEND_HOLD; } out: @@ -1887,40 +1944,37 @@ static CURLcode qng_verify_peer(struct Curl_cfilter *cf, struct Curl_easy *data) { struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct ssl_primary_config *conn_config; CURLcode result = CURLE_OK; - const char *hostname, *disp_hostname; - int port; - char *snihost; - Curl_conn_get_host(data, cf->sockindex, &hostname, &disp_hostname, &port); - snihost = Curl_ssl_snihost(data, hostname, NULL); - if(!snihost) - return CURLE_PEER_FAILED_VERIFICATION; + conn_config = Curl_ssl_cf_get_primary_config(cf); + if(!conn_config) + return CURLE_FAILED_INIT; cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ cf->conn->httpversion = 30; cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX; - if(cf->conn->ssl_config.verifyhost) { + if(conn_config->verifyhost) { #ifdef USE_OPENSSL X509 *server_cert; - server_cert = SSL_get_peer_certificate(ctx->ssl); + server_cert = SSL_get1_peer_certificate(ctx->ssl); if(!server_cert) { return CURLE_PEER_FAILED_VERIFICATION; } - result = Curl_ossl_verifyhost(data, cf->conn, server_cert); + result = Curl_ossl_verifyhost(data, cf->conn, &ctx->peer, server_cert); X509_free(server_cert); if(result) return result; #elif defined(USE_GNUTLS) result = Curl_gtls_verifyserver(data, ctx->gtls->session, - &cf->conn->ssl_config, &data->set.ssl, - hostname, disp_hostname, + conn_config, &data->set.ssl, &ctx->peer, data->set.str[STRING_SSL_PINNEDPUBLICKEY]); if(result) return result; #elif defined(USE_WOLFSSL) - if(wolfSSL_check_domain_name(ctx->ssl, snihost) == SSL_FAILURE) + if(!ctx->peer.sni || + wolfSSL_check_domain_name(ctx->ssl, ctx->peer.sni) == SSL_FAILURE) return CURLE_PEER_FAILED_VERIFICATION; #endif infof(data, "Verified certificate just fine"); @@ -1955,8 +2009,8 @@ static CURLcode recv_pkt(const unsigned char *pkt, size_t pktlen, rv = ngtcp2_conn_read_pkt(ctx->qconn, &path, &pi, pkt, pktlen, pktx->ts); if(rv) { - CURL_TRC_CF(pktx->data, pktx->cf, "ingress, read_pkt -> %s", - ngtcp2_strerror(rv)); + CURL_TRC_CF(pktx->data, pktx->cf, "ingress, read_pkt -> %s (%d)", + ngtcp2_strerror(rv), rv); if(!ctx->last_error.error_code) { if(rv == NGTCP2_ERR_CRYPTO) { ngtcp2_ccerr_set_tls_alert(&ctx->last_error, @@ -1993,7 +2047,7 @@ static CURLcode cf_progress_ingress(struct Curl_cfilter *cf, pktx = &local_pktx; } else { - pktx->ts = timestamp(); + pktx_update_time(pktx, cf); } #ifdef USE_OPENSSL @@ -2081,11 +2135,18 @@ static ssize_t read_pkt_to_send(void *userp, } else if(n < 0) { switch(n) { - case NGTCP2_ERR_STREAM_DATA_BLOCKED: + case NGTCP2_ERR_STREAM_DATA_BLOCKED: { + struct h3_stream_ctx *stream = H3_STREAM_CTX(x->data); DEBUGASSERT(ndatalen == -1); nghttp3_conn_block_stream(ctx->h3conn, stream_id); + CURL_TRC_CF(x->data, x->cf, "[%" PRId64 "] block quic flow", + stream_id); + DEBUGASSERT(stream); + if(stream) + stream->quic_flow_blocked = TRUE; n = 0; break; + } case NGTCP2_ERR_STREAM_SHUT_WR: DEBUGASSERT(ndatalen == -1); nghttp3_conn_shutdown_stream_write(ctx->h3conn, stream_id); @@ -2145,7 +2206,7 @@ static CURLcode cf_progress_egress(struct Curl_cfilter *cf, pktx = &local_pktx; } else { - pktx->ts = timestamp(); + pktx_update_time(pktx, cf); ngtcp2_path_storage_zero(&pktx->ps); } @@ -2282,10 +2343,12 @@ static CURLcode cf_ngtcp2_data_event(struct Curl_cfilter *cf, case CF_CTRL_DATA_PAUSE: result = h3_data_pause(cf, data, (arg1 != 0)); break; - case CF_CTRL_DATA_DONE: { + case CF_CTRL_DATA_DETACH: + h3_data_done(cf, data); + break; + case CF_CTRL_DATA_DONE: h3_data_done(cf, data); break; - } case CF_CTRL_DATA_DONE_SEND: { struct h3_stream_ctx *stream = H3_STREAM_CTX(data); if(stream && !stream->send_closed) { @@ -2344,6 +2407,7 @@ static void cf_ngtcp2_ctx_clear(struct cf_ngtcp2_ctx *ctx) if(ctx->qconn) ngtcp2_conn_del(ctx->qconn); Curl_bufcp_free(&ctx->stream_bufcp); + Curl_ssl_peer_cleanup(&ctx->peer); memset(ctx, 0, sizeof(*ctx)); ctx->qlogfd = -1; @@ -2358,15 +2422,15 @@ static void cf_ngtcp2_close(struct Curl_cfilter *cf, struct Curl_easy *data) CF_DATA_SAVE(save, cf, data); if(ctx && ctx->qconn) { char buffer[NGTCP2_MAX_UDP_PAYLOAD_SIZE]; - ngtcp2_tstamp ts; + struct pkt_io_ctx pktx; ngtcp2_ssize rc; CURL_TRC_CF(data, cf, "close"); - ts = timestamp(); + pktx_init(&pktx, cf, data); rc = ngtcp2_conn_write_connection_close(ctx->qconn, NULL, /* path */ NULL, /* pkt_info */ (uint8_t *)buffer, sizeof(buffer), - &ctx->last_error, ts); + &ctx->last_error, pktx.ts); if(rc > 0) { while((send(ctx->q.sockfd, buffer, (SEND_TYPE_ARG3)rc, 0) == -1) && SOCKERRNO == EINTR); @@ -2411,9 +2475,14 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, ctx->version = NGTCP2_PROTO_VER_MAX; ctx->max_stream_window = H3_STREAM_WINDOW_SIZE; + ctx->max_idle_ms = CURL_QUIC_MAX_IDLE_MS; Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE, H3_STREAM_POOL_SPARES); + result = Curl_ssl_peer_init(&ctx->peer, cf); + if(result) + return result; + #ifdef USE_OPENSSL result = quic_ssl_ctx(&ctx->sslctx, cf, data); if(result) @@ -2559,27 +2628,9 @@ out: ngtcp2_conn_in_draining_period(ctx->qconn)) { /* When a QUIC server instance is shutting down, it may send us a * CONNECTION_CLOSE right away. Our connection then enters the DRAINING - * state. - * This may be a stopping of the service or it may be that the server - * is reloading and a new instance will start serving soon. - * In any case, we tear down our socket and start over with a new one. - * We re-open the underlying UDP cf right now, but do not start - * connecting until called again. - */ - int reconn_delay_ms = 200; - - CURL_TRC_CF(data, cf, "connect, remote closed, reconnect after %dms", - reconn_delay_ms); - Curl_conn_cf_close(cf->next, data); - cf_ngtcp2_ctx_clear(ctx); - result = Curl_conn_cf_connect(cf->next, data, FALSE, done); - if(!result && *done) { - *done = FALSE; - ctx->reconnect_at = now; - ctx->reconnect_at.tv_usec += reconn_delay_ms * 1000; - Curl_expire(data, reconn_delay_ms, EXPIRE_QUIC); - result = CURLE_OK; - } + * state. The CONNECT may work in the near future again. Indicate + * that as a "weird" reply. */ + result = CURLE_WEIRD_SERVER_REPLY; } #ifndef CURL_DISABLE_VERBOSE_STRINGS @@ -2657,24 +2708,51 @@ static bool cf_ngtcp2_conn_is_alive(struct Curl_cfilter *cf, struct Curl_easy *data, bool *input_pending) { - bool alive = TRUE; + struct cf_ngtcp2_ctx *ctx = cf->ctx; + bool alive = FALSE; + const ngtcp2_transport_params *rp; + struct cf_call_data save; + CF_DATA_SAVE(save, cf, data); *input_pending = FALSE; + if(!ctx->qconn) + goto out; + + /* Both sides of the QUIC connection announce they max idle times in + * the transport parameters. Look at the minimum of both and if + * we exceed this, regard the connection as dead. The other side + * may have completely purged it and will no longer respond + * to any packets from us. */ + rp = ngtcp2_conn_get_remote_transport_params(ctx->qconn); + if(rp) { + timediff_t idletime; + uint64_t idle_ms = ctx->max_idle_ms; + + if(rp->max_idle_timeout && + (rp->max_idle_timeout / NGTCP2_MILLISECONDS) < idle_ms) + idle_ms = (rp->max_idle_timeout / NGTCP2_MILLISECONDS); + idletime = Curl_timediff(Curl_now(), ctx->q.last_io); + if(idletime > 0 && (uint64_t)idletime > idle_ms) + goto out; + } + if(!cf->next || !cf->next->cft->is_alive(cf->next, data, input_pending)) - return FALSE; + goto out; + alive = TRUE; if(*input_pending) { + CURLcode result; /* This happens before we've sent off a request and the connection is not in use by any other transfer, there shouldn't be any data here, only "protocol frames" */ *input_pending = FALSE; - if(cf_progress_ingress(cf, data, NULL)) - alive = FALSE; - else { - alive = TRUE; - } + result = cf_progress_ingress(cf, data, NULL); + CURL_TRC_CF(data, cf, "is_alive, progress ingress -> %d", result); + alive = result? FALSE : TRUE; } +out: + CF_DATA_RESTORE(cf, save); return alive; } @@ -2686,7 +2764,7 @@ struct Curl_cftype Curl_cft_http3 = { cf_ngtcp2_connect, cf_ngtcp2_close, Curl_cf_def_get_host, - cf_ngtcp2_get_select_socks, + cf_ngtcp2_adjust_pollset, cf_ngtcp2_data_pending, cf_ngtcp2_send, cf_ngtcp2_recv, @@ -2706,7 +2784,7 @@ CURLcode Curl_cf_ngtcp2_create(struct Curl_cfilter **pcf, CURLcode result; (void)data; - ctx = calloc(sizeof(*ctx), 1); + ctx = calloc(1, sizeof(*ctx)); if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; diff --git a/Utilities/cmcurl/lib/vquic/curl_quiche.c b/Utilities/cmcurl/lib/vquic/curl_quiche.c index 3f5d327..7123d63 100644 --- a/Utilities/cmcurl/lib/vquic/curl_quiche.c +++ b/Utilities/cmcurl/lib/vquic/curl_quiche.c @@ -55,10 +55,10 @@ #include "curl_memory.h" #include "memdebug.h" -/* #define DEBUG_QUICHE */ +/* HTTP/3 error values defined in RFC 9114, ch. 8.1 */ +#define CURL_H3_NO_ERROR (0x0100) #define QUIC_MAX_STREAMS (100) -#define QUIC_IDLE_TIMEOUT (60 * 1000) /* milliseconds */ #define H3_STREAM_WINDOW_SIZE (128 * 1024) #define H3_STREAM_CHUNK_SIZE (16 * 1024) @@ -92,6 +92,7 @@ static void keylog_callback(const SSL *ssl, const char *line) struct cf_quiche_ctx { struct cf_quic_ctx q; + struct ssl_peer peer; quiche_conn *qconn; quiche_config *cfg; quiche_h3_conn *h3c; @@ -105,7 +106,7 @@ struct cf_quiche_ctx { struct curltime reconnect_at; /* time the next attempt should start */ struct bufc_pool stream_bufcp; /* chunk pool for streams */ curl_off_t data_recvd; - size_t sends_on_hold; /* # of streams with SEND_HOLD set */ + uint64_t max_idle_ms; /* max idle time for QUIC conn */ BIT(goaway); /* got GOAWAY from server */ BIT(got_first_byte); /* if first byte was received */ BIT(x509_store_setup); /* if x509 store has been set up */ @@ -132,6 +133,8 @@ static void cf_quiche_ctx_clear(struct cf_quiche_ctx *ctx) if(ctx->cfg) quiche_config_free(ctx->cfg); Curl_bufcp_free(&ctx->stream_bufcp); + Curl_ssl_peer_cleanup(&ctx->peer); + memset(ctx, 0, sizeof(*ctx)); } } @@ -140,11 +143,16 @@ static CURLcode quic_x509_store_setup(struct Curl_cfilter *cf, struct Curl_easy *data) { struct cf_quiche_ctx *ctx = cf->ctx; + struct ssl_primary_config *conn_config; + + conn_config = Curl_ssl_cf_get_primary_config(cf); + if(!conn_config) + return CURLE_FAILED_INIT; if(!ctx->x509_store_setup) { - if(cf->conn->ssl_config.verifypeer) { - const char * const ssl_cafile = cf->conn->ssl_config.CAfile; - const char * const ssl_capath = cf->conn->ssl_config.CApath; + if(conn_config->verifypeer) { + const char * const ssl_cafile = conn_config->CAfile; + const char * const ssl_capath = conn_config->CApath; if(ssl_cafile || ssl_capath) { SSL_CTX_set_verify(ctx->sslctx, SSL_VERIFY_PEER, NULL); /* tell OpenSSL where to find CA certificates that are used to verify @@ -177,9 +185,16 @@ static CURLcode quic_x509_store_setup(struct Curl_cfilter *cf, static CURLcode quic_ssl_setup(struct Curl_cfilter *cf, struct Curl_easy *data) { struct cf_quiche_ctx *ctx = cf->ctx; - unsigned char checkip[16]; - struct connectdata *conn = data->conn; - const char *curves = conn->ssl_config.curves; + struct ssl_primary_config *conn_config; + CURLcode result; + + conn_config = Curl_ssl_cf_get_primary_config(cf); + if(!conn_config) + return CURLE_FAILED_INIT; + + result = Curl_ssl_peer_init(&ctx->peer, cf); + if(result) + return result; DEBUGASSERT(!ctx->sslctx); ctx->sslctx = SSL_CTX_new(TLS_method()); @@ -198,8 +213,10 @@ static CURLcode quic_ssl_setup(struct Curl_cfilter *cf, struct Curl_easy *data) SSL_CTX_set_keylog_callback(ctx->sslctx, keylog_callback); } - if(curves && !SSL_CTX_set1_curves_list(ctx->sslctx, curves)) { - failf(data, "failed setting curves list for QUIC: '%s'", curves); + if(conn_config->curves && + !SSL_CTX_set1_curves_list(ctx->sslctx, conn_config->curves)) { + failf(data, "failed setting curves list for QUIC: '%s'", + conn_config->curves); return CURLE_SSL_CIPHER; } @@ -209,13 +226,8 @@ static CURLcode quic_ssl_setup(struct Curl_cfilter *cf, struct Curl_easy *data) SSL_set_app_data(ctx->ssl, cf); - if((0 == Curl_inet_pton(AF_INET, cf->conn->host.name, checkip)) -#ifdef ENABLE_IPV6 - && (0 == Curl_inet_pton(AF_INET6, cf->conn->host.name, checkip)) -#endif - ) { - char *snihost = Curl_ssl_snihost(data, cf->conn->host.name, NULL); - if(!snihost || !SSL_set_tlsext_host_name(ctx->ssl, snihost)) { + if(ctx->peer.sni) { + if(!SSL_set_tlsext_host_name(ctx->ssl, ctx->peer.sni)) { failf(data, "Failed set SNI"); SSL_free(ctx->ssl); ctx->ssl = NULL; @@ -240,6 +252,7 @@ struct stream_ctx { bool send_closed; /* stream is locally closed */ bool resp_hds_complete; /* complete, final response has been received */ bool resp_got_header; /* TRUE when h3 stream has recvd some HEADER */ + BIT(quic_flow_blocked); /* stream is blocked by QUIC flow control */ }; #define H3_STREAM_CTX(d) ((struct stream_ctx *)(((d) && (d)->req.p.http)? \ @@ -249,56 +262,20 @@ struct stream_ctx { #define H3_STREAM_ID(d) (H3_STREAM_CTX(d)? \ H3_STREAM_CTX(d)->id : -2) -static bool stream_send_is_suspended(struct Curl_easy *data) -{ - return (data->req.keepon & KEEP_SEND_HOLD); -} - -static void stream_send_suspend(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_quiche_ctx *ctx = cf->ctx; - - if((data->req.keepon & KEEP_SENDBITS) == KEEP_SEND) { - data->req.keepon |= KEEP_SEND_HOLD; - ++ctx->sends_on_hold; - if(H3_STREAM_ID(data) >= 0) - CURL_TRC_CF(data, cf, "[%"PRId64"] suspend sending", - H3_STREAM_ID(data)); - else - CURL_TRC_CF(data, cf, "[%s] suspend sending", data->state.url); - } -} - -static void stream_send_resume(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_quiche_ctx *ctx = cf->ctx; - - if(stream_send_is_suspended(data)) { - data->req.keepon &= ~KEEP_SEND_HOLD; - --ctx->sends_on_hold; - if(H3_STREAM_ID(data) >= 0) - CURL_TRC_CF(data, cf, "[%"PRId64"] resume sending", - H3_STREAM_ID(data)); - else - CURL_TRC_CF(data, cf, "[%s] resume sending", data->state.url); - Curl_expire(data, 0, EXPIRE_RUN_NOW); - } -} - static void check_resumes(struct Curl_cfilter *cf, struct Curl_easy *data) { - struct cf_quiche_ctx *ctx = cf->ctx; struct Curl_easy *sdata; - - if(ctx->sends_on_hold) { - DEBUGASSERT(data->multi); - for(sdata = data->multi->easyp; - sdata && ctx->sends_on_hold; sdata = sdata->next) { - if(stream_send_is_suspended(sdata)) { - stream_send_resume(cf, sdata); + struct stream_ctx *stream; + + DEBUGASSERT(data->multi); + for(sdata = data->multi->easyp; sdata; sdata = sdata->next) { + if(sdata->conn == data->conn) { + stream = H3_STREAM_CTX(sdata); + if(stream && stream->quic_flow_blocked) { + stream->quic_flow_blocked = FALSE; + Curl_expire(data, 0, EXPIRE_RUN_NOW); + CURL_TRC_CF(data, cf, "[%"PRId64"] unblock", stream->id); } } } @@ -333,9 +310,15 @@ static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) (void)cf; if(stream) { CURL_TRC_CF(data, cf, "[%"PRId64"] easy handle is done", stream->id); - if(stream_send_is_suspended(data)) { - data->req.keepon &= ~KEEP_SEND_HOLD; - --ctx->sends_on_hold; + if(ctx->qconn && !stream->closed) { + quiche_conn_stream_shutdown(ctx->qconn, stream->id, + QUICHE_SHUTDOWN_READ, CURL_H3_NO_ERROR); + if(!stream->send_closed) { + quiche_conn_stream_shutdown(ctx->qconn, stream->id, + QUICHE_SHUTDOWN_WRITE, CURL_H3_NO_ERROR); + stream->send_closed = TRUE; + } + stream->closed = TRUE; } Curl_bufq_free(&stream->recvbuf); Curl_h1_req_parse_free(&stream->h1); @@ -590,7 +573,6 @@ static CURLcode h3_process_event(struct Curl_cfilter *cf, } stream->closed = TRUE; streamclose(cf->conn, "End of stream"); - data->req.keepon &= ~KEEP_SEND_HOLD; break; case QUICHE_H3_EVENT_GOAWAY: @@ -883,6 +865,8 @@ static ssize_t cf_quiche_recv(struct Curl_cfilter *cf, struct Curl_easy *data, ssize_t nread = -1; CURLcode result; + vquic_ctx_update_time(&ctx->q); + if(!stream) { *err = CURLE_RECV_ERROR; return -1; @@ -1035,9 +1019,8 @@ static ssize_t h3_open_stream(struct Curl_cfilter *cf, if(QUICHE_H3_ERR_STREAM_BLOCKED == stream3_id) { /* quiche seems to report this error if the connection window is * exhausted. Which happens frequently and intermittent. */ - CURL_TRC_CF(data, cf, "send_request(%s) rejected with BLOCKED", - data->state.url); - stream_send_suspend(cf, data); + CURL_TRC_CF(data, cf, "[%"PRId64"] blocked", stream->id); + stream->quic_flow_blocked = TRUE; *err = CURLE_AGAIN; nwritten = -1; goto out; @@ -1081,6 +1064,8 @@ static ssize_t cf_quiche_send(struct Curl_cfilter *cf, struct Curl_easy *data, CURLcode result; ssize_t nwritten; + vquic_ctx_update_time(&ctx->q); + *err = cf_process_ingress(cf, data); if(*err) { nwritten = -1; @@ -1104,7 +1089,7 @@ static ssize_t cf_quiche_send(struct Curl_cfilter *cf, struct Curl_easy *data, if(!quiche_conn_stream_writable(ctx->qconn, stream->id, len)) { CURL_TRC_CF(data, cf, "[%" PRId64 "] send_body(len=%zu) " "-> window exhausted", stream->id, len); - stream_send_suspend(cf, data); + stream->quic_flow_blocked = TRUE; } *err = CURLE_AGAIN; nwritten = -1; @@ -1173,30 +1158,32 @@ static bool stream_is_writeable(struct Curl_cfilter *cf, struct cf_quiche_ctx *ctx = cf->ctx; struct stream_ctx *stream = H3_STREAM_CTX(data); - return stream && - quiche_conn_stream_writable(ctx->qconn, (uint64_t)stream->id, 1); + return stream && (quiche_conn_stream_writable(ctx->qconn, + (uint64_t)stream->id, 1) > 0); } -static int cf_quiche_get_select_socks(struct Curl_cfilter *cf, - struct Curl_easy *data, - curl_socket_t *socks) +static void cf_quiche_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) { struct cf_quiche_ctx *ctx = cf->ctx; - struct SingleRequest *k = &data->req; - int rv = GETSOCK_BLANK; - - socks[0] = ctx->q.sockfd; + bool want_recv = CURL_WANT_RECV(data); + bool want_send = CURL_WANT_SEND(data); - /* in an HTTP/3 connection we can basically always get a frame so we should - always be ready for one */ - rv |= GETSOCK_READSOCK(0); + if(ctx->qconn && (want_recv || want_send)) { + struct stream_ctx *stream = H3_STREAM_CTX(data); + bool c_exhaust, s_exhaust; - /* we're still uploading or the HTTP/3 layer wants to send data */ - if(((k->keepon & KEEP_SENDBITS) == KEEP_SEND) - && stream_is_writeable(cf, data)) - rv |= GETSOCK_WRITESOCK(0); + c_exhaust = FALSE; /* Have not found any call in quiche that tells + us if the connection itself is blocked */ + s_exhaust = stream && stream->id >= 0 && + (stream->quic_flow_blocked || !stream_is_writeable(cf, data)); + want_recv = (want_recv || c_exhaust || s_exhaust); + want_send = (!s_exhaust && want_send) || + !Curl_bufq_is_empty(&ctx->q.sendbuf); - return rv; + Curl_pollset_set(data, ps, ctx->q.sockfd, want_recv, want_send); + } } /* @@ -1238,10 +1225,12 @@ static CURLcode cf_quiche_data_event(struct Curl_cfilter *cf, case CF_CTRL_DATA_PAUSE: result = h3_data_pause(cf, data, (arg1 != 0)); break; - case CF_CTRL_DATA_DONE: { + case CF_CTRL_DATA_DETACH: + h3_data_done(cf, data); + break; + case CF_CTRL_DATA_DONE: h3_data_done(cf, data); break; - } case CF_CTRL_DATA_DONE_SEND: { struct stream_ctx *stream = H3_STREAM_CTX(data); if(stream && !stream->send_closed) { @@ -1276,20 +1265,25 @@ static CURLcode cf_verify_peer(struct Curl_cfilter *cf, struct Curl_easy *data) { struct cf_quiche_ctx *ctx = cf->ctx; + struct ssl_primary_config *conn_config; CURLcode result = CURLE_OK; + conn_config = Curl_ssl_cf_get_primary_config(cf); + if(!conn_config) + return CURLE_FAILED_INIT; + cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ cf->conn->httpversion = 30; cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX; - if(cf->conn->ssl_config.verifyhost) { + if(conn_config->verifyhost) { X509 *server_cert; server_cert = SSL_get_peer_certificate(ctx->ssl); if(!server_cert) { result = CURLE_PEER_FAILED_VERIFICATION; goto out; } - result = Curl_ossl_verifyhost(data, cf->conn, server_cert); + result = Curl_ossl_verifyhost(data, cf->conn, &ctx->peer, server_cert); X509_free(server_cert); if(result) goto out; @@ -1345,6 +1339,7 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, debug_log_init = 1; } #endif + ctx->max_idle_ms = CURL_QUIC_MAX_IDLE_MS; Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE, H3_STREAM_POOL_SPARES); ctx->data_recvd = 0; @@ -1359,7 +1354,7 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, return CURLE_FAILED_INIT; } quiche_config_enable_pacing(ctx->cfg, false); - quiche_config_set_max_idle_timeout(ctx->cfg, QUIC_IDLE_TIMEOUT); + quiche_config_set_max_idle_timeout(ctx->cfg, ctx->max_idle_ms * 1000); quiche_config_set_initial_max_data(ctx->cfg, (1 * 1024 * 1024) /* (QUIC_MAX_STREAMS/2) * H3_STREAM_WINDOW_SIZE */); quiche_config_set_initial_max_streams_bidi(ctx->cfg, QUIC_MAX_STREAMS); @@ -1411,7 +1406,7 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, } /* Known to not work on Windows */ -#if !defined(WIN32) && defined(HAVE_QUICHE_CONN_SET_QLOG_FD) +#if !defined(_WIN32) && defined(HAVE_QUICHE_CONN_SET_QLOG_FD) { int qfd; (void)Curl_qlogdir(data, ctx->scid, sizeof(ctx->scid), &qfd); @@ -1449,7 +1444,6 @@ static CURLcode cf_quiche_connect(struct Curl_cfilter *cf, { struct cf_quiche_ctx *ctx = cf->ctx; CURLcode result = CURLE_OK; - struct curltime now; if(cf->connected) { *done = TRUE; @@ -1464,9 +1458,10 @@ static CURLcode cf_quiche_connect(struct Curl_cfilter *cf, } *done = FALSE; - now = Curl_now(); + vquic_ctx_update_time(&ctx->q); - if(ctx->reconnect_at.tv_sec && Curl_timediff(now, ctx->reconnect_at) < 0) { + if(ctx->reconnect_at.tv_sec && + Curl_timediff(ctx->q.last_op, ctx->reconnect_at) < 0) { /* Not time yet to attempt the next connect */ CURL_TRC_CF(data, cf, "waiting for reconnect time"); goto out; @@ -1476,7 +1471,7 @@ static CURLcode cf_quiche_connect(struct Curl_cfilter *cf, result = cf_connect_start(cf, data); if(result) goto out; - ctx->started_at = now; + ctx->started_at = ctx->q.last_op; result = cf_flush_egress(cf, data); /* we do not expect to be able to recv anything yet */ goto out; @@ -1491,9 +1486,9 @@ static CURLcode cf_quiche_connect(struct Curl_cfilter *cf, goto out; if(quiche_conn_is_established(ctx->qconn)) { + ctx->handshake_at = ctx->q.last_op; CURL_TRC_CF(data, cf, "handshake complete after %dms", - (int)Curl_timediff(now, ctx->started_at)); - ctx->handshake_at = now; + (int)Curl_timediff(ctx->handshake_at, ctx->started_at)); result = cf_verify_peer(cf, data); if(!result) { CURL_TRC_CF(data, cf, "peer verified"); @@ -1506,27 +1501,9 @@ static CURLcode cf_quiche_connect(struct Curl_cfilter *cf, else if(quiche_conn_is_draining(ctx->qconn)) { /* When a QUIC server instance is shutting down, it may send us a * CONNECTION_CLOSE right away. Our connection then enters the DRAINING - * state. - * This may be a stopping of the service or it may be that the server - * is reloading and a new instance will start serving soon. - * In any case, we tear down our socket and start over with a new one. - * We re-open the underlying UDP cf right now, but do not start - * connecting until called again. - */ - int reconn_delay_ms = 200; - - CURL_TRC_CF(data, cf, "connect, remote closed, reconnect after %dms", - reconn_delay_ms); - Curl_conn_cf_close(cf->next, data); - cf_quiche_ctx_clear(ctx); - result = Curl_conn_cf_connect(cf->next, data, FALSE, done); - if(!result && *done) { - *done = FALSE; - ctx->reconnect_at = Curl_now(); - ctx->reconnect_at.tv_usec += reconn_delay_ms * 1000; - Curl_expire(data, reconn_delay_ms, EXPIRE_QUIC); - result = CURLE_OK; - } + * state. The CONNECT may work in the near future again. Indicate + * that as a "weird" reply. */ + result = CURLE_WEIRD_SERVER_REPLY; } out: @@ -1550,6 +1527,7 @@ static void cf_quiche_close(struct Curl_cfilter *cf, struct Curl_easy *data) if(ctx) { if(ctx->qconn) { + vquic_ctx_update_time(&ctx->q); (void)quiche_conn_close(ctx->qconn, TRUE, 0, NULL, 0); /* flushing the egress is not a failsafe way to deliver all the outstanding packets, but we also don't want to get stuck here... */ @@ -1617,9 +1595,32 @@ static bool cf_quiche_conn_is_alive(struct Curl_cfilter *cf, struct Curl_easy *data, bool *input_pending) { + struct cf_quiche_ctx *ctx = cf->ctx; bool alive = TRUE; *input_pending = FALSE; + if(!ctx->qconn) + return FALSE; + + /* Both sides of the QUIC connection announce they max idle times in + * the transport parameters. Look at the minimum of both and if + * we exceed this, regard the connection as dead. The other side + * may have completely purged it and will no longer respond + * to any packets from us. */ + { + quiche_transport_params qpeerparams; + timediff_t idletime; + uint64_t idle_ms = ctx->max_idle_ms; + + if(quiche_conn_peer_transport_params(ctx->qconn, &qpeerparams) && + qpeerparams.peer_max_idle_timeout && + qpeerparams.peer_max_idle_timeout < idle_ms) + idle_ms = qpeerparams.peer_max_idle_timeout; + idletime = Curl_timediff(Curl_now(), cf->conn->lastused); + if(idletime > 0 && (uint64_t)idletime > idle_ms) + return FALSE; + } + if(!cf->next || !cf->next->cft->is_alive(cf->next, data, input_pending)) return FALSE; @@ -1646,7 +1647,7 @@ struct Curl_cftype Curl_cft_http3 = { cf_quiche_connect, cf_quiche_close, Curl_cf_def_get_host, - cf_quiche_get_select_socks, + cf_quiche_adjust_pollset, cf_quiche_data_pending, cf_quiche_send, cf_quiche_recv, @@ -1667,7 +1668,7 @@ CURLcode Curl_cf_quiche_create(struct Curl_cfilter **pcf, (void)data; (void)conn; - ctx = calloc(sizeof(*ctx), 1); + ctx = calloc(1, sizeof(*ctx)); if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; diff --git a/Utilities/cmcurl/lib/vquic/vquic.c b/Utilities/cmcurl/lib/vquic/vquic.c index 9a1a1bb..523b807 100644 --- a/Utilities/cmcurl/lib/vquic/vquic.c +++ b/Utilities/cmcurl/lib/vquic/vquic.c @@ -100,6 +100,7 @@ CURLcode vquic_ctx_init(struct cf_quic_ctx *qctx) } } #endif + vquic_ctx_update_time(qctx); return CURLE_OK; } @@ -109,6 +110,11 @@ void vquic_ctx_free(struct cf_quic_ctx *qctx) Curl_bufq_free(&qctx->sendbuf); } +void vquic_ctx_update_time(struct cf_quic_ctx *qctx) +{ + qctx->last_op = Curl_now(); +} + static CURLcode send_packet_no_gso(struct Curl_cfilter *cf, struct Curl_easy *data, struct cf_quic_ctx *qctx, @@ -242,6 +248,7 @@ static CURLcode vquic_send_packets(struct Curl_cfilter *cf, const uint8_t *pkt, size_t pktlen, size_t gsolen, size_t *psent) { + CURLcode result; #ifdef DEBUGBUILD /* simulate network blocking/partial writes */ if(qctx->wblock_percent > 0) { @@ -254,10 +261,14 @@ static CURLcode vquic_send_packets(struct Curl_cfilter *cf, } #endif if(qctx->no_gso && pktlen > gsolen) { - return send_packet_no_gso(cf, data, qctx, pkt, pktlen, gsolen, psent); + result = send_packet_no_gso(cf, data, qctx, pkt, pktlen, gsolen, psent); } - - return do_sendmsg(cf, data, qctx, pkt, pktlen, gsolen, psent); + else { + result = do_sendmsg(cf, data, qctx, pkt, pktlen, gsolen, psent); + } + if(!result) + qctx->last_io = qctx->last_op; + return result; } CURLcode vquic_flush(struct Curl_cfilter *cf, struct Curl_easy *data, @@ -524,13 +535,17 @@ CURLcode vquic_recv_packets(struct Curl_cfilter *cf, size_t max_pkts, vquic_recv_pkt_cb *recv_cb, void *userp) { + CURLcode result; #if defined(HAVE_SENDMMSG) - return recvmmsg_packets(cf, data, qctx, max_pkts, recv_cb, userp); + result = recvmmsg_packets(cf, data, qctx, max_pkts, recv_cb, userp); #elif defined(HAVE_SENDMSG) - return recvmsg_packets(cf, data, qctx, max_pkts, recv_cb, userp); + result = recvmsg_packets(cf, data, qctx, max_pkts, recv_cb, userp); #else - return recvfrom_packets(cf, data, qctx, max_pkts, recv_cb, userp); + result = recvfrom_packets(cf, data, qctx, max_pkts, recv_cb, userp); #endif + if(!result) + qctx->last_io = qctx->last_op; + return result; } /* diff --git a/Utilities/cmcurl/lib/vquic/vquic_int.h b/Utilities/cmcurl/lib/vquic/vquic_int.h index dbcd009..a820f39 100644 --- a/Utilities/cmcurl/lib/vquic/vquic_int.h +++ b/Utilities/cmcurl/lib/vquic/vquic_int.h @@ -31,6 +31,8 @@ #define MAX_PKT_BURST 10 #define MAX_UDP_PAYLOAD_SIZE 1452 +/* Default QUIC connection timeout we announce from our side */ +#define CURL_QUIC_MAX_IDLE_MS (120 * 1000) struct cf_quic_ctx { curl_socket_t sockfd; /* connected UDP socket */ @@ -38,6 +40,8 @@ struct cf_quic_ctx { socklen_t local_addrlen; /* length of local address */ struct bufq sendbuf; /* buffer for sending one or more packets */ + struct curltime last_op; /* last (attempted) send/recv operation */ + struct curltime last_io; /* last successful socket IO */ size_t gsolen; /* length of individual packets in send buf */ size_t split_len; /* if != 0, buffer length after which GSO differs */ size_t split_gsolen; /* length of individual packets after split_len */ @@ -50,6 +54,8 @@ struct cf_quic_ctx { CURLcode vquic_ctx_init(struct cf_quic_ctx *qctx); void vquic_ctx_free(struct cf_quic_ctx *qctx); +void vquic_ctx_update_time(struct cf_quic_ctx *qctx); + void vquic_push_blocked_pkt(struct Curl_cfilter *cf, struct cf_quic_ctx *qctx, const uint8_t *pkt, size_t pktlen, size_t gsolen); diff --git a/Utilities/cmcurl/lib/vssh/libssh.c b/Utilities/cmcurl/lib/vssh/libssh.c index b0f49d6..97143c4 100644 --- a/Utilities/cmcurl/lib/vssh/libssh.c +++ b/Utilities/cmcurl/lib/vssh/libssh.c @@ -93,6 +93,7 @@ #if defined(__GNUC__) && \ (LIBSSH_VERSION_MINOR >= 10) || \ (LIBSSH_VERSION_MAJOR > 0) +#pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif @@ -1159,13 +1160,23 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block) break; } else if(statvfs) { + #ifdef _MSC_VER + #define LIBSSH_VFS_SIZE_MASK "I64u" + #else + #define LIBSSH_VFS_SIZE_MASK PRIu64 + #endif char *tmp = aprintf("statvfs:\n" - "f_bsize: %llu\n" "f_frsize: %llu\n" - "f_blocks: %llu\n" "f_bfree: %llu\n" - "f_bavail: %llu\n" "f_files: %llu\n" - "f_ffree: %llu\n" "f_favail: %llu\n" - "f_fsid: %llu\n" "f_flag: %llu\n" - "f_namemax: %llu\n", + "f_bsize: %" LIBSSH_VFS_SIZE_MASK "\n" + "f_frsize: %" LIBSSH_VFS_SIZE_MASK "\n" + "f_blocks: %" LIBSSH_VFS_SIZE_MASK "\n" + "f_bfree: %" LIBSSH_VFS_SIZE_MASK "\n" + "f_bavail: %" LIBSSH_VFS_SIZE_MASK "\n" + "f_files: %" LIBSSH_VFS_SIZE_MASK "\n" + "f_ffree: %" LIBSSH_VFS_SIZE_MASK "\n" + "f_favail: %" LIBSSH_VFS_SIZE_MASK "\n" + "f_fsid: %" LIBSSH_VFS_SIZE_MASK "\n" + "f_flag: %" LIBSSH_VFS_SIZE_MASK "\n" + "f_namemax: %" LIBSSH_VFS_SIZE_MASK "\n", statvfs->f_bsize, statvfs->f_frsize, statvfs->f_blocks, statvfs->f_bfree, statvfs->f_bavail, statvfs->f_files, @@ -1466,13 +1477,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block) state(data, SSH_STOP); break; } - /* since this counts what we send to the client, we include the - newline in this counter */ - data->req.bytecount += sshc->readdir_len + 1; - /* output debug output if that is requested */ - Curl_debug(data, CURLINFO_DATA_OUT, (char *)sshc->readdir_filename, - sshc->readdir_len); } else { if(Curl_dyn_add(&sshc->readdir_buf, sshc->readdir_longentry)) { @@ -1564,12 +1569,6 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block) Curl_dyn_ptr(&sshc->readdir_buf), Curl_dyn_len(&sshc->readdir_buf)); - if(!result) { - /* output debug output if that is requested */ - Curl_debug(data, CURLINFO_DATA_OUT, Curl_dyn_ptr(&sshc->readdir_buf), - Curl_dyn_len(&sshc->readdir_buf)); - data->req.bytecount += Curl_dyn_len(&sshc->readdir_buf); - } ssh_string_free_char(sshc->readdir_tmp); sshc->readdir_tmp = NULL; @@ -1963,10 +1962,9 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block) ssh_disconnect(sshc->ssh_session); if(!ssh_version(SSH_VERSION_INT(0, 10, 0))) { /* conn->sock[FIRSTSOCKET] is closed by ssh_disconnect behind our back, - explicitly mark it as closed with the memdebug macro. This libssh + tell the connection to forget about it. This libssh bug is fixed in 0.10.0. */ - fake_sclose(conn->sock[FIRSTSOCKET]); - conn->sock[FIRSTSOCKET] = CURL_SOCKET_BAD; + Curl_conn_forget_socket(data, FIRSTSOCKET); } SSH_STRING_FREE_CHAR(sshc->homedir); @@ -2959,4 +2957,10 @@ void Curl_ssh_version(char *buffer, size_t buflen) (void)msnprintf(buffer, buflen, "libssh/%s", ssh_version(0)); } +#if defined(__GNUC__) && \ + (LIBSSH_VERSION_MINOR >= 10) || \ + (LIBSSH_VERSION_MAJOR > 0) +#pragma GCC diagnostic pop +#endif + #endif /* USE_LIBSSH */ diff --git a/Utilities/cmcurl/lib/vssh/libssh2.c b/Utilities/cmcurl/lib/vssh/libssh2.c index f539b39..11f5f4f 100644 --- a/Utilities/cmcurl/lib/vssh/libssh2.c +++ b/Utilities/cmcurl/lib/vssh/libssh2.c @@ -1537,139 +1537,137 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) state(data, SSH_SFTP_NEXT_QUOTE); break; } - { - /* - * the arguments following the command must be separated from the - * command with a space so we can check for it unconditionally - */ - cp = strchr(cmd, ' '); - if(!cp) { - failf(data, "Syntax error command '%s', missing parameter", - cmd); + + /* + * the arguments following the command must be separated from the + * command with a space so we can check for it unconditionally + */ + cp = strchr(cmd, ' '); + if(!cp) { + failf(data, "Syntax error command '%s', missing parameter", + cmd); + state(data, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = CURLE_QUOTE_ERROR; + break; + } + + /* + * also, every command takes at least one argument so we get that + * first argument right now + */ + result = Curl_get_pathname(&cp, &sshc->quote_path1, sshc->homedir); + if(result) { + if(result == CURLE_OUT_OF_MEMORY) + failf(data, "Out of memory"); + else + failf(data, "Syntax error: Bad first parameter to '%s'", cmd); + state(data, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = result; + break; + } + + /* + * SFTP is a binary protocol, so we don't send text commands + * to the server. Instead, we scan for commands used by + * OpenSSH's sftp program and call the appropriate libssh2 + * functions. + */ + if(strncasecompare(cmd, "chgrp ", 6) || + strncasecompare(cmd, "chmod ", 6) || + strncasecompare(cmd, "chown ", 6) || + strncasecompare(cmd, "atime ", 6) || + strncasecompare(cmd, "mtime ", 6)) { + /* attribute change */ + + /* sshc->quote_path1 contains the mode to set */ + /* get the destination */ + result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir); + if(result) { + if(result == CURLE_OUT_OF_MEMORY) + failf(data, "Out of memory"); + else + failf(data, "Syntax error in %s: Bad second parameter", cmd); + Curl_safefree(sshc->quote_path1); state(data, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; + sshc->actualcode = result; break; } - - /* - * also, every command takes at least one argument so we get that - * first argument right now - */ - result = Curl_get_pathname(&cp, &sshc->quote_path1, sshc->homedir); + memset(&sshp->quote_attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES)); + state(data, SSH_SFTP_QUOTE_STAT); + break; + } + if(strncasecompare(cmd, "ln ", 3) || + strncasecompare(cmd, "symlink ", 8)) { + /* symbolic linking */ + /* sshc->quote_path1 is the source */ + /* get the destination */ + result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir); if(result) { if(result == CURLE_OUT_OF_MEMORY) failf(data, "Out of memory"); else - failf(data, "Syntax error: Bad first parameter to '%s'", cmd); + failf(data, + "Syntax error in ln/symlink: Bad second parameter"); + Curl_safefree(sshc->quote_path1); state(data, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; sshc->actualcode = result; break; } - - /* - * SFTP is a binary protocol, so we don't send text commands - * to the server. Instead, we scan for commands used by - * OpenSSH's sftp program and call the appropriate libssh2 - * functions. - */ - if(strncasecompare(cmd, "chgrp ", 6) || - strncasecompare(cmd, "chmod ", 6) || - strncasecompare(cmd, "chown ", 6) || - strncasecompare(cmd, "atime ", 6) || - strncasecompare(cmd, "mtime ", 6)) { - /* attribute change */ - - /* sshc->quote_path1 contains the mode to set */ - /* get the destination */ - result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir); - if(result) { - if(result == CURLE_OUT_OF_MEMORY) - failf(data, "Out of memory"); - else - failf(data, "Syntax error in %s: Bad second parameter", cmd); - Curl_safefree(sshc->quote_path1); - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = result; - break; - } - memset(&sshp->quote_attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES)); - state(data, SSH_SFTP_QUOTE_STAT); - break; - } - if(strncasecompare(cmd, "ln ", 3) || - strncasecompare(cmd, "symlink ", 8)) { - /* symbolic linking */ - /* sshc->quote_path1 is the source */ - /* get the destination */ - result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir); - if(result) { - if(result == CURLE_OUT_OF_MEMORY) - failf(data, "Out of memory"); - else - failf(data, - "Syntax error in ln/symlink: Bad second parameter"); - Curl_safefree(sshc->quote_path1); - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = result; - break; - } - state(data, SSH_SFTP_QUOTE_SYMLINK); - break; - } - else if(strncasecompare(cmd, "mkdir ", 6)) { - /* create dir */ - state(data, SSH_SFTP_QUOTE_MKDIR); - break; - } - else if(strncasecompare(cmd, "rename ", 7)) { - /* rename file */ - /* first param is the source path */ - /* second param is the dest. path */ - result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir); - if(result) { - if(result == CURLE_OUT_OF_MEMORY) - failf(data, "Out of memory"); - else - failf(data, "Syntax error in rename: Bad second parameter"); - Curl_safefree(sshc->quote_path1); - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = result; - break; - } - state(data, SSH_SFTP_QUOTE_RENAME); - break; - } - else if(strncasecompare(cmd, "rmdir ", 6)) { - /* delete dir */ - state(data, SSH_SFTP_QUOTE_RMDIR); - break; - } - else if(strncasecompare(cmd, "rm ", 3)) { - state(data, SSH_SFTP_QUOTE_UNLINK); + state(data, SSH_SFTP_QUOTE_SYMLINK); + break; + } + else if(strncasecompare(cmd, "mkdir ", 6)) { + /* create dir */ + state(data, SSH_SFTP_QUOTE_MKDIR); + break; + } + else if(strncasecompare(cmd, "rename ", 7)) { + /* rename file */ + /* first param is the source path */ + /* second param is the dest. path */ + result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir); + if(result) { + if(result == CURLE_OUT_OF_MEMORY) + failf(data, "Out of memory"); + else + failf(data, "Syntax error in rename: Bad second parameter"); + Curl_safefree(sshc->quote_path1); + state(data, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = result; break; } + state(data, SSH_SFTP_QUOTE_RENAME); + break; + } + else if(strncasecompare(cmd, "rmdir ", 6)) { + /* delete dir */ + state(data, SSH_SFTP_QUOTE_RMDIR); + break; + } + else if(strncasecompare(cmd, "rm ", 3)) { + state(data, SSH_SFTP_QUOTE_UNLINK); + break; + } #ifdef HAS_STATVFS_SUPPORT - else if(strncasecompare(cmd, "statvfs ", 8)) { - state(data, SSH_SFTP_QUOTE_STATVFS); - break; - } -#endif - - failf(data, "Unknown SFTP command"); - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; + else if(strncasecompare(cmd, "statvfs ", 8)) { + state(data, SSH_SFTP_QUOTE_STATVFS); break; } +#endif + + failf(data, "Unknown SFTP command"); + Curl_safefree(sshc->quote_path1); + Curl_safefree(sshc->quote_path2); + state(data, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = CURLE_QUOTE_ERROR; + break; } - break; case SSH_SFTP_NEXT_QUOTE: Curl_safefree(sshc->quote_path1); @@ -1962,13 +1960,23 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) break; } else if(rc == 0) { + #ifdef _MSC_VER + #define LIBSSH2_VFS_SIZE_MASK "I64u" + #else + #define LIBSSH2_VFS_SIZE_MASK "llu" + #endif char *tmp = aprintf("statvfs:\n" - "f_bsize: %llu\n" "f_frsize: %llu\n" - "f_blocks: %llu\n" "f_bfree: %llu\n" - "f_bavail: %llu\n" "f_files: %llu\n" - "f_ffree: %llu\n" "f_favail: %llu\n" - "f_fsid: %llu\n" "f_flag: %llu\n" - "f_namemax: %llu\n", + "f_bsize: %" LIBSSH2_VFS_SIZE_MASK "\n" + "f_frsize: %" LIBSSH2_VFS_SIZE_MASK "\n" + "f_blocks: %" LIBSSH2_VFS_SIZE_MASK "\n" + "f_bfree: %" LIBSSH2_VFS_SIZE_MASK "\n" + "f_bavail: %" LIBSSH2_VFS_SIZE_MASK "\n" + "f_files: %" LIBSSH2_VFS_SIZE_MASK "\n" + "f_ffree: %" LIBSSH2_VFS_SIZE_MASK "\n" + "f_favail: %" LIBSSH2_VFS_SIZE_MASK "\n" + "f_fsid: %" LIBSSH2_VFS_SIZE_MASK "\n" + "f_flag: %" LIBSSH2_VFS_SIZE_MASK "\n" + "f_namemax: %" LIBSSH2_VFS_SIZE_MASK "\n", statvfs.f_bsize, statvfs.f_frsize, statvfs.f_blocks, statvfs.f_bfree, statvfs.f_bavail, statvfs.f_files, @@ -2341,14 +2349,7 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) state(data, SSH_STOP); break; } - /* since this counts what we send to the client, we include the - newline in this counter */ - data->req.bytecount += readdir_len + 1; - /* output debug output if that is requested */ - Curl_debug(data, CURLINFO_DATA_IN, sshp->readdir_filename, - readdir_len); - Curl_debug(data, CURLINFO_DATA_IN, (char *)"\n", 1); } else { result = Curl_dyn_add(&sshp->readdir, sshp->readdir_longentry); @@ -2427,13 +2428,6 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) Curl_dyn_ptr(&sshp->readdir), Curl_dyn_len(&sshp->readdir)); - if(!result) { - /* output debug output if that is requested */ - Curl_debug(data, CURLINFO_DATA_IN, - Curl_dyn_ptr(&sshp->readdir), - Curl_dyn_len(&sshp->readdir)); - data->req.bytecount += Curl_dyn_len(&sshp->readdir); - } if(result) { Curl_dyn_free(&sshp->readdir); state(data, SSH_STOP); diff --git a/Utilities/cmcurl/lib/vssh/ssh.h b/Utilities/cmcurl/lib/vssh/ssh.h index 1e1b137..ca0533a 100644 --- a/Utilities/cmcurl/lib/vssh/ssh.h +++ b/Utilities/cmcurl/lib/vssh/ssh.h @@ -267,6 +267,7 @@ void Curl_ssh_attach(struct Curl_easy *data, /* for non-SSH builds */ #define Curl_ssh_cleanup() #define Curl_ssh_attach(x,y) +#define Curl_ssh_init() 0 #endif #endif /* HEADER_CURL_SSH_H */ diff --git a/Utilities/cmcurl/lib/vssh/wolfssh.c b/Utilities/cmcurl/lib/vssh/wolfssh.c index 39cee50..4da7e9d 100644 --- a/Utilities/cmcurl/lib/vssh/wolfssh.c +++ b/Utilities/cmcurl/lib/vssh/wolfssh.c @@ -343,9 +343,6 @@ static CURLcode wssh_setup_connection(struct Curl_easy *data, return CURLE_OK; } -static Curl_recv wscp_recv, wsftp_recv; -static Curl_send wscp_send, wsftp_send; - static int userauth(byte authtype, WS_UserAuthData* authdata, void *ctx) diff --git a/Utilities/cmcurl/lib/vtls/bearssl.c b/Utilities/cmcurl/lib/vtls/bearssl.c index 934149c..a6566f4 100644 --- a/Utilities/cmcurl/lib/vtls/bearssl.c +++ b/Utilities/cmcurl/lib/vtls/bearssl.c @@ -582,17 +582,12 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf, const char * const ssl_cafile = /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */ (ca_info_blob ? NULL : conn_config->CAfile); - const char *hostname = connssl->hostname; + const char *hostname = connssl->peer.hostname; const bool verifypeer = conn_config->verifypeer; const bool verifyhost = conn_config->verifyhost; CURLcode ret; unsigned version_min, version_max; int session_set = 0; -#ifdef ENABLE_IPV6 - struct in6_addr addr; -#else - struct in_addr addr; -#endif DEBUGASSERT(backend); CURL_TRC_CF(data, cf, "connect_step1"); @@ -706,11 +701,7 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf, infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data); } - if((1 == Curl_inet_pton(AF_INET, hostname, &addr)) -#ifdef ENABLE_IPV6 - || (1 == Curl_inet_pton(AF_INET6, hostname, &addr)) -#endif - ) { + if(connssl->peer.is_ip_address) { if(verifyhost) { failf(data, "BearSSL: " "host verification of IP address is not supported"); @@ -719,12 +710,11 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf, hostname = NULL; } else { - char *snihost = Curl_ssl_snihost(data, hostname, NULL); - if(!snihost) { + if(!connssl->peer.sni) { failf(data, "Failed to set SNI"); return CURLE_SSL_CONNECT_ERROR; } - hostname = snihost; + hostname = connssl->peer.sni; CURL_TRC_CF(data, cf, "connect_step1, SNI set"); } @@ -749,26 +739,26 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf, return CURLE_OK; } -static int bearssl_get_select_socks(struct Curl_cfilter *cf, - struct Curl_easy *data, - curl_socket_t *socks) +static void bearssl_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) { - struct ssl_connect_data *connssl = cf->ctx; - curl_socket_t sock = Curl_conn_cf_get_socket(cf->next, data); - - if(sock == CURL_SOCKET_BAD) - return GETSOCK_BLANK; - else { - struct bearssl_ssl_backend_data *backend = - (struct bearssl_ssl_backend_data *)connssl->backend; - unsigned state = br_ssl_engine_current_state(&backend->ctx.eng); - if(state & BR_SSL_SENDREC) { - socks[0] = sock; - return GETSOCK_WRITESOCK(0); + if(!cf->connected) { + curl_socket_t sock = Curl_conn_cf_get_socket(cf->next, data); + if(sock != CURL_SOCKET_BAD) { + struct ssl_connect_data *connssl = cf->ctx; + struct bearssl_ssl_backend_data *backend = + (struct bearssl_ssl_backend_data *)connssl->backend; + unsigned state = br_ssl_engine_current_state(&backend->ctx.eng); + + if(state & BR_SSL_SENDREC) { + Curl_pollset_set_out_only(data, ps, sock); + } + else { + Curl_pollset_set_in_only(data, ps, sock); + } } } - socks[0] = sock; - return GETSOCK_READSOCK(0); } static CURLcode bearssl_run_until(struct Curl_cfilter *cf, @@ -1210,7 +1200,7 @@ const struct Curl_ssl Curl_ssl_bearssl = { Curl_none_cert_status_request, /* cert_status_request */ bearssl_connect, /* connect */ bearssl_connect_nonblocking, /* connect_nonblocking */ - bearssl_get_select_socks, /* getsock */ + bearssl_adjust_pollset, /* adjust_pollset */ bearssl_get_internals, /* get_internals */ bearssl_close, /* close_one */ Curl_none_close_all, /* close_all */ diff --git a/Utilities/cmcurl/lib/vtls/gtls.c b/Utilities/cmcurl/lib/vtls/gtls.c index c538a96..4e337f5 100644 --- a/Utilities/cmcurl/lib/vtls/gtls.c +++ b/Utilities/cmcurl/lib/vtls/gtls.c @@ -402,18 +402,13 @@ set_ssl_version_min_max(struct Curl_easy *data, CURLcode gtls_client_init(struct Curl_easy *data, struct ssl_primary_config *config, struct ssl_config_data *ssl_config, - const char *hostname, + struct ssl_peer *peer, struct gtls_instance *gtls, long *pverifyresult) { unsigned int init_flags; int rc; bool sni = TRUE; /* default is SNI enabled */ -#ifdef ENABLE_IPV6 - struct in6_addr addr; -#else - struct in_addr addr; -#endif const char *prioritylist; const char *err = NULL; const char *tls13support; @@ -460,50 +455,60 @@ CURLcode gtls_client_init(struct Curl_easy *data, } #endif - if(config->CAfile) { - /* set the trusted CA cert bundle file */ - gnutls_certificate_set_verify_flags(gtls->cred, - GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT); + if(config->verifypeer) { + bool imported_native_ca = false; - rc = gnutls_certificate_set_x509_trust_file(gtls->cred, - config->CAfile, - GNUTLS_X509_FMT_PEM); - if(rc < 0) { - infof(data, "error reading ca cert file %s (%s)", - config->CAfile, gnutls_strerror(rc)); - if(config->verifypeer) { - *pverifyresult = rc; - return CURLE_SSL_CACERT_BADFILE; + if(ssl_config->native_ca_store) { + rc = gnutls_certificate_set_x509_system_trust(gtls->cred); + if(rc < 0) + infof(data, "error reading native ca store (%s), continuing anyway", + gnutls_strerror(rc)); + else { + infof(data, "found %d certificates in native ca store", rc); + if(rc > 0) + imported_native_ca = true; } } - else - infof(data, "found %d certificates in %s", rc, config->CAfile); - } - if(config->CApath) { - /* set the trusted CA cert directory */ - rc = gnutls_certificate_set_x509_trust_dir(gtls->cred, - config->CApath, - GNUTLS_X509_FMT_PEM); - if(rc < 0) { - infof(data, "error reading ca cert file %s (%s)", - config->CApath, gnutls_strerror(rc)); - if(config->verifypeer) { - *pverifyresult = rc; - return CURLE_SSL_CACERT_BADFILE; + if(config->CAfile) { + /* set the trusted CA cert bundle file */ + gnutls_certificate_set_verify_flags(gtls->cred, + GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT); + + rc = gnutls_certificate_set_x509_trust_file(gtls->cred, + config->CAfile, + GNUTLS_X509_FMT_PEM); + if(rc < 0) { + infof(data, "error reading ca cert file %s (%s)%s", + config->CAfile, gnutls_strerror(rc), + (imported_native_ca ? ", continuing anyway" : "")); + if(!imported_native_ca) { + *pverifyresult = rc; + return CURLE_SSL_CACERT_BADFILE; + } } + else + infof(data, "found %d certificates in %s", rc, config->CAfile); } - else - infof(data, "found %d certificates in %s", rc, config->CApath); - } -#ifdef CURL_CA_FALLBACK - /* use system ca certificate store as fallback */ - if(config->verifypeer && !(config->CAfile || config->CApath)) { - /* this ignores errors on purpose */ - gnutls_certificate_set_x509_system_trust(gtls->cred); + if(config->CApath) { + /* set the trusted CA cert directory */ + rc = gnutls_certificate_set_x509_trust_dir(gtls->cred, + config->CApath, + GNUTLS_X509_FMT_PEM); + if(rc < 0) { + infof(data, "error reading ca cert file %s (%s)%s", + config->CApath, gnutls_strerror(rc), + (imported_native_ca ? ", continuing anyway" : "")); + if(!imported_native_ca) { + *pverifyresult = rc; + return CURLE_SSL_CACERT_BADFILE; + } + } + else + infof(data, "found %d certificates in %s", rc, config->CApath); + } } -#endif if(config->CRLfile) { /* set the CRL list file */ @@ -537,15 +542,9 @@ CURLcode gtls_client_init(struct Curl_easy *data, return CURLE_SSL_CONNECT_ERROR; } - if((0 == Curl_inet_pton(AF_INET, hostname, &addr)) && -#ifdef ENABLE_IPV6 - (0 == Curl_inet_pton(AF_INET6, hostname, &addr)) && -#endif - sni) { - size_t snilen; - char *snihost = Curl_ssl_snihost(data, hostname, &snilen); - if(!snihost || gnutls_server_name_set(gtls->session, GNUTLS_NAME_DNS, - snihost, snilen) < 0) { + if(sni && peer->sni) { + if(gnutls_server_name_set(gtls->session, GNUTLS_NAME_DNS, + peer->sni, strlen(peer->sni)) < 0) { failf(data, "Failed to set SNI"); return CURLE_SSL_CONNECT_ERROR; } @@ -699,7 +698,7 @@ gtls_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) return CURLE_OK; result = gtls_client_init(data, conn_config, ssl_config, - connssl->hostname, + &connssl->peer, &backend->gtls, pverifyresult); if(result) return result; @@ -811,8 +810,7 @@ Curl_gtls_verifyserver(struct Curl_easy *data, gnutls_session_t session, struct ssl_primary_config *config, struct ssl_config_data *ssl_config, - const char *hostname, - const char *dispname, + struct ssl_peer *peer, const char *pinned_key) { unsigned int cert_list_size; @@ -1068,7 +1066,7 @@ Curl_gtls_verifyserver(struct Curl_easy *data, in RFC2818 (HTTPS), which takes into account wildcards, and the subject alternative name PKIX extension. Returns non zero on success, and zero on failure. */ - rc = gnutls_x509_crt_check_hostname(x509_cert, hostname); + rc = gnutls_x509_crt_check_hostname(x509_cert, peer->hostname); #if GNUTLS_VERSION_NUMBER < 0x030306 /* Before 3.3.6, gnutls_x509_crt_check_hostname() didn't check IP addresses. */ @@ -1081,10 +1079,10 @@ Curl_gtls_verifyserver(struct Curl_easy *data, unsigned char addrbuf[sizeof(struct use_addr)]; size_t addrlen = 0; - if(Curl_inet_pton(AF_INET, hostname, addrbuf) > 0) + if(Curl_inet_pton(AF_INET, peer->hostname, addrbuf) > 0) addrlen = 4; #ifdef ENABLE_IPV6 - else if(Curl_inet_pton(AF_INET6, hostname, addrbuf) > 0) + else if(Curl_inet_pton(AF_INET6, peer->hostname, addrbuf) > 0) addrlen = 16; #endif @@ -1114,13 +1112,13 @@ Curl_gtls_verifyserver(struct Curl_easy *data, if(!rc) { if(config->verifyhost) { failf(data, "SSL: certificate subject name (%s) does not match " - "target host name '%s'", certname, dispname); + "target host name '%s'", certname, peer->dispname); gnutls_x509_crt_deinit(x509_cert); return CURLE_PEER_FAILED_VERIFICATION; } else infof(data, " common name: %s (does not match '%s')", - certname, dispname); + certname, peer->dispname); } else infof(data, " common name: %s (matched)", certname); @@ -1253,8 +1251,7 @@ static CURLcode gtls_verifyserver(struct Curl_cfilter *cf, CURLcode result; result = Curl_gtls_verifyserver(data, session, conn_config, ssl_config, - connssl->hostname, connssl->dispname, - pinned_key); + &connssl->peer, pinned_key); if(result) goto out; @@ -1662,7 +1659,7 @@ const struct Curl_ssl Curl_ssl_gnutls = { gtls_cert_status_request, /* cert_status_request */ gtls_connect, /* connect */ gtls_connect_nonblocking, /* connect_nonblocking */ - Curl_ssl_get_select_socks, /* getsock */ + Curl_ssl_adjust_pollset, /* adjust_pollset */ gtls_get_internals, /* get_internals */ gtls_close, /* close_one */ Curl_none_close_all, /* close_all */ diff --git a/Utilities/cmcurl/lib/vtls/gtls.h b/Utilities/cmcurl/lib/vtls/gtls.h index ac141e1..1a81c01 100644 --- a/Utilities/cmcurl/lib/vtls/gtls.h +++ b/Utilities/cmcurl/lib/vtls/gtls.h @@ -43,6 +43,7 @@ struct Curl_easy; struct Curl_cfilter; struct ssl_primary_config; struct ssl_config_data; +struct ssl_peer; struct gtls_instance { gnutls_session_t session; @@ -56,7 +57,7 @@ CURLcode gtls_client_init(struct Curl_easy *data, struct ssl_primary_config *config, struct ssl_config_data *ssl_config, - const char *hostname, + struct ssl_peer *peer, struct gtls_instance *gtls, long *pverifyresult); @@ -65,8 +66,7 @@ Curl_gtls_verifyserver(struct Curl_easy *data, gnutls_session_t session, struct ssl_primary_config *config, struct ssl_config_data *ssl_config, - const char *hostname, - const char *dispname, + struct ssl_peer *peer, const char *pinned_key); extern const struct Curl_ssl Curl_ssl_gnutls; diff --git a/Utilities/cmcurl/lib/vtls/keylog.c b/Utilities/cmcurl/lib/vtls/keylog.c index d37bb18..fbcb25c 100644 --- a/Utilities/cmcurl/lib/vtls/keylog.c +++ b/Utilities/cmcurl/lib/vtls/keylog.c @@ -23,6 +23,11 @@ ***************************************************************************/ #include "curl_setup.h" +#if defined(USE_OPENSSL) || \ + defined(USE_WOLFSSL) || \ + (defined(USE_NGTCP2) && defined(USE_NGHTTP3)) || \ + defined(USE_QUICHE) + #include "keylog.h" #include <curl/curl.h> @@ -55,7 +60,7 @@ Curl_tls_keylog_open(void) if(keylog_file_name) { keylog_file_fp = fopen(keylog_file_name, FOPEN_APPENDTEXT); if(keylog_file_fp) { -#ifdef WIN32 +#ifdef _WIN32 if(setvbuf(keylog_file_fp, NULL, _IONBF, 0)) #else if(setvbuf(keylog_file_fp, NULL, _IOLBF, 4096)) @@ -157,3 +162,5 @@ Curl_tls_keylog_write(const char *label, fputs(line, keylog_file_fp); return true; } + +#endif /* TLS or QUIC backend */ diff --git a/Utilities/cmcurl/lib/vtls/mbedtls.c b/Utilities/cmcurl/lib/vtls/mbedtls.c index 2f994d7..38f7de7 100644 --- a/Utilities/cmcurl/lib/vtls/mbedtls.c +++ b/Utilities/cmcurl/lib/vtls/mbedtls.c @@ -322,7 +322,7 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) char * const ssl_cert = ssl_config->primary.clientcert; const struct curl_blob *ssl_cert_blob = ssl_config->primary.cert_blob; const char * const ssl_crlfile = ssl_config->primary.CRLfile; - const char *hostname = connssl->hostname; + const char *hostname = connssl->peer.hostname; int ret = -1; char errorbuf[128]; @@ -639,9 +639,9 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) mbedtls_ssl_conf_own_cert(&backend->config, &backend->clicert, &backend->pk); } - { - char *snihost = Curl_ssl_snihost(data, hostname, NULL); - if(!snihost || mbedtls_ssl_set_hostname(&backend->ssl, snihost)) { + + if(connssl->peer.sni) { + if(mbedtls_ssl_set_hostname(&backend->ssl, connssl->peer.sni)) { /* mbedtls_ssl_set_hostname() sets the name to use in CN/SAN checks and the name to set in the SNI extension. So even if curl connects to a host specified as an IP address, this function must be used. */ @@ -1274,7 +1274,7 @@ const struct Curl_ssl Curl_ssl_mbedtls = { Curl_none_cert_status_request, /* cert_status_request */ mbedtls_connect, /* connect */ mbedtls_connect_nonblocking, /* connect_nonblocking */ - Curl_ssl_get_select_socks, /* getsock */ + Curl_ssl_adjust_pollset, /* adjust_pollset */ mbedtls_get_internals, /* get_internals */ mbedtls_close, /* close_one */ mbedtls_close_all, /* close_all */ diff --git a/Utilities/cmcurl/lib/vtls/mbedtls_threadlock.c b/Utilities/cmcurl/lib/vtls/mbedtls_threadlock.c index bcb7106..22b1b22 100644 --- a/Utilities/cmcurl/lib/vtls/mbedtls_threadlock.c +++ b/Utilities/cmcurl/lib/vtls/mbedtls_threadlock.c @@ -51,7 +51,7 @@ int Curl_mbedtlsthreadlock_thread_setup(void) { int i; - mutex_buf = calloc(NUMT * sizeof(MBEDTLS_MUTEX_T), 1); + mutex_buf = calloc(1, NUMT * sizeof(MBEDTLS_MUTEX_T)); if(!mutex_buf) return 0; /* error, no number of threads defined */ diff --git a/Utilities/cmcurl/lib/vtls/openssl.c b/Utilities/cmcurl/lib/vtls/openssl.c index 15d84ed..ca6d931 100644 --- a/Utilities/cmcurl/lib/vtls/openssl.c +++ b/Utilities/cmcurl/lib/vtls/openssl.c @@ -79,6 +79,8 @@ #include <openssl/bio.h> #include <openssl/buffer.h> #include <openssl/pkcs12.h> +#include <openssl/tls1.h> +#include <openssl/evp.h> #if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_OCSP) #include <openssl/ocsp.h> @@ -96,6 +98,9 @@ #include "curl_memory.h" #include "memdebug.h" +#ifndef ARRAYSIZE +#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0])) +#endif /* Uncomment the ALLOW_RENEG line to a real #define if you want to allow TLS renegotiations when built with BoringSSL. Renegotiating is non-compliant @@ -173,8 +178,6 @@ #if (OPENSSL_VERSION_NUMBER >= 0x30000000L) #define HAVE_EVP_PKEY_GET_PARAMS 1 -#else -#define SSL_get1_peer_certificate SSL_get_peer_certificate #endif #ifdef HAVE_EVP_PKEY_GET_PARAMS @@ -237,7 +240,11 @@ #elif defined(OPENSSL_IS_AWSLC) #define OSSL_PACKAGE "AWS-LC" #else -#define OSSL_PACKAGE "OpenSSL" +# if defined(USE_NGTCP2) && defined(USE_NGHTTP3) +# define OSSL_PACKAGE "quictls" +# else +# define OSSL_PACKAGE "OpenSSL" +#endif #endif #if (OPENSSL_VERSION_NUMBER >= 0x10100000L) @@ -546,9 +553,9 @@ CURLcode Curl_ossl_certchain(struct Curl_easy *data, SSL *ssl) #else RSA_get0_key(rsa, &n, &e, NULL); #endif /* HAVE_EVP_PKEY_GET_PARAMS */ - BIO_printf(mem, "%d", BN_num_bits(n)); + BIO_printf(mem, "%d", n ? BN_num_bits(n) : 0); #else - BIO_printf(mem, "%d", BN_num_bits(rsa->n)); + BIO_printf(mem, "%d", rsa->n ? BN_num_bits(rsa->n) : 0); #endif /* HAVE_OPAQUE_RSA_DSA_DH */ push_certinfo("RSA Public Key", i); print_pubkey_BN(rsa, n, i); @@ -2106,22 +2113,6 @@ static bool subj_alt_hostcheck(struct Curl_easy *data, return FALSE; } -static CURLcode -ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, - X509 *server_cert, const char *hostname, - const char *dispname); - -CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, - X509 *server_cert) -{ - const char *hostname, *dispname; - int port; - - (void)conn; - Curl_conn_get_host(data, FIRSTSOCKET, &hostname, &dispname, &port); - return ossl_verifyhost(data, conn, server_cert, hostname, dispname); -} - /* Quote from RFC2818 section 3.1 "Server Identity" If a subjectAltName extension of type dNSName is present, that MUST @@ -2144,10 +2135,8 @@ CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, This function is now used from ngtcp2 (QUIC) as well. */ -static CURLcode -ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, - X509 *server_cert, const char *hostname, - const char *dispname) +CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, + struct ssl_peer *peer, X509 *server_cert) { bool matched = FALSE; int target = GEN_DNS; /* target type, GEN_DNS or GEN_IPADD */ @@ -2164,25 +2153,21 @@ ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, size_t hostlen; (void)conn; - hostlen = strlen(hostname); - -#ifndef ENABLE_IPV6 - /* Silence compiler warnings for unused params */ - (void) conn; -#endif - + hostlen = strlen(peer->hostname); + if(peer->is_ip_address) { #ifdef ENABLE_IPV6 - if(conn->bits.ipv6_ip && - Curl_inet_pton(AF_INET6, hostname, &addr)) { - target = GEN_IPADD; - addrlen = sizeof(struct in6_addr); - } - else -#endif - if(Curl_inet_pton(AF_INET, hostname, &addr)) { + if(conn->bits.ipv6_ip && + Curl_inet_pton(AF_INET6, peer->hostname, &addr)) { target = GEN_IPADD; - addrlen = sizeof(struct in_addr); + addrlen = sizeof(struct in6_addr); } + else +#endif + if(Curl_inet_pton(AF_INET, peer->hostname, &addr)) { + target = GEN_IPADD; + addrlen = sizeof(struct in_addr); + } + } /* get a "list" of alternative names */ altnames = X509_get_ext_d2i(server_cert, NID_subject_alt_name, NULL, NULL); @@ -2232,9 +2217,9 @@ ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, if((altlen == strlen(altptr)) && /* if this isn't true, there was an embedded zero in the name string and we cannot match it. */ - subj_alt_hostcheck(data, - altptr, - altlen, hostname, hostlen, dispname)) { + subj_alt_hostcheck(data, altptr, altlen, + peer->hostname, hostlen, + peer->dispname)) { dnsmatched = TRUE; } break; @@ -2246,7 +2231,7 @@ ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, ipmatched = TRUE; infof(data, " subjectAltName: host \"%s\" matched cert's IP address!", - dispname); + peer->dispname); } break; } @@ -2262,9 +2247,9 @@ ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, /* an alternative name matched */ ; else if(dNSName || iPAddress) { - infof(data, " subjectAltName does not match %s", dispname); + infof(data, " subjectAltName does not match %s", peer->dispname); failf(data, "SSL: no alternative certificate subject name matches " - "target host name '%s'", dispname); + "target host name '%s'", peer->dispname); result = CURLE_PEER_FAILED_VERIFICATION; } else { @@ -2328,9 +2313,9 @@ ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, result = CURLE_PEER_FAILED_VERIFICATION; } else if(!Curl_cert_hostcheck((const char *)peer_CN, - peerlen, hostname, hostlen)) { + peerlen, peer->hostname, hostlen)) { failf(data, "SSL: certificate subject name '%s' does not match " - "target host name '%s'", peer_CN, dispname); + "target host name '%s'", peer_CN, peer->dispname); result = CURLE_PEER_FAILED_VERIFICATION; } else { @@ -2739,12 +2724,6 @@ static void ossl_trace(int direction, int ssl_ver, int content_type, #ifdef USE_OPENSSL /* ====================================================== */ -#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME -# define use_sni(x) sni = (x) -#else -# define use_sni(x) Curl_nop_stmt -#endif - /* Check for OpenSSL 1.0.2 which has ALPN support. */ #undef HAS_ALPN #if OPENSSL_VERSION_NUMBER >= 0x10002000L \ @@ -3048,6 +3027,151 @@ static CURLcode load_cacert_from_memory(X509_STORE *store, return (count > 0) ? CURLE_OK : CURLE_SSL_CACERT_BADFILE; } +#if defined(USE_WIN32_CRYPTO) +static CURLcode import_windows_cert_store(struct Curl_easy *data, + const char *name, + X509_STORE *store, + bool *imported) +{ + CURLcode result = CURLE_OK; + HCERTSTORE hStore; + + *imported = false; + + hStore = CertOpenSystemStoreA(0, name); + if(hStore) { + PCCERT_CONTEXT pContext = NULL; + /* The array of enhanced key usage OIDs will vary per certificate and + is declared outside of the loop so that rather than malloc/free each + iteration we can grow it with realloc, when necessary. */ + CERT_ENHKEY_USAGE *enhkey_usage = NULL; + DWORD enhkey_usage_size = 0; + + /* This loop makes a best effort to import all valid certificates from + the MS root store. If a certificate cannot be imported it is + skipped. 'result' is used to store only hard-fail conditions (such + as out of memory) that cause an early break. */ + result = CURLE_OK; + for(;;) { + X509 *x509; + FILETIME now; + BYTE key_usage[2]; + DWORD req_size; + const unsigned char *encoded_cert; +#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) + char cert_name[256]; +#endif + + pContext = CertEnumCertificatesInStore(hStore, pContext); + if(!pContext) + break; + +#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) + if(!CertGetNameStringA(pContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, + NULL, cert_name, sizeof(cert_name))) { + strcpy(cert_name, "Unknown"); + } + infof(data, "SSL: Checking cert \"%s\"", cert_name); +#endif + encoded_cert = (const unsigned char *)pContext->pbCertEncoded; + if(!encoded_cert) + continue; + + GetSystemTimeAsFileTime(&now); + if(CompareFileTime(&pContext->pCertInfo->NotBefore, &now) > 0 || + CompareFileTime(&now, &pContext->pCertInfo->NotAfter) > 0) + continue; + + /* If key usage exists check for signing attribute */ + if(CertGetIntendedKeyUsage(pContext->dwCertEncodingType, + pContext->pCertInfo, + key_usage, sizeof(key_usage))) { + if(!(key_usage[0] & CERT_KEY_CERT_SIGN_KEY_USAGE)) + continue; + } + else if(GetLastError()) + continue; + + /* If enhanced key usage exists check for server auth attribute. + * + * Note "In a Microsoft environment, a certificate might also have + * EKU extended properties that specify valid uses for the + * certificate." The call below checks both, and behavior varies + * depending on what is found. For more details see + * CertGetEnhancedKeyUsage doc. + */ + if(CertGetEnhancedKeyUsage(pContext, 0, NULL, &req_size)) { + if(req_size && req_size > enhkey_usage_size) { + void *tmp = realloc(enhkey_usage, req_size); + + if(!tmp) { + failf(data, "SSL: Out of memory allocating for OID list"); + result = CURLE_OUT_OF_MEMORY; + break; + } + + enhkey_usage = (CERT_ENHKEY_USAGE *)tmp; + enhkey_usage_size = req_size; + } + + if(CertGetEnhancedKeyUsage(pContext, 0, enhkey_usage, &req_size)) { + if(!enhkey_usage->cUsageIdentifier) { + /* "If GetLastError returns CRYPT_E_NOT_FOUND, the certificate + is good for all uses. If it returns zero, the certificate + has no valid uses." */ + if((HRESULT)GetLastError() != CRYPT_E_NOT_FOUND) + continue; + } + else { + DWORD i; + bool found = false; + + for(i = 0; i < enhkey_usage->cUsageIdentifier; ++i) { + if(!strcmp("1.3.6.1.5.5.7.3.1" /* OID server auth */, + enhkey_usage->rgpszUsageIdentifier[i])) { + found = true; + break; + } + } + + if(!found) + continue; + } + } + else + continue; + } + else + continue; + + x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded); + if(!x509) + continue; + + /* Try to import the certificate. This may fail for legitimate + reasons such as duplicate certificate, which is allowed by MS but + not OpenSSL. */ + if(X509_STORE_add_cert(store, x509) == 1) { +#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) + infof(data, "SSL: Imported cert \"%s\"", cert_name); +#endif + *imported = true; + } + X509_free(x509); + } + + free(enhkey_usage); + CertFreeCertificateContext(pContext); + CertCloseStore(hStore, 0); + + if(result) + return result; + } + + return result; +} +#endif + static CURLcode populate_x509_store(struct Curl_cfilter *cf, struct Curl_easy *data, X509_STORE *store) @@ -3077,140 +3201,25 @@ static CURLcode populate_x509_store(struct Curl_cfilter *cf, https://github.com/d3x0r/SACK/blob/master/src/netlib/ssl_layer.c#L1037 https://datatracker.ietf.org/doc/html/rfc5280 */ if(ssl_config->native_ca_store) { - HCERTSTORE hStore = CertOpenSystemStore(0, TEXT("ROOT")); - - if(hStore) { - PCCERT_CONTEXT pContext = NULL; - /* The array of enhanced key usage OIDs will vary per certificate and - is declared outside of the loop so that rather than malloc/free each - iteration we can grow it with realloc, when necessary. */ - CERT_ENHKEY_USAGE *enhkey_usage = NULL; - DWORD enhkey_usage_size = 0; - - /* This loop makes a best effort to import all valid certificates from - the MS root store. If a certificate cannot be imported it is - skipped. 'result' is used to store only hard-fail conditions (such - as out of memory) that cause an early break. */ - result = CURLE_OK; - for(;;) { - X509 *x509; - FILETIME now; - BYTE key_usage[2]; - DWORD req_size; - const unsigned char *encoded_cert; -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) - char cert_name[256]; -#endif - - pContext = CertEnumCertificatesInStore(hStore, pContext); - if(!pContext) - break; - -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) - if(!CertGetNameStringA(pContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, - NULL, cert_name, sizeof(cert_name))) { - strcpy(cert_name, "Unknown"); - } - infof(data, "SSL: Checking cert \"%s\"", cert_name); -#endif - encoded_cert = (const unsigned char *)pContext->pbCertEncoded; - if(!encoded_cert) - continue; - - GetSystemTimeAsFileTime(&now); - if(CompareFileTime(&pContext->pCertInfo->NotBefore, &now) > 0 || - CompareFileTime(&now, &pContext->pCertInfo->NotAfter) > 0) - continue; - - /* If key usage exists check for signing attribute */ - if(CertGetIntendedKeyUsage(pContext->dwCertEncodingType, - pContext->pCertInfo, - key_usage, sizeof(key_usage))) { - if(!(key_usage[0] & CERT_KEY_CERT_SIGN_KEY_USAGE)) - continue; - } - else if(GetLastError()) - continue; - - /* If enhanced key usage exists check for server auth attribute. - * - * Note "In a Microsoft environment, a certificate might also have - * EKU extended properties that specify valid uses for the - * certificate." The call below checks both, and behavior varies - * depending on what is found. For more details see - * CertGetEnhancedKeyUsage doc. - */ - if(CertGetEnhancedKeyUsage(pContext, 0, NULL, &req_size)) { - if(req_size && req_size > enhkey_usage_size) { - void *tmp = realloc(enhkey_usage, req_size); - - if(!tmp) { - failf(data, "SSL: Out of memory allocating for OID list"); - result = CURLE_OUT_OF_MEMORY; - break; - } - - enhkey_usage = (CERT_ENHKEY_USAGE *)tmp; - enhkey_usage_size = req_size; - } - - if(CertGetEnhancedKeyUsage(pContext, 0, enhkey_usage, &req_size)) { - if(!enhkey_usage->cUsageIdentifier) { - /* "If GetLastError returns CRYPT_E_NOT_FOUND, the certificate - is good for all uses. If it returns zero, the certificate - has no valid uses." */ - if((HRESULT)GetLastError() != CRYPT_E_NOT_FOUND) - continue; - } - else { - DWORD i; - bool found = false; - - for(i = 0; i < enhkey_usage->cUsageIdentifier; ++i) { - if(!strcmp("1.3.6.1.5.5.7.3.1" /* OID server auth */, - enhkey_usage->rgpszUsageIdentifier[i])) { - found = true; - break; - } - } - - if(!found) - continue; - } - } - else - continue; - } - else - continue; - - x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded); - if(!x509) - continue; - - /* Try to import the certificate. This may fail for legitimate - reasons such as duplicate certificate, which is allowed by MS but - not OpenSSL. */ - if(X509_STORE_add_cert(store, x509) == 1) { -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) - infof(data, "SSL: Imported cert \"%s\"", cert_name); -#endif - imported_native_ca = true; - } - X509_free(x509); - } - - free(enhkey_usage); - CertFreeCertificateContext(pContext); - CertCloseStore(hStore, 0); - + const char *storeNames[] = { + "ROOT", /* Trusted Root Certification Authorities */ + "CA" /* Intermediate Certification Authorities */ + }; + size_t i; + for(i = 0; i < ARRAYSIZE(storeNames); ++i) { + bool imported = false; + result = import_windows_cert_store(data, storeNames[i], store, + &imported); if(result) return result; + if(imported) { + infof(data, "successfully imported Windows %s store", storeNames[i]); + imported_native_ca = true; + } + else + infof(data, "error importing Windows %s store, continuing anyway", + storeNames[i]); } - if(imported_native_ca) - infof(data, "successfully imported Windows CA store"); - else - infof(data, "error importing Windows CA store, continuing anyway"); } #endif if(ca_info_blob) { @@ -3226,7 +3235,7 @@ static CURLcode populate_x509_store(struct Curl_cfilter *cf, } if(ssl_cafile || ssl_capath) { -#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3) +#if (OPENSSL_VERSION_NUMBER >= 0x30000000L) /* OpenSSL 3.0.0 has deprecated SSL_CTX_load_verify_locations */ if(ssl_cafile && !X509_STORE_load_file(store, ssl_cafile)) { if(!imported_native_ca && !imported_ca_info_blob) { @@ -3355,6 +3364,7 @@ static X509_STORE *get_cached_x509_store(struct Curl_cfilter *cf, struct Curl_multi *multi = data->multi_easy ? data->multi_easy : data->multi; X509_STORE *store = NULL; + DEBUGASSERT(multi); if(multi && multi->ssl_backend_data && multi->ssl_backend_data->store && @@ -3374,6 +3384,7 @@ static void set_cached_x509_store(struct Curl_cfilter *cf, struct Curl_multi *multi = data->multi_easy ? data->multi_easy : data->multi; struct multi_ssl_backend_data *mbackend; + DEBUGASSERT(multi); if(!multi) return; @@ -3465,17 +3476,6 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); BIO *bio; - -#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME - bool sni; - const char *hostname = connssl->hostname; - -#ifdef ENABLE_IPV6 - struct in6_addr addr; -#else - struct in_addr addr; -#endif -#endif const long int ssl_version = conn_config->version; char * const ssl_cert = ssl_config->primary.clientcert; const struct curl_blob *ssl_cert_blob = ssl_config->primary.cert_blob; @@ -3510,7 +3510,6 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, #else req_method = SSLv23_client_method(); #endif - use_sni(TRUE); break; case CURL_SSLVERSION_SSLv2: failf(data, "No SSLv2 support"); @@ -3803,13 +3802,8 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, backend->server_cert = 0x0; #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME - if((0 == Curl_inet_pton(AF_INET, hostname, &addr)) && -#ifdef ENABLE_IPV6 - (0 == Curl_inet_pton(AF_INET6, hostname, &addr)) && -#endif - sni) { - char *snihost = Curl_ssl_snihost(data, hostname, NULL); - if(!snihost || !SSL_set_tlsext_host_name(backend->handle, snihost)) { + if(connssl->peer.sni) { + if(!SSL_set_tlsext_host_name(backend->handle, connssl->peer.sni)) { failf(data, "Failed set SNI"); return CURLE_SSL_CONNECT_ERROR; } @@ -3818,6 +3812,7 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, SSL_set_app_data(backend->handle, cf); + connssl->reused_session = FALSE; if(ssl_config->primary.sessionid) { Curl_ssl_sessionid_lock(data); if(!Curl_ssl_getsessionid(cf, data, &ssl_sessionid, NULL)) { @@ -3831,6 +3826,7 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, } /* Informational message */ infof(data, "SSL reusing session ID"); + connssl->reused_session = TRUE; } Curl_ssl_sessionid_unlock(data); } @@ -3991,7 +3987,7 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, Curl_strerror(sockerr, extramsg, sizeof(extramsg)); failf(data, OSSL_PACKAGE " SSL_connect: %s in connection to %s:%d ", extramsg[0] ? extramsg : SSL_ERROR_to_str(detail), - connssl->hostname, connssl->port); + connssl->peer.hostname, connssl->port); return result; } @@ -4002,13 +3998,28 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, } } else { + int psigtype_nid = NID_undef; + const char *negotiated_group_name = NULL; + /* we connected fine, we're not waiting for anything else. */ connssl->connecting_state = ssl_connect_3; +#if (OPENSSL_VERSION_NUMBER >= 0x30000000L) + SSL_get_peer_signature_type_nid(backend->handle, &psigtype_nid); +#if (OPENSSL_VERSION_NUMBER >= 0x30200000L) + negotiated_group_name = SSL_get0_group_name(backend->handle); +#else + negotiated_group_name = + OBJ_nid2sn(SSL_get_negotiated_group(backend->handle) & 0x0000FFFF); +#endif +#endif + /* Informational message */ - infof(data, "SSL connection using %s / %s", + infof(data, "SSL connection using %s / %s / %s / %s", SSL_get_version(backend->handle), - SSL_get_cipher(backend->handle)); + SSL_get_cipher(backend->handle), + negotiated_group_name? negotiated_group_name : "[blank]", + OBJ_nid2sn(psigtype_nid)); #ifdef HAS_ALPN /* Sets data and len to negotiated protocol, len is 0 if no protocol was @@ -4085,6 +4096,75 @@ static CURLcode ossl_pkp_pin_peer_pubkey(struct Curl_easy *data, X509* cert, return result; } +#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && \ + !(defined(LIBRESSL_VERSION_NUMBER) && \ + LIBRESSL_VERSION_NUMBER < 0x3060000fL) && \ + !defined(OPENSSL_IS_BORINGSSL) && \ + !defined(OPENSSL_IS_AWSLC) && \ + !defined(CURL_DISABLE_VERBOSE_STRINGS) +static void infof_certstack(struct Curl_easy *data, const SSL *ssl) +{ + STACK_OF(X509) *certstack; + long verify_result; + int num_cert_levels; + int cert_level; + + verify_result = SSL_get_verify_result(ssl); + if(verify_result != X509_V_OK) + certstack = SSL_get_peer_cert_chain(ssl); + else + certstack = SSL_get0_verified_chain(ssl); + num_cert_levels = sk_X509_num(certstack); + + for(cert_level = 0; cert_level < num_cert_levels; cert_level++) { + char cert_algorithm[80] = ""; + char group_name_final[80] = ""; + const X509_ALGOR *palg_cert = NULL; + const ASN1_OBJECT *paobj_cert = NULL; + X509 *current_cert; + EVP_PKEY *current_pkey; + int key_bits; + int key_sec_bits; + int get_group_name; + const char *type_name; + + current_cert = sk_X509_value(certstack, cert_level); + + X509_get0_signature(NULL, &palg_cert, current_cert); + X509_ALGOR_get0(&paobj_cert, NULL, NULL, palg_cert); + OBJ_obj2txt(cert_algorithm, sizeof(cert_algorithm), paobj_cert, 0); + + current_pkey = X509_get0_pubkey(current_cert); + key_bits = EVP_PKEY_bits(current_pkey); +#if (OPENSSL_VERSION_NUMBER < 0x30000000L) +#define EVP_PKEY_get_security_bits EVP_PKEY_security_bits +#endif + key_sec_bits = EVP_PKEY_get_security_bits(current_pkey); +#if (OPENSSL_VERSION_NUMBER >= 0x30000000L) + { + char group_name[80] = ""; + get_group_name = EVP_PKEY_get_group_name(current_pkey, group_name, + sizeof(group_name), NULL); + msnprintf(group_name_final, sizeof(group_name_final), "/%s", group_name); + } + type_name = EVP_PKEY_get0_type_name(current_pkey); +#else + get_group_name = 0; + type_name = NULL; +#endif + + infof(data, + " Certificate level %d: " + "Public key type %s%s (%d/%d Bits/secBits), signed using %s", + cert_level, type_name ? type_name : "?", + get_group_name == 0 ? "" : group_name_final, + key_bits, key_sec_bits, cert_algorithm); + } +} +#else +#define infof_certstack(data, ssl) +#endif + /* * Get the server cert, verify it and show it, etc., only call failf() if the * 'strict' argument is TRUE as otherwise all this is for informational @@ -4163,8 +4243,8 @@ static CURLcode servercert(struct Curl_cfilter *cf, BIO_free(mem); if(conn_config->verifyhost) { - result = ossl_verifyhost(data, conn, backend->server_cert, - connssl->hostname, connssl->dispname); + result = Curl_ossl_verifyhost(data, conn, &connssl->peer, + backend->server_cert); if(result) { X509_free(backend->server_cert); backend->server_cert = NULL; @@ -4274,9 +4354,12 @@ static CURLcode servercert(struct Curl_cfilter *cf, infof(data, " SSL certificate verify ok."); } + infof_certstack(data, backend->handle); + #if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \ !defined(OPENSSL_NO_OCSP) - if(conn_config->verifystatus) { + if(conn_config->verifystatus && !connssl->reused_session) { + /* don't do this after Session ID reuse */ result = verifystatus(cf, data); if(result) { X509_free(backend->server_cert); @@ -4538,22 +4621,9 @@ static ssize_t ossl_send(struct Curl_cfilter *cf, case SSL_ERROR_SSL: { /* A failure in the SSL library occurred, usually a protocol error. The OpenSSL error queue contains more information on the error. */ - struct Curl_cfilter *cf_ssl_next = Curl_ssl_cf_get_ssl(cf->next); - struct ssl_connect_data *connssl_next = cf_ssl_next? - cf_ssl_next->ctx : NULL; sslerror = ERR_get_error(); - if(ERR_GET_LIB(sslerror) == ERR_LIB_SSL && - ERR_GET_REASON(sslerror) == SSL_R_BIO_NOT_SET && - connssl->state == ssl_connection_complete && - (connssl_next && connssl_next->state == ssl_connection_complete) - ) { - char ver[120]; - (void)ossl_version(ver, sizeof(ver)); - failf(data, "Error: %s does not support double SSL tunneling.", ver); - } - else - failf(data, "SSL_write() error: %s", - ossl_strerror(sslerror, error_buffer, sizeof(error_buffer))); + failf(data, "SSL_write() error: %s", + ossl_strerror(sslerror, error_buffer, sizeof(error_buffer))); *curlcode = CURLE_SEND_ERROR; rc = -1; goto out; @@ -4858,7 +4928,7 @@ const struct Curl_ssl Curl_ssl_openssl = { ossl_cert_status_request, /* cert_status_request */ ossl_connect, /* connect */ ossl_connect_nonblocking, /* connect_nonblocking */ - Curl_ssl_get_select_socks,/* getsock */ + Curl_ssl_adjust_pollset, /* adjust_pollset */ ossl_get_internals, /* get_internals */ ossl_close, /* close_one */ ossl_close_all, /* close_all */ diff --git a/Utilities/cmcurl/lib/vtls/openssl.h b/Utilities/cmcurl/lib/vtls/openssl.h index 950faab..e802363 100644 --- a/Utilities/cmcurl/lib/vtls/openssl.h +++ b/Utilities/cmcurl/lib/vtls/openssl.h @@ -31,24 +31,21 @@ * This header should only be needed to get included by vtls.c, openssl.c * and ngtcp2.c */ +#include <openssl/ossl_typ.h> #include <openssl/ssl.h> #include "urldata.h" -/* - * In an effort to avoid using 'X509 *' here, we instead use the struct - * x509_st version of the type so that we can forward-declare it here without - * having to include <openssl/x509v3.h>. Including that header causes name - * conflicts when libcurl is built with both Schannel and OpenSSL support. - */ -struct x509_st; +#if (OPENSSL_VERSION_NUMBER < 0x30000000L) +#define SSL_get1_peer_certificate SSL_get_peer_certificate +#endif + CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, - struct x509_st *server_cert); + struct ssl_peer *peer, X509 *server_cert); extern const struct Curl_ssl Curl_ssl_openssl; -struct ssl_ctx_st; CURLcode Curl_ossl_set_client_cert(struct Curl_easy *data, - struct ssl_ctx_st *ctx, char *cert_file, + SSL_CTX *ctx, char *cert_file, const struct curl_blob *cert_blob, const char *cert_type, char *key_file, const struct curl_blob *key_blob, @@ -65,5 +62,9 @@ CURLcode Curl_ssl_setup_x509_store(struct Curl_cfilter *cf, struct Curl_easy *data, SSL_CTX *ssl_ctx); +CURLcode Curl_ossl_ctx_configure(struct Curl_cfilter *cf, + struct Curl_easy *data, + SSL_CTX *ssl_ctx); + #endif /* USE_OPENSSL */ #endif /* HEADER_CURL_SSLUSE_H */ diff --git a/Utilities/cmcurl/lib/vtls/rustls.c b/Utilities/cmcurl/lib/vtls/rustls.c index a3e9d96..8751fd9 100644 --- a/Utilities/cmcurl/lib/vtls/rustls.c +++ b/Utilities/cmcurl/lib/vtls/rustls.c @@ -39,6 +39,7 @@ #include "select.h" #include "strerror.h" #include "multiif.h" +#include "connect.h" /* for the connect timeout */ struct rustls_ssl_backend_data { @@ -75,14 +76,6 @@ cr_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data) return backend->data_pending; } -static CURLcode -cr_connect(struct Curl_cfilter *cf UNUSED_PARAM, - struct Curl_easy *data UNUSED_PARAM) -{ - infof(data, "rustls_connect: unimplemented"); - return CURLE_SSL_CONNECT_ERROR; -} - struct io_ctx { struct Curl_cfilter *cf; struct Curl_easy *data; @@ -386,7 +379,7 @@ cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data, /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */ (ca_info_blob ? NULL : conn_config->CAfile); const bool verifypeer = conn_config->verifypeer; - const char *hostname = connssl->hostname; + const char *hostname = connssl->peer.hostname; char errorbuf[256]; size_t errorlen; int result; @@ -458,12 +451,11 @@ cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data, backend->config = rustls_client_config_builder_build(config_builder); DEBUGASSERT(rconn == NULL); { - char *snihost = Curl_ssl_snihost(data, hostname, NULL); - if(!snihost) { - failf(data, "rustls: failed to get SNI"); - return CURLE_SSL_CONNECT_ERROR; - } - result = rustls_client_connection_new(backend->config, snihost, &rconn); + /* rustls claims to manage ip address hostnames as well here. So, + * if we have an SNI, we use it, otherwise we pass the hostname */ + char *server = connssl->peer.sni? + connssl->peer.sni : connssl->peer.hostname; + result = rustls_client_connection_new(backend->config, server, &rconn); } if(result != RUSTLS_RESULT_OK) { rustls_error(result, errorbuf, sizeof(errorbuf), &errorlen); @@ -486,9 +478,20 @@ cr_set_negotiated_alpn(struct Curl_cfilter *cf, struct Curl_easy *data, Curl_alpn_set_negotiated(cf, data, protocol, len); } +/* Given an established network connection, do a TLS handshake. + * + * If `blocking` is true, this function will block until the handshake is + * complete. Otherwise it will return as soon as I/O would block. + * + * For the non-blocking I/O case, this function will set `*done` to true + * once the handshake is complete. This function never reads the value of + * `*done*`. + */ static CURLcode -cr_connect_nonblocking(struct Curl_cfilter *cf, - struct Curl_easy *data, bool *done) +cr_connect_common(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool blocking, + bool *done) { struct ssl_connect_data *const connssl = cf->ctx; curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data); @@ -502,6 +505,8 @@ cr_connect_nonblocking(struct Curl_cfilter *cf, bool wants_write; curl_socket_t writefd; curl_socket_t readfd; + timediff_t timeout_ms; + timediff_t socket_check_timeout; DEBUGASSERT(backend); @@ -539,12 +544,29 @@ cr_connect_nonblocking(struct Curl_cfilter *cf, writefd = wants_write?sockfd:CURL_SOCKET_BAD; readfd = wants_read?sockfd:CURL_SOCKET_BAD; - what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd, 0); + /* check allowed time left */ + timeout_ms = Curl_timeleft(data, NULL, TRUE); + + if(timeout_ms < 0) { + /* no need to continue if time already is up */ + failf(data, "rustls: operation timed out before socket check"); + return CURLE_OPERATION_TIMEDOUT; + } + + socket_check_timeout = blocking?timeout_ms:0; + + what = Curl_socket_check( + readfd, CURL_SOCKET_BAD, writefd, socket_check_timeout); if(what < 0) { /* fatal error */ failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); return CURLE_SSL_CONNECT_ERROR; } + if(blocking && 0 == what) { + failf(data, "rustls connection timeout after %d ms", + socket_check_timeout); + return CURLE_OPERATION_TIMEDOUT; + } if(0 == what) { infof(data, "Curl_socket_check: %s would block", wants_read&&wants_write ? "writing and reading" : @@ -589,32 +611,43 @@ cr_connect_nonblocking(struct Curl_cfilter *cf, DEBUGASSERT(false); } -/* returns a bitmap of flags for this connection's first socket indicating - whether we want to read or write */ -static int -cr_get_select_socks(struct Curl_cfilter *cf, struct Curl_easy *data, - curl_socket_t *socks) +static CURLcode +cr_connect_nonblocking(struct Curl_cfilter *cf, + struct Curl_easy *data, bool *done) { - struct ssl_connect_data *const connssl = cf->ctx; - curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data); - struct rustls_ssl_backend_data *const backend = - (struct rustls_ssl_backend_data *)connssl->backend; - struct rustls_connection *rconn = NULL; + return cr_connect_common(cf, data, false, done); +} - (void)data; - DEBUGASSERT(backend); - rconn = backend->conn; +static CURLcode +cr_connect_blocking(struct Curl_cfilter *cf UNUSED_PARAM, + struct Curl_easy *data UNUSED_PARAM) +{ + bool done; /* unused */ + return cr_connect_common(cf, data, true, &done); +} - if(rustls_connection_wants_write(rconn)) { - socks[0] = sockfd; - return GETSOCK_WRITESOCK(0); - } - if(rustls_connection_wants_read(rconn)) { - socks[0] = sockfd; - return GETSOCK_READSOCK(0); +static void cr_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) +{ + if(!cf->connected) { + curl_socket_t sock = Curl_conn_cf_get_socket(cf->next, data); + struct ssl_connect_data *const connssl = cf->ctx; + struct rustls_ssl_backend_data *const backend = + (struct rustls_ssl_backend_data *)connssl->backend; + struct rustls_connection *rconn = NULL; + + (void)data; + DEBUGASSERT(backend); + rconn = backend->conn; + + if(rustls_connection_wants_write(rconn)) { + Curl_pollset_add_out(data, ps, sock); + } + if(rustls_connection_wants_read(rconn)) { + Curl_pollset_add_in(data, ps, sock); + } } - - return GETSOCK_BLANK; } static void * @@ -675,9 +708,9 @@ const struct Curl_ssl Curl_ssl_rustls = { cr_data_pending, /* data_pending */ Curl_none_random, /* random */ Curl_none_cert_status_request, /* cert_status_request */ - cr_connect, /* connect */ + cr_connect_blocking, /* connect */ cr_connect_nonblocking, /* connect_nonblocking */ - cr_get_select_socks, /* get_select_socks */ + cr_adjust_pollset, /* adjust_pollset */ cr_get_internals, /* get_internals */ cr_close, /* close_one */ Curl_none_close_all, /* close_all */ diff --git a/Utilities/cmcurl/lib/vtls/schannel.c b/Utilities/cmcurl/lib/vtls/schannel.c index 410a5c4..ae7f295 100644 --- a/Utilities/cmcurl/lib/vtls/schannel.c +++ b/Utilities/cmcurl/lib/vtls/schannel.c @@ -1063,17 +1063,12 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) #endif SECURITY_STATUS sspi_status = SEC_E_OK; struct Curl_schannel_cred *old_cred = NULL; - struct in_addr addr; -#ifdef ENABLE_IPV6 - struct in6_addr addr6; -#endif CURLcode result; - const char *hostname = connssl->hostname; DEBUGASSERT(backend); DEBUGF(infof(data, "schannel: SSL/TLS connection with %s port %d (step 1/3)", - hostname, connssl->port)); + connssl->peer.hostname, connssl->port)); if(curlx_verify_windows_version(5, 1, 0, PLATFORM_WINNT, VERSION_LESS_THAN_EQUAL)) { @@ -1154,22 +1149,14 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) /* A hostname associated with the credential is needed by InitializeSecurityContext for SNI and other reasons. */ - snihost = Curl_ssl_snihost(data, hostname, NULL); - if(!snihost) { - failf(data, "Failed to set SNI"); - return CURLE_SSL_CONNECT_ERROR; - } + snihost = connssl->peer.sni? connssl->peer.sni : connssl->peer.hostname; backend->cred->sni_hostname = curlx_convert_UTF8_to_tchar(snihost); if(!backend->cred->sni_hostname) return CURLE_OUT_OF_MEMORY; } /* Warn if SNI is disabled due to use of an IP address */ - if(Curl_inet_pton(AF_INET, hostname, &addr) -#ifdef ENABLE_IPV6 - || Curl_inet_pton(AF_INET6, hostname, &addr6) -#endif - ) { + if(connssl->peer.is_ip_address) { infof(data, "schannel: using IP address, SNI is not supported by OS."); } @@ -1346,7 +1333,7 @@ schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) DEBUGF(infof(data, "schannel: SSL/TLS connection with %s port %d (step 2/3)", - connssl->hostname, connssl->port)); + connssl->peer.hostname, connssl->port)); if(!backend->cred || !backend->ctxt) return CURLE_SSL_CONNECT_ERROR; @@ -1700,7 +1687,7 @@ schannel_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data) DEBUGF(infof(data, "schannel: SSL/TLS connection with %s port %d (step 3/3)", - connssl->hostname, connssl->port)); + connssl->peer.hostname, connssl->port)); if(!backend->cred) return CURLE_SSL_CONNECT_ERROR; @@ -2498,7 +2485,7 @@ static int schannel_shutdown(struct Curl_cfilter *cf, if(backend->ctxt) { infof(data, "schannel: shutting down SSL/TLS connection with %s port %d", - connssl->hostname, connssl->port); + connssl->peer.hostname, connssl->port); } if(backend->cred && backend->ctxt) { @@ -2754,6 +2741,151 @@ static void *schannel_get_internals(struct ssl_connect_data *connssl, return &backend->ctxt->ctxt_handle; } +HCERTSTORE Curl_schannel_get_cached_cert_store(struct Curl_cfilter *cf, + const struct Curl_easy *data) +{ + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + struct Curl_multi *multi = data->multi_easy ? data->multi_easy : data->multi; + const struct curl_blob *ca_info_blob = conn_config->ca_info_blob; + struct schannel_multi_ssl_backend_data *mbackend; + const struct ssl_general_config *cfg = &data->set.general_ssl; + timediff_t timeout_ms; + timediff_t elapsed_ms; + struct curltime now; + unsigned char info_blob_digest[CURL_SHA256_DIGEST_LENGTH]; + + DEBUGASSERT(multi); + + if(!multi || !multi->ssl_backend_data) { + return NULL; + } + + mbackend = (struct schannel_multi_ssl_backend_data *)multi->ssl_backend_data; + if(!mbackend->cert_store) { + return NULL; + } + + /* zero ca_cache_timeout completely disables caching */ + if(!cfg->ca_cache_timeout) { + return NULL; + } + + /* check for cache timeout by using the cached_x509_store_expired timediff + calculation pattern from openssl.c. + negative timeout means retain forever. */ + timeout_ms = cfg->ca_cache_timeout * (timediff_t)1000; + if(timeout_ms >= 0) { + now = Curl_now(); + elapsed_ms = Curl_timediff(now, mbackend->time); + if(elapsed_ms >= timeout_ms) { + return NULL; + } + } + + if(ca_info_blob) { + if(!mbackend->CAinfo_blob_digest) { + return NULL; + } + if(mbackend->CAinfo_blob_size != ca_info_blob->len) { + return NULL; + } + schannel_sha256sum((const unsigned char *)ca_info_blob->data, + ca_info_blob->len, + info_blob_digest, + CURL_SHA256_DIGEST_LENGTH); + if(memcmp(mbackend->CAinfo_blob_digest, + info_blob_digest, + CURL_SHA256_DIGEST_LENGTH)) { + return NULL; + } + } + else { + if(!conn_config->CAfile || !mbackend->CAfile || + strcmp(mbackend->CAfile, conn_config->CAfile)) { + return NULL; + } + } + + return mbackend->cert_store; +} + +bool Curl_schannel_set_cached_cert_store(struct Curl_cfilter *cf, + const struct Curl_easy *data, + HCERTSTORE cert_store) +{ + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + struct Curl_multi *multi = data->multi_easy ? data->multi_easy : data->multi; + const struct curl_blob *ca_info_blob = conn_config->ca_info_blob; + struct schannel_multi_ssl_backend_data *mbackend; + unsigned char *CAinfo_blob_digest = NULL; + size_t CAinfo_blob_size = 0; + char *CAfile = NULL; + + DEBUGASSERT(multi); + + if(!multi) { + return false; + } + + if(!multi->ssl_backend_data) { + multi->ssl_backend_data = + calloc(1, sizeof(struct schannel_multi_ssl_backend_data)); + if(!multi->ssl_backend_data) { + return false; + } + } + + mbackend = (struct schannel_multi_ssl_backend_data *)multi->ssl_backend_data; + + + if(ca_info_blob) { + CAinfo_blob_digest = malloc(CURL_SHA256_DIGEST_LENGTH); + if(!CAinfo_blob_digest) { + return false; + } + schannel_sha256sum((const unsigned char *)ca_info_blob->data, + ca_info_blob->len, + CAinfo_blob_digest, + CURL_SHA256_DIGEST_LENGTH); + CAinfo_blob_size = ca_info_blob->len; + } + else { + if(conn_config->CAfile) { + CAfile = strdup(conn_config->CAfile); + if(!CAfile) { + return false; + } + } + } + + /* free old cache data */ + if(mbackend->cert_store) { + CertCloseStore(mbackend->cert_store, 0); + } + free(mbackend->CAinfo_blob_digest); + free(mbackend->CAfile); + + mbackend->time = Curl_now(); + mbackend->cert_store = cert_store; + mbackend->CAinfo_blob_digest = CAinfo_blob_digest; + mbackend->CAinfo_blob_size = CAinfo_blob_size; + mbackend->CAfile = CAfile; + return true; +} + +static void schannel_free_multi_ssl_backend_data( + struct multi_ssl_backend_data *msbd) +{ + struct schannel_multi_ssl_backend_data *mbackend = + (struct schannel_multi_ssl_backend_data*)msbd; + if(mbackend->cert_store) { + CertCloseStore(mbackend->cert_store, 0); + } + free(mbackend->CAinfo_blob_digest); + free(mbackend->CAfile); + free(mbackend); +} + const struct Curl_ssl Curl_ssl_schannel = { { CURLSSLBACKEND_SCHANNEL, "schannel" }, /* info */ @@ -2777,7 +2909,7 @@ const struct Curl_ssl Curl_ssl_schannel = { Curl_none_cert_status_request, /* cert_status_request */ schannel_connect, /* connect */ schannel_connect_nonblocking, /* connect_nonblocking */ - Curl_ssl_get_select_socks, /* getsock */ + Curl_ssl_adjust_pollset, /* adjust_pollset */ schannel_get_internals, /* get_internals */ schannel_close, /* close_one */ Curl_none_close_all, /* close_all */ @@ -2789,7 +2921,7 @@ const struct Curl_ssl Curl_ssl_schannel = { schannel_sha256sum, /* sha256sum */ NULL, /* associate_connection */ NULL, /* disassociate_connection */ - NULL, /* free_multi_ssl_backend_data */ + schannel_free_multi_ssl_backend_data, /* free_multi_ssl_backend_data */ schannel_recv, /* recv decrypted data */ schannel_send, /* send data to encrypt */ }; diff --git a/Utilities/cmcurl/lib/vtls/schannel_int.h b/Utilities/cmcurl/lib/vtls/schannel_int.h index a128e04..fe7450d 100644 --- a/Utilities/cmcurl/lib/vtls/schannel_int.h +++ b/Utilities/cmcurl/lib/vtls/schannel_int.h @@ -149,5 +149,22 @@ struct schannel_ssl_backend_data { #endif }; +struct schannel_multi_ssl_backend_data { + unsigned char *CAinfo_blob_digest; /* CA info blob digest */ + size_t CAinfo_blob_size; /* CA info blob size */ + char *CAfile; /* CAfile path used to generate + certificate store */ + HCERTSTORE cert_store; /* cached certificate store or + NULL if none */ + struct curltime time; /* when the cached store was created */ +}; + +HCERTSTORE Curl_schannel_get_cached_cert_store(struct Curl_cfilter *cf, + const struct Curl_easy *data); + +bool Curl_schannel_set_cached_cert_store(struct Curl_cfilter *cf, + const struct Curl_easy *data, + HCERTSTORE cert_store); + #endif /* USE_SCHANNEL */ #endif /* HEADER_CURL_SCHANNEL_INT_H */ diff --git a/Utilities/cmcurl/lib/vtls/schannel_verify.c b/Utilities/cmcurl/lib/vtls/schannel_verify.c index a5d5c98..e7c8bc6 100644 --- a/Utilities/cmcurl/lib/vtls/schannel_verify.c +++ b/Utilities/cmcurl/lib/vtls/schannel_verify.c @@ -470,7 +470,7 @@ CURLcode Curl_verify_host(struct Curl_cfilter *cf, CERT_CONTEXT *pCertContextServer = NULL; TCHAR *cert_hostname_buff = NULL; size_t cert_hostname_buff_index = 0; - const char *conn_hostname = connssl->hostname; + const char *conn_hostname = connssl->peer.hostname; size_t hostlen = strlen(conn_hostname); DWORD len = 0; DWORD actual_len = 0; @@ -600,6 +600,7 @@ CURLcode Curl_verify_certificate(struct Curl_cfilter *cf, const CERT_CHAIN_CONTEXT *pChainContext = NULL; HCERTCHAINENGINE cert_chain_engine = NULL; HCERTSTORE trust_store = NULL; + HCERTSTORE own_trust_store = NULL; DEBUGASSERT(BACKEND); @@ -630,31 +631,46 @@ CURLcode Curl_verify_certificate(struct Curl_cfilter *cf, result = CURLE_SSL_CACERT_BADFILE; } else { - /* Open the certificate store */ - trust_store = CertOpenStore(CERT_STORE_PROV_MEMORY, - 0, - (HCRYPTPROV)NULL, - CERT_STORE_CREATE_NEW_FLAG, - NULL); - if(!trust_store) { - char buffer[STRERROR_LEN]; - failf(data, "schannel: failed to create certificate store: %s", - Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer))); - result = CURLE_SSL_CACERT_BADFILE; + /* try cache */ + trust_store = Curl_schannel_get_cached_cert_store(cf, data); + + if(trust_store) { + infof(data, "schannel: reusing certificate store from cache"); } else { - const struct curl_blob *ca_info_blob = conn_config->ca_info_blob; - if(ca_info_blob) { - result = add_certs_data_to_store(trust_store, - (const char *)ca_info_blob->data, - ca_info_blob->len, - "(memory blob)", - data); + /* Open the certificate store */ + trust_store = CertOpenStore(CERT_STORE_PROV_MEMORY, + 0, + (HCRYPTPROV)NULL, + CERT_STORE_CREATE_NEW_FLAG, + NULL); + if(!trust_store) { + char buffer[STRERROR_LEN]; + failf(data, "schannel: failed to create certificate store: %s", + Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer))); + result = CURLE_SSL_CACERT_BADFILE; } else { - result = add_certs_file_to_store(trust_store, - conn_config->CAfile, - data); + const struct curl_blob *ca_info_blob = conn_config->ca_info_blob; + own_trust_store = trust_store; + + if(ca_info_blob) { + result = add_certs_data_to_store(trust_store, + (const char *)ca_info_blob->data, + ca_info_blob->len, + "(memory blob)", + data); + } + else { + result = add_certs_file_to_store(trust_store, + conn_config->CAfile, + data); + } + if(result == CURLE_OK) { + if(Curl_schannel_set_cached_cert_store(cf, data, trust_store)) { + own_trust_store = NULL; + } + } } } } @@ -754,8 +770,8 @@ CURLcode Curl_verify_certificate(struct Curl_cfilter *cf, CertFreeCertificateChainEngine(cert_chain_engine); } - if(trust_store) { - CertCloseStore(trust_store, 0); + if(own_trust_store) { + CertCloseStore(own_trust_store, 0); } if(pChainContext) diff --git a/Utilities/cmcurl/lib/vtls/sectransp.c b/Utilities/cmcurl/lib/vtls/sectransp.c index 3378f76..0a22ff6 100644 --- a/Utilities/cmcurl/lib/vtls/sectransp.c +++ b/Utilities/cmcurl/lib/vtls/sectransp.c @@ -46,8 +46,10 @@ #endif /* __clang__ */ #ifdef __GNUC__ +#pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Waddress" #pragma GCC diagnostic ignored "-Wundef" +#pragma GCC diagnostic ignored "-Wunreachable-code" #endif #include <limits.h> @@ -1013,7 +1015,7 @@ static CURLcode CopyCertSubject(struct Curl_easy *data, } else { size_t cbuf_size = ((size_t)CFStringGetLength(c) * 4) + 1; - cbuf = calloc(cbuf_size, 1); + cbuf = calloc(1, cbuf_size); if(cbuf) { if(!CFStringGetCString(c, cbuf, cbuf_size, kCFStringEncodingUTF8)) { @@ -1651,11 +1653,6 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, const bool verifypeer = conn_config->verifypeer; char * const ssl_cert = ssl_config->primary.clientcert; const struct curl_blob *ssl_cert_blob = ssl_config->primary.cert_blob; -#ifdef ENABLE_IPV6 - struct in6_addr addr; -#else - struct in_addr addr; -#endif /* ENABLE_IPV6 */ char *ciphers; OSStatus err = noErr; #if CURL_BUILD_MAC @@ -2003,13 +2000,9 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, * Both hostname check and SNI require SSLSetPeerDomainName(). * Also: the verifyhost setting influences SNI usage */ if(conn_config->verifyhost) { - size_t snilen; - char *snihost = Curl_ssl_snihost(data, connssl->hostname, &snilen); - if(!snihost) { - failf(data, "Failed to set SNI"); - return CURLE_SSL_CONNECT_ERROR; - } - err = SSLSetPeerDomainName(backend->ssl_ctx, snihost, snilen); + char *server = connssl->peer.sni? + connssl->peer.sni : connssl->peer.hostname; + err = SSLSetPeerDomainName(backend->ssl_ctx, server, strlen(server)); if(err != noErr) { failf(data, "SSL: SSLSetPeerDomainName() failed: OSStatus %d", @@ -2017,11 +2010,7 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, return CURLE_SSL_CONNECT_ERROR; } - if((Curl_inet_pton(AF_INET, connssl->hostname, &addr)) - #ifdef ENABLE_IPV6 - || (Curl_inet_pton(AF_INET6, connssl->hostname, &addr)) - #endif - ) { + if(connssl->peer.is_ip_address) { infof(data, "WARNING: using IP address, SNI is being disabled by " "the OS."); } @@ -2079,7 +2068,7 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, ssl_sessionid = aprintf("%s:%d:%d:%s:%d", ssl_cafile ? ssl_cafile : "(blob memory)", - verifypeer, conn_config->verifyhost, connssl->hostname, + verifypeer, conn_config->verifyhost, connssl->peer.hostname, connssl->port); ssl_sessionid_len = strlen(ssl_sessionid); @@ -2665,7 +2654,7 @@ check_handshake: host name: */ case errSSLHostNameMismatch: failf(data, "SSL certificate peer verification failed, the " - "certificate did not match \"%s\"\n", connssl->dispname); + "certificate did not match \"%s\"\n", connssl->peer.dispname); return CURLE_PEER_FAILED_VERIFICATION; /* Problem with SSL / TLS negotiation */ @@ -2757,7 +2746,7 @@ check_handshake: default: /* May also return codes listed in Security Framework Result Codes */ failf(data, "Unknown SSL protocol error in connection to %s:%d", - connssl->hostname, err); + connssl->peer.hostname, err); break; } return CURLE_SSL_CONNECT_ERROR; @@ -3415,7 +3404,6 @@ again: } *curlcode = CURLE_AGAIN; return -1L; - break; /* errSSLClosedGraceful - server gracefully shut down the SSL session errSSLClosedNoNotify - server hung up on us instead of sending a @@ -3425,7 +3413,6 @@ again: case errSSLClosedNoNotify: *curlcode = CURLE_OK; return 0; - break; /* The below is errSSLPeerAuthCompleted; it's not defined in Leopard's headers */ @@ -3445,7 +3432,6 @@ again: failf(data, "SSLRead() return error %d", err); *curlcode = CURLE_RECV_ERROR; return -1L; - break; } } return (ssize_t)processed; @@ -3483,7 +3469,7 @@ const struct Curl_ssl Curl_ssl_sectransp = { Curl_none_cert_status_request, /* cert_status_request */ sectransp_connect, /* connect */ sectransp_connect_nonblocking, /* connect_nonblocking */ - Curl_ssl_get_select_socks, /* getsock */ + Curl_ssl_adjust_pollset, /* adjust_pollset */ sectransp_get_internals, /* get_internals */ sectransp_close, /* close_one */ Curl_none_close_all, /* close_all */ @@ -3500,6 +3486,10 @@ const struct Curl_ssl Curl_ssl_sectransp = { sectransp_send, /* send data to encrypt */ }; +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + #ifdef __clang__ #pragma clang diagnostic pop #endif diff --git a/Utilities/cmcurl/lib/vtls/vtls.c b/Utilities/cmcurl/lib/vtls/vtls.c index 494b660..34eda3e 100644 --- a/Utilities/cmcurl/lib/vtls/vtls.c +++ b/Utilities/cmcurl/lib/vtls/vtls.c @@ -67,6 +67,7 @@ #include "warnless.h" #include "curl_base64.h" #include "curl_printf.h" +#include "inet_pton.h" #include "strdup.h" /* The last #include files should be: */ @@ -131,9 +132,6 @@ static bool blobcmp(struct curl_blob *first, struct curl_blob *second) } #ifdef USE_SSL -static const struct alpn_spec ALPN_SPEC_H10 = { - { ALPN_HTTP_1_0 }, 1 -}; static const struct alpn_spec ALPN_SPEC_H11 = { { ALPN_HTTP_1_1 }, 1 }; @@ -147,51 +145,83 @@ static const struct alpn_spec *alpn_get_spec(int httpwant, bool use_alpn) { if(!use_alpn) return NULL; - if(httpwant == CURL_HTTP_VERSION_1_0) - return &ALPN_SPEC_H10; #ifdef USE_HTTP2 if(httpwant >= CURL_HTTP_VERSION_2) return &ALPN_SPEC_H2_H11; +#else + (void)httpwant; #endif + /* Use the ALPN protocol "http/1.1" for HTTP/1.x. + Avoid "http/1.0" because some servers don't support it. */ return &ALPN_SPEC_H11; } #endif /* USE_SSL */ -bool -Curl_ssl_config_matches(struct ssl_primary_config *data, - struct ssl_primary_config *needle) -{ - if((data->version == needle->version) && - (data->version_max == needle->version_max) && - (data->ssl_options == needle->ssl_options) && - (data->verifypeer == needle->verifypeer) && - (data->verifyhost == needle->verifyhost) && - (data->verifystatus == needle->verifystatus) && - blobcmp(data->cert_blob, needle->cert_blob) && - blobcmp(data->ca_info_blob, needle->ca_info_blob) && - blobcmp(data->issuercert_blob, needle->issuercert_blob) && - Curl_safecmp(data->CApath, needle->CApath) && - Curl_safecmp(data->CAfile, needle->CAfile) && - Curl_safecmp(data->issuercert, needle->issuercert) && - Curl_safecmp(data->clientcert, needle->clientcert) && +void Curl_ssl_easy_config_init(struct Curl_easy *data) +{ + /* + * libcurl 7.10 introduced SSL verification *by default*! This needs to be + * switched off unless wanted. + */ + data->set.ssl.primary.verifypeer = TRUE; + data->set.ssl.primary.verifyhost = TRUE; + data->set.ssl.primary.sessionid = TRUE; /* session ID caching by default */ +#ifndef CURL_DISABLE_PROXY + data->set.proxy_ssl = data->set.ssl; +#endif +} + +static bool +match_ssl_primary_config(struct Curl_easy *data, + struct ssl_primary_config *c1, + struct ssl_primary_config *c2) +{ + (void)data; + if((c1->version == c2->version) && + (c1->version_max == c2->version_max) && + (c1->ssl_options == c2->ssl_options) && + (c1->verifypeer == c2->verifypeer) && + (c1->verifyhost == c2->verifyhost) && + (c1->verifystatus == c2->verifystatus) && + blobcmp(c1->cert_blob, c2->cert_blob) && + blobcmp(c1->ca_info_blob, c2->ca_info_blob) && + blobcmp(c1->issuercert_blob, c2->issuercert_blob) && + Curl_safecmp(c1->CApath, c2->CApath) && + Curl_safecmp(c1->CAfile, c2->CAfile) && + Curl_safecmp(c1->issuercert, c2->issuercert) && + Curl_safecmp(c1->clientcert, c2->clientcert) && #ifdef USE_TLS_SRP - !Curl_timestrcmp(data->username, needle->username) && - !Curl_timestrcmp(data->password, needle->password) && + !Curl_timestrcmp(c1->username, c2->username) && + !Curl_timestrcmp(c1->password, c2->password) && #endif - strcasecompare(data->cipher_list, needle->cipher_list) && - strcasecompare(data->cipher_list13, needle->cipher_list13) && - strcasecompare(data->curves, needle->curves) && - strcasecompare(data->CRLfile, needle->CRLfile) && - strcasecompare(data->pinned_key, needle->pinned_key)) + strcasecompare(c1->cipher_list, c2->cipher_list) && + strcasecompare(c1->cipher_list13, c2->cipher_list13) && + strcasecompare(c1->curves, c2->curves) && + strcasecompare(c1->CRLfile, c2->CRLfile) && + strcasecompare(c1->pinned_key, c2->pinned_key)) return TRUE; return FALSE; } -bool -Curl_clone_primary_ssl_config(struct ssl_primary_config *source, - struct ssl_primary_config *dest) +bool Curl_ssl_conn_config_match(struct Curl_easy *data, + struct connectdata *candidate, + bool proxy) +{ +#ifndef CURL_DISABLE_PROXY + if(proxy) + return match_ssl_primary_config(data, &data->set.proxy_ssl.primary, + &candidate->proxy_ssl_config); +#else + (void)proxy; +#endif + return match_ssl_primary_config(data, &data->set.ssl.primary, + &candidate->ssl_config); +} + +static bool clone_ssl_primary_config(struct ssl_primary_config *source, + struct ssl_primary_config *dest) { dest->version = source->version; dest->version_max = source->version_max; @@ -221,7 +251,7 @@ Curl_clone_primary_ssl_config(struct ssl_primary_config *source, return TRUE; } -void Curl_free_primary_ssl_config(struct ssl_primary_config *sslc) +static void Curl_free_primary_ssl_config(struct ssl_primary_config *sslc) { Curl_safefree(sslc->CApath); Curl_safefree(sslc->CAfile); @@ -241,6 +271,111 @@ void Curl_free_primary_ssl_config(struct ssl_primary_config *sslc) #endif } +CURLcode Curl_ssl_easy_config_complete(struct Curl_easy *data) +{ + data->set.ssl.primary.CApath = data->set.str[STRING_SSL_CAPATH]; + data->set.ssl.primary.CAfile = data->set.str[STRING_SSL_CAFILE]; + data->set.ssl.primary.CRLfile = data->set.str[STRING_SSL_CRLFILE]; + data->set.ssl.primary.issuercert = data->set.str[STRING_SSL_ISSUERCERT]; + data->set.ssl.primary.issuercert_blob = data->set.blobs[BLOB_SSL_ISSUERCERT]; + data->set.ssl.primary.cipher_list = + data->set.str[STRING_SSL_CIPHER_LIST]; + data->set.ssl.primary.cipher_list13 = + data->set.str[STRING_SSL_CIPHER13_LIST]; + data->set.ssl.primary.pinned_key = + data->set.str[STRING_SSL_PINNEDPUBLICKEY]; + data->set.ssl.primary.cert_blob = data->set.blobs[BLOB_CERT]; + data->set.ssl.primary.ca_info_blob = data->set.blobs[BLOB_CAINFO]; + data->set.ssl.primary.curves = data->set.str[STRING_SSL_EC_CURVES]; +#ifdef USE_TLS_SRP + data->set.ssl.primary.username = data->set.str[STRING_TLSAUTH_USERNAME]; + data->set.ssl.primary.password = data->set.str[STRING_TLSAUTH_PASSWORD]; +#endif + data->set.ssl.cert_type = data->set.str[STRING_CERT_TYPE]; + data->set.ssl.key = data->set.str[STRING_KEY]; + data->set.ssl.key_type = data->set.str[STRING_KEY_TYPE]; + data->set.ssl.key_passwd = data->set.str[STRING_KEY_PASSWD]; + data->set.ssl.primary.clientcert = data->set.str[STRING_CERT]; + data->set.ssl.key_blob = data->set.blobs[BLOB_KEY]; + +#ifndef CURL_DISABLE_PROXY + data->set.proxy_ssl.primary.CApath = data->set.str[STRING_SSL_CAPATH_PROXY]; + data->set.proxy_ssl.primary.CAfile = data->set.str[STRING_SSL_CAFILE_PROXY]; + data->set.proxy_ssl.primary.cipher_list = + data->set.str[STRING_SSL_CIPHER_LIST_PROXY]; + data->set.proxy_ssl.primary.cipher_list13 = + data->set.str[STRING_SSL_CIPHER13_LIST_PROXY]; + data->set.proxy_ssl.primary.pinned_key = + data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY]; + data->set.proxy_ssl.primary.cert_blob = data->set.blobs[BLOB_CERT_PROXY]; + data->set.proxy_ssl.primary.ca_info_blob = + data->set.blobs[BLOB_CAINFO_PROXY]; + data->set.proxy_ssl.primary.issuercert = + data->set.str[STRING_SSL_ISSUERCERT_PROXY]; + data->set.proxy_ssl.primary.issuercert_blob = + data->set.blobs[BLOB_SSL_ISSUERCERT_PROXY]; + data->set.proxy_ssl.primary.CRLfile = + data->set.str[STRING_SSL_CRLFILE_PROXY]; + data->set.proxy_ssl.cert_type = data->set.str[STRING_CERT_TYPE_PROXY]; + data->set.proxy_ssl.key = data->set.str[STRING_KEY_PROXY]; + data->set.proxy_ssl.key_type = data->set.str[STRING_KEY_TYPE_PROXY]; + data->set.proxy_ssl.key_passwd = data->set.str[STRING_KEY_PASSWD_PROXY]; + data->set.proxy_ssl.primary.clientcert = data->set.str[STRING_CERT_PROXY]; + data->set.proxy_ssl.key_blob = data->set.blobs[BLOB_KEY_PROXY]; +#ifdef USE_TLS_SRP + data->set.proxy_ssl.primary.username = + data->set.str[STRING_TLSAUTH_USERNAME_PROXY]; + data->set.proxy_ssl.primary.password = + data->set.str[STRING_TLSAUTH_PASSWORD_PROXY]; +#endif +#endif /* CURL_DISABLE_PROXY */ + + return CURLE_OK; +} + +CURLcode Curl_ssl_conn_config_init(struct Curl_easy *data, + struct connectdata *conn) +{ + /* Clone "primary" SSL configurations from the esay handle to + * the connection. They are used for connection cache matching and + * probably outlive the easy handle */ + if(!clone_ssl_primary_config(&data->set.ssl.primary, &conn->ssl_config)) + return CURLE_OUT_OF_MEMORY; +#ifndef CURL_DISABLE_PROXY + if(!clone_ssl_primary_config(&data->set.proxy_ssl.primary, + &conn->proxy_ssl_config)) + return CURLE_OUT_OF_MEMORY; +#endif + return CURLE_OK; +} + +void Curl_ssl_conn_config_cleanup(struct connectdata *conn) +{ + Curl_free_primary_ssl_config(&conn->ssl_config); +#ifndef CURL_DISABLE_PROXY + Curl_free_primary_ssl_config(&conn->proxy_ssl_config); +#endif +} + +void Curl_ssl_conn_config_update(struct Curl_easy *data, bool for_proxy) +{ + /* May be called on an easy that has no connection yet */ + if(data->conn) { + struct ssl_primary_config *src, *dest; +#ifndef CURL_DISABLE_PROXY + src = for_proxy? &data->set.proxy_ssl.primary : &data->set.ssl.primary; + dest = for_proxy? &data->conn->proxy_ssl_config : &data->conn->ssl_config; +#else + (void)for_proxy; + src = &data->set.ssl.primary; + dest = &data->conn->ssl_config; +#endif + dest->verifyhost = src->verifyhost; + dest->verifypeer = src->verifypeer; + dest->verifystatus = src->verifystatus; + } +} + #ifdef USE_SSL static int multissl_setup(const struct Curl_ssl *backend); #endif @@ -432,7 +567,7 @@ bool Curl_ssl_getsessionid(struct Curl_cfilter *cf, if(!check->sessionid) /* not session ID means blank entry */ continue; - if(strcasecompare(connssl->hostname, check->name) && + if(strcasecompare(connssl->peer.hostname, check->name) && ((!cf->conn->bits.conn_to_host && !check->conn_to_host) || (cf->conn->bits.conn_to_host && check->conn_to_host && strcasecompare(cf->conn->conn_to_host.name, check->conn_to_host))) && @@ -441,7 +576,7 @@ bool Curl_ssl_getsessionid(struct Curl_cfilter *cf, cf->conn->conn_to_port == check->conn_to_port)) && (connssl->port == check->remote_port) && strcasecompare(cf->conn->handler->scheme, check->scheme) && - Curl_ssl_config_matches(conn_config, &check->ssl_config)) { + match_ssl_primary_config(data, conn_config, &check->ssl_config)) { /* yes, we have a session ID! */ (*general_age)++; /* increase general age */ check->age = *general_age; /* set this as used in this age */ @@ -456,7 +591,8 @@ bool Curl_ssl_getsessionid(struct Curl_cfilter *cf, DEBUGF(infof(data, "%s Session ID in cache for %s %s://%s:%d", no_match? "Didn't find": "Found", Curl_ssl_cf_is_proxy(cf) ? "proxy" : "host", - cf->conn->handler->scheme, connssl->hostname, connssl->port)); + cf->conn->handler->scheme, connssl->peer.hostname, + connssl->port)); return no_match; } @@ -532,7 +668,7 @@ CURLcode Curl_ssl_addsessionid(struct Curl_cfilter *cf, (void)ssl_config; DEBUGASSERT(ssl_config->primary.sessionid); - clone_host = strdup(connssl->hostname); + clone_host = strdup(connssl->peer.hostname); if(!clone_host) return CURLE_OUT_OF_MEMORY; /* bail out */ @@ -590,7 +726,7 @@ CURLcode Curl_ssl_addsessionid(struct Curl_cfilter *cf, store->remote_port = connssl->port; store->scheme = cf->conn->handler->scheme; - if(!Curl_clone_primary_ssl_config(conn_config, &store->ssl_config)) { + if(!clone_ssl_primary_config(conn_config, &store->ssl_config)) { Curl_free_primary_ssl_config(&store->ssl_config); store->sessionid = NULL; /* let caller free sessionid */ free(clone_host); @@ -629,22 +765,21 @@ void Curl_ssl_close_all(struct Curl_easy *data) Curl_ssl->close_all(data); } -int Curl_ssl_get_select_socks(struct Curl_cfilter *cf, struct Curl_easy *data, - curl_socket_t *socks) +void Curl_ssl_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data, + struct easy_pollset *ps) { - struct ssl_connect_data *connssl = cf->ctx; - curl_socket_t sock = Curl_conn_cf_get_socket(cf->next, data); - - if(sock == CURL_SOCKET_BAD) - return GETSOCK_BLANK; - - if(connssl->connecting_state == ssl_connect_2_writing) { - /* we are only interested in writing */ - socks[0] = sock; - return GETSOCK_WRITESOCK(0); + if(!cf->connected) { + struct ssl_connect_data *connssl = cf->ctx; + curl_socket_t sock = Curl_conn_cf_get_socket(cf->next, data); + if(sock != CURL_SOCKET_BAD) { + if(connssl->connecting_state == ssl_connect_2_writing) { + Curl_pollset_set_out_only(data, ps, sock); + } + else { + Curl_pollset_set_in_only(data, ps, sock); + } + } } - socks[0] = sock; - return GETSOCK_READSOCK(0); } /* Selects an SSL crypto engine @@ -786,32 +921,6 @@ CURLcode Curl_ssl_random(struct Curl_easy *data, } /* - * Curl_ssl_snihost() converts the input host name to a suitable SNI name put - * in data->state.buffer. Returns a pointer to the name (or NULL if a problem) - * and stores the new length in 'olen'. - * - * SNI fields must not have any trailing dot and while RFC 6066 section 3 says - * the SNI field is case insensitive, browsers always send the data lowercase - * and subsequently there are numerous servers out there that don't work - * unless the name is lowercased. - */ - -char *Curl_ssl_snihost(struct Curl_easy *data, const char *host, size_t *olen) -{ - size_t len = strlen(host); - if(len && (host[len-1] == '.')) - len--; - if(len >= data->set.buffer_size) - return NULL; - - Curl_strntolower(data->state.buffer, host, len); - data->state.buffer[len] = 0; - if(olen) - *olen = len; - return data->state.buffer; -} - -/* * Public key pem to der conversion */ @@ -1156,13 +1265,13 @@ static CURLcode multissl_connect_nonblocking(struct Curl_cfilter *cf, return Curl_ssl->connect_nonblocking(cf, data, done); } -static int multissl_get_select_socks(struct Curl_cfilter *cf, +static void multissl_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data, - curl_socket_t *socks) + struct easy_pollset *ps) { if(multissl_setup(NULL)) - return 0; - return Curl_ssl->get_select_socks(cf, data, socks); + return; + Curl_ssl->adjust_pollset(cf, data, ps); } static void *multissl_get_internals(struct ssl_connect_data *connssl, @@ -1214,7 +1323,7 @@ static const struct Curl_ssl Curl_ssl_multi = { Curl_none_cert_status_request, /* cert_status_request */ multissl_connect, /* connect */ multissl_connect_nonblocking, /* connect_nonblocking */ - multissl_get_select_socks, /* getsock */ + multissl_adjust_pollset, /* adjust_pollset */ multissl_get_internals, /* get_internals */ multissl_close, /* close_one */ Curl_none_close_all, /* close_all */ @@ -1409,12 +1518,14 @@ CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name, #ifdef USE_SSL -static void free_hostname(struct ssl_connect_data *connssl) +void Curl_ssl_peer_cleanup(struct ssl_peer *peer) { - if(connssl->dispname != connssl->hostname) - free(connssl->dispname); - free(connssl->hostname); - connssl->hostname = connssl->dispname = NULL; + if(peer->dispname != peer->hostname) + free(peer->dispname); + free(peer->sni); + free(peer->hostname); + peer->hostname = peer->sni = peer->dispname = NULL; + peer->is_ip_address = FALSE; } static void cf_close(struct Curl_cfilter *cf, struct Curl_easy *data) @@ -1423,12 +1534,26 @@ static void cf_close(struct Curl_cfilter *cf, struct Curl_easy *data) if(connssl) { Curl_ssl->close(cf, data); connssl->state = ssl_connection_none; - free_hostname(connssl); + Curl_ssl_peer_cleanup(&connssl->peer); } cf->connected = FALSE; } -static CURLcode reinit_hostname(struct Curl_cfilter *cf) +static int is_ip_address(const char *hostname) +{ +#ifdef ENABLE_IPV6 + struct in6_addr addr; +#else + struct in_addr addr; +#endif + return (hostname && hostname[0] && (Curl_inet_pton(AF_INET, hostname, &addr) +#ifdef ENABLE_IPV6 + || Curl_inet_pton(AF_INET6, hostname, &addr) +#endif + )); +} + +CURLcode Curl_ssl_peer_init(struct ssl_peer *peer, struct Curl_cfilter *cf) { struct ssl_connect_data *connssl = cf->ctx; const char *ehostname, *edispname; @@ -1454,23 +1579,43 @@ static CURLcode reinit_hostname(struct Curl_cfilter *cf) } /* change if ehostname changed */ - if(ehostname && (!connssl->hostname - || strcmp(ehostname, connssl->hostname))) { - free_hostname(connssl); - connssl->hostname = strdup(ehostname); - if(!connssl->hostname) { - free_hostname(connssl); + if(ehostname && (!peer->hostname + || strcmp(ehostname, peer->hostname))) { + Curl_ssl_peer_cleanup(peer); + peer->hostname = strdup(ehostname); + if(!peer->hostname) { + Curl_ssl_peer_cleanup(peer); return CURLE_OUT_OF_MEMORY; } if(!edispname || !strcmp(ehostname, edispname)) - connssl->dispname = connssl->hostname; + peer->dispname = peer->hostname; else { - connssl->dispname = strdup(edispname); - if(!connssl->dispname) { - free_hostname(connssl); + peer->dispname = strdup(edispname); + if(!peer->dispname) { + Curl_ssl_peer_cleanup(peer); return CURLE_OUT_OF_MEMORY; } } + + peer->sni = NULL; + peer->is_ip_address = is_ip_address(peer->hostname)? TRUE : FALSE; + if(peer->hostname[0] && !peer->is_ip_address) { + /* not an IP address, normalize according to RCC 6066 ch. 3, + * max len of SNI is 2^16-1, no trailing dot */ + size_t len = strlen(peer->hostname); + if(len && (peer->hostname[len-1] == '.')) + len--; + if(len < USHRT_MAX) { + peer->sni = calloc(1, len + 1); + if(!peer->sni) { + Curl_ssl_peer_cleanup(peer); + return CURLE_OUT_OF_MEMORY; + } + Curl_strntolower(peer->sni, peer->hostname, len); + peer->sni[len] = 0; + } + } + } connssl->port = eport; return CURLE_OK; @@ -1525,7 +1670,7 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf, goto out; *done = FALSE; - result = reinit_hostname(cf); + result = Curl_ssl_peer_init(&connssl->peer, cf); if(result) goto out; @@ -1599,22 +1744,17 @@ static ssize_t ssl_cf_recv(struct Curl_cfilter *cf, return nread; } -static int ssl_cf_get_select_socks(struct Curl_cfilter *cf, +static void ssl_cf_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data, - curl_socket_t *socks) + struct easy_pollset *ps) { struct cf_call_data save; - int fds = GETSOCK_BLANK; - if(!cf->next->connected) { - fds = cf->next->cft->get_select_socks(cf->next, data, socks); - } - else if(!cf->connected) { + if(!cf->connected) { CF_DATA_SAVE(save, cf, data); - fds = Curl_ssl->get_select_socks(cf, data, socks); + Curl_ssl->adjust_pollset(cf, data, ps); CF_DATA_RESTORE(cf, save); } - return fds; } static CURLcode ssl_cf_cntrl(struct Curl_cfilter *cf, @@ -1705,7 +1845,7 @@ struct Curl_cftype Curl_cft_ssl = { ssl_cf_connect, ssl_cf_close, Curl_cf_def_get_host, - ssl_cf_get_select_socks, + ssl_cf_adjust_pollset, ssl_cf_data_pending, ssl_cf_send, ssl_cf_recv, @@ -1723,7 +1863,7 @@ struct Curl_cftype Curl_cft_ssl_proxy = { ssl_cf_connect, ssl_cf_close, Curl_cf_def_get_host, - ssl_cf_get_select_socks, + ssl_cf_adjust_pollset, ssl_cf_data_pending, ssl_cf_send, ssl_cf_recv, @@ -1837,6 +1977,16 @@ bool Curl_ssl_supports(struct Curl_easy *data, int option) return (Curl_ssl->supports & option)? TRUE : FALSE; } +static struct Curl_cfilter *get_ssl_filter(struct Curl_cfilter *cf) +{ + for(; cf; cf = cf->next) { + if(cf->cft == &Curl_cft_ssl || cf->cft == &Curl_cft_ssl_proxy) + return cf; + } + return NULL; +} + + void *Curl_ssl_get_internals(struct Curl_easy *data, int sockindex, CURLINFO info, int n) { @@ -1844,8 +1994,8 @@ void *Curl_ssl_get_internals(struct Curl_easy *data, int sockindex, (void)n; if(data->conn) { struct Curl_cfilter *cf; - /* get first filter in chain, if any is present */ - cf = Curl_ssl_cf_get_ssl(data->conn->cfilter[sockindex]); + /* get first SSL filter in chain, if any is present */ + cf = get_ssl_filter(data->conn->cfilter[sockindex]); if(cf) { struct cf_call_data save; CF_DATA_SAVE(save, cf, data); @@ -1875,23 +2025,6 @@ CURLcode Curl_ssl_cfilter_remove(struct Curl_easy *data, return result; } -static struct Curl_cfilter *get_ssl_cf_engaged(struct connectdata *conn, - int sockindex) -{ - struct Curl_cfilter *cf, *lowest_ssl_cf = NULL; - - for(cf = conn->cfilter[sockindex]; cf; cf = cf->next) { - if(cf->cft == &Curl_cft_ssl || cf->cft == &Curl_cft_ssl_proxy) { - lowest_ssl_cf = cf; - if(cf->connected || (cf->next && cf->next->connected)) { - /* connected or about to start */ - return cf; - } - } - } - return lowest_ssl_cf; -} - bool Curl_ssl_cf_is_proxy(struct Curl_cfilter *cf) { return (cf->cft == &Curl_cft_ssl_proxy); @@ -1908,17 +2041,6 @@ Curl_ssl_cf_get_config(struct Curl_cfilter *cf, struct Curl_easy *data) #endif } -struct ssl_config_data * -Curl_ssl_get_config(struct Curl_easy *data, int sockindex) -{ - struct Curl_cfilter *cf; - - (void)data; - DEBUGASSERT(data->conn); - cf = get_ssl_cf_engaged(data->conn, sockindex); - return cf? Curl_ssl_cf_get_config(cf, data) : &data->set.ssl; -} - struct ssl_primary_config * Curl_ssl_cf_get_primary_config(struct Curl_cfilter *cf) { @@ -1930,15 +2052,6 @@ Curl_ssl_cf_get_primary_config(struct Curl_cfilter *cf) #endif } -struct Curl_cfilter *Curl_ssl_cf_get_ssl(struct Curl_cfilter *cf) -{ - for(; cf; cf = cf->next) { - if(cf->cft == &Curl_cft_ssl || cf->cft == &Curl_cft_ssl_proxy) - return cf; - } - return NULL; -} - CURLcode Curl_alpn_to_proto_buf(struct alpn_proto_buf *buf, const struct alpn_spec *spec) { @@ -2005,10 +2118,6 @@ CURLcode Curl_alpn_set_negotiated(struct Curl_cfilter *cf, !memcmp(ALPN_HTTP_1_1, proto, ALPN_HTTP_1_1_LENGTH)) { *palpn = CURL_HTTP_VERSION_1_1; } - else if(proto_len == ALPN_HTTP_1_0_LENGTH && - !memcmp(ALPN_HTTP_1_0, proto, ALPN_HTTP_1_0_LENGTH)) { - *palpn = CURL_HTTP_VERSION_1_0; - } #ifdef USE_HTTP2 else if(proto_len == ALPN_H2_LENGTH && !memcmp(ALPN_H2, proto, ALPN_H2_LENGTH)) { diff --git a/Utilities/cmcurl/lib/vtls/vtls.h b/Utilities/cmcurl/lib/vtls/vtls.h index 8ad1cf6..f1856bd 100644 --- a/Utilities/cmcurl/lib/vtls/vtls.h +++ b/Utilities/cmcurl/lib/vtls/vtls.h @@ -65,15 +65,54 @@ CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name, #define CURL_SHA256_DIGEST_LENGTH 32 /* fixed size */ #endif -char *Curl_ssl_snihost(struct Curl_easy *data, const char *host, size_t *olen); -bool Curl_ssl_config_matches(struct ssl_primary_config *data, - struct ssl_primary_config *needle); -bool Curl_clone_primary_ssl_config(struct ssl_primary_config *source, - struct ssl_primary_config *dest); -void Curl_free_primary_ssl_config(struct ssl_primary_config *sslc); - curl_sslbackend Curl_ssl_backend(void); +/** + * Init ssl config for a new easy handle. + */ +void Curl_ssl_easy_config_init(struct Curl_easy *data); + +/** + * Init the `data->set.ssl` and `data->set.proxy_ssl` for + * connection matching use. + */ +CURLcode Curl_ssl_easy_config_complete(struct Curl_easy *data); + +/** + * Init SSL configs (main + proxy) for a new connection from the easy handle. + */ +CURLcode Curl_ssl_conn_config_init(struct Curl_easy *data, + struct connectdata *conn); + +/** + * Free allocated resources in SSL configs (main + proxy) for + * the given connection. + */ +void Curl_ssl_conn_config_cleanup(struct connectdata *conn); + +/** + * Return TRUE iff SSL configuration from `conn` is functionally the + * same as the one on `candidate`. + * @param proxy match the proxy SSL config or the main one + */ +bool Curl_ssl_conn_config_match(struct Curl_easy *data, + struct connectdata *candidate, + bool proxy); + +/* Update certain connection SSL config flags after they have + * been changed on the easy handle. Will work for `verifypeer`, + * `verifyhost` and `verifystatus`. */ +void Curl_ssl_conn_config_update(struct Curl_easy *data, bool for_proxy); + +/** + * Init SSL peer information for filter. Can be called repeatedly. + */ +CURLcode Curl_ssl_peer_init(struct ssl_peer *peer, struct Curl_cfilter *cf); +/** + * Free all allocated data and reset peer information. + */ +void Curl_ssl_peer_cleanup(struct ssl_peer *peer); + #ifdef USE_SSL int Curl_ssl_init(void); void Curl_ssl_cleanup(void); @@ -160,18 +199,6 @@ CURLcode Curl_cf_ssl_proxy_insert_after(struct Curl_cfilter *cf_at, #endif /* !CURL_DISABLE_PROXY */ /** - * Get the SSL configuration that is used on the connection. - * This returns NULL if no SSL is configured. - * Otherwise it returns the config of the first (highest) one that is - * either connected, in handshake or about to start - * (e.g. all filters below it are connected). If SSL filters are present, - * but neither can start operating, return the config of the lowest one - * that will first come into effect when connecting. - */ -struct ssl_config_data *Curl_ssl_get_config(struct Curl_easy *data, - int sockindex); - -/** * True iff the underlying SSL implementation supports the option. * Option is one of the defined SSLSUPP_* values. * `data` maybe NULL for the features of the default implementation. @@ -188,6 +215,18 @@ bool Curl_ssl_supports(struct Curl_easy *data, int ssl_option); void *Curl_ssl_get_internals(struct Curl_easy *data, int sockindex, CURLINFO info, int n); +/** + * Get the ssl_config_data in `data` that is relevant for cfilter `cf`. + */ +struct ssl_config_data *Curl_ssl_cf_get_config(struct Curl_cfilter *cf, + struct Curl_easy *data); + +/** + * Get the primary config relevant for the filter from its connection. + */ +struct ssl_primary_config * + Curl_ssl_cf_get_primary_config(struct Curl_cfilter *cf); + extern struct Curl_cftype Curl_cft_ssl; extern struct Curl_cftype Curl_cft_ssl_proxy; @@ -209,8 +248,9 @@ extern struct Curl_cftype Curl_cft_ssl_proxy; #define Curl_ssl_get_internals(a,b,c,d) NULL #define Curl_ssl_supports(a,b) FALSE #define Curl_ssl_cfilter_add(a,b,c) CURLE_NOT_BUILT_IN -#define Curl_ssl_get_config(a,b) NULL #define Curl_ssl_cfilter_remove(a,b) CURLE_OK +#define Curl_ssl_cf_get_config(a,b) NULL +#define Curl_ssl_cf_get_primary_config(a) NULL #endif #endif /* HEADER_CURL_VTLS_H */ diff --git a/Utilities/cmcurl/lib/vtls/vtls_int.h b/Utilities/cmcurl/lib/vtls/vtls_int.h index a6e4544..af7ae55 100644 --- a/Utilities/cmcurl/lib/vtls/vtls_int.h +++ b/Utilities/cmcurl/lib/vtls/vtls_int.h @@ -32,8 +32,6 @@ /* see https://www.iana.org/assignments/tls-extensiontype-values/ */ #define ALPN_HTTP_1_1_LENGTH 8 #define ALPN_HTTP_1_1 "http/1.1" -#define ALPN_HTTP_1_0_LENGTH 8 -#define ALPN_HTTP_1_0 "http/1.0" #define ALPN_H2_LENGTH 2 #define ALPN_H2 "h2" #define ALPN_H3_LENGTH 2 @@ -70,14 +68,14 @@ CURLcode Curl_alpn_set_negotiated(struct Curl_cfilter *cf, struct ssl_connect_data { ssl_connection_state state; ssl_connect_state connecting_state; - char *hostname; /* hostname for verification */ - char *dispname; /* display version of hostname */ + struct ssl_peer peer; const struct alpn_spec *alpn; /* ALPN to use or NULL for none */ void *backend; /* vtls backend specific props */ struct cf_call_data call_data; /* data handle used in current call */ struct curltime handshake_done; /* time when handshake finished */ int port; /* remote port at origin */ BIT(use_alpn); /* if ALPN shall be used in handshake */ + BIT(reused_session); /* session-ID was reused for this */ }; @@ -118,14 +116,11 @@ struct Curl_ssl { struct Curl_easy *data, bool *done); - /* If the SSL backend wants to read or write on this connection during a - handshake, set socks[0] to the connection's FIRSTSOCKET, and return - a bitmap indicating read or write with GETSOCK_WRITESOCK(0) or - GETSOCK_READSOCK(0). Otherwise return GETSOCK_BLANK. - Mandatory. */ - int (*get_select_socks)(struct Curl_cfilter *cf, struct Curl_easy *data, - curl_socket_t *socks); - + /* During handshake, adjust the pollset to include the socket + * for POLLOUT or POLLIN as needed. + * Mandatory. */ + void (*adjust_pollset)(struct Curl_cfilter *cf, struct Curl_easy *data, + struct easy_pollset *ps); void *(*get_internals)(struct ssl_connect_data *connssl, CURLINFO info); void (*close)(struct Curl_cfilter *cf, struct Curl_easy *data); void (*close_all)(struct Curl_easy *data); @@ -169,25 +164,8 @@ CURLcode Curl_none_set_engine(struct Curl_easy *data, const char *engine); CURLcode Curl_none_set_engine_default(struct Curl_easy *data); struct curl_slist *Curl_none_engines_list(struct Curl_easy *data); bool Curl_none_false_start(void); -int Curl_ssl_get_select_socks(struct Curl_cfilter *cf, struct Curl_easy *data, - curl_socket_t *socks); - -/** - * Get the ssl_config_data in `data` that is relevant for cfilter `cf`. - */ -struct ssl_config_data *Curl_ssl_cf_get_config(struct Curl_cfilter *cf, - struct Curl_easy *data); - -/** - * Get the primary config relevant for the filter from its connection. - */ -struct ssl_primary_config * - Curl_ssl_cf_get_primary_config(struct Curl_cfilter *cf); - -/** - * Get the first SSL filter in the chain starting with `cf`, or NULL. - */ -struct Curl_cfilter *Curl_ssl_cf_get_ssl(struct Curl_cfilter *cf); +void Curl_ssl_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data, + struct easy_pollset *ps); /** * Get the SSL filter below the given one or NULL if there is none. diff --git a/Utilities/cmcurl/lib/vtls/wolfssl.c b/Utilities/cmcurl/lib/vtls/wolfssl.c index b1384a6..5890bb6 100644 --- a/Utilities/cmcurl/lib/vtls/wolfssl.c +++ b/Utilities/cmcurl/lib/vtls/wolfssl.c @@ -480,6 +480,7 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) return CURLE_SSL_CONNECT_ERROR; } #endif + default: break; } @@ -513,7 +514,7 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) } } -#ifndef NO_FILESYSTEM +#if !defined(NO_FILESYSTEM) && defined(WOLFSSL_SYS_CA_CERTS) /* load native CA certificates */ if(ssl_config->native_ca_store) { if(wolfSSL_CTX_load_system_CA_certs(backend->ctx) != WOLFSSL_SUCCESS) { @@ -608,24 +609,12 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) SSL_VERIFY_NONE, NULL); #ifdef HAVE_SNI - if(sni) { - struct in_addr addr4; -#ifdef ENABLE_IPV6 - struct in6_addr addr6; -#endif - size_t hostname_len = strlen(connssl->hostname); - - if((hostname_len < USHRT_MAX) && - !Curl_inet_pton(AF_INET, connssl->hostname, &addr4) -#ifdef ENABLE_IPV6 - && !Curl_inet_pton(AF_INET6, connssl->hostname, &addr6) -#endif - ) { - size_t snilen; - char *snihost = Curl_ssl_snihost(data, connssl->hostname, &snilen); - if(!snihost || - wolfSSL_CTX_UseSNI(backend->ctx, WOLFSSL_SNI_HOST_NAME, snihost, - (unsigned short)snilen) != 1) { + if(sni && connssl->peer.sni) { + size_t sni_len = strlen(connssl->peer.sni); + if((sni_len < USHRT_MAX)) { + if(wolfSSL_CTX_UseSNI(backend->ctx, WOLFSSL_SNI_HOST_NAME, + connssl->peer.sni, + (unsigned short)sni_len) != 1) { failf(data, "Failed to set SNI"); return CURLE_SSL_CONNECT_ERROR; } @@ -763,9 +752,9 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) /* Enable RFC2818 checks */ if(conn_config->verifyhost) { - char *snihost = Curl_ssl_snihost(data, connssl->hostname, NULL); - if(!snihost || - (wolfSSL_check_domain_name(backend->handle, snihost) == SSL_FAILURE)) + char *snihost = connssl->peer.sni? + connssl->peer.sni : connssl->peer.hostname; + if(wolfSSL_check_domain_name(backend->handle, snihost) == SSL_FAILURE) return CURLE_SSL_CONNECT_ERROR; } @@ -813,7 +802,7 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) else if(DOMAIN_NAME_MISMATCH == detail) { #if 1 failf(data, " subject alt name(s) or common name do not match \"%s\"", - connssl->dispname); + connssl->peer.dispname); return CURLE_PEER_FAILED_VERIFICATION; #else /* When the wolfssl_check_domain_name() is used and you desire to @@ -1398,7 +1387,7 @@ const struct Curl_ssl Curl_ssl_wolfssl = { Curl_none_cert_status_request, /* cert_status_request */ wolfssl_connect, /* connect */ wolfssl_connect_nonblocking, /* connect_nonblocking */ - Curl_ssl_get_select_socks, /* getsock */ + Curl_ssl_adjust_pollset, /* adjust_pollset */ wolfssl_get_internals, /* get_internals */ wolfssl_close, /* close_one */ Curl_none_close_all, /* close_all */ diff --git a/Utilities/cmcurl/lib/vtls/x509asn1.c b/Utilities/cmcurl/lib/vtls/x509asn1.c index c3fd3a3..8b1eed6 100644 --- a/Utilities/cmcurl/lib/vtls/x509asn1.c +++ b/Utilities/cmcurl/lib/vtls/x509asn1.c @@ -1317,16 +1317,16 @@ CURLcode Curl_verifyhost(struct Curl_cfilter *cf, if(Curl_parseX509(&cert, beg, end)) return CURLE_PEER_FAILED_VERIFICATION; - hostlen = strlen(connssl->hostname); + hostlen = strlen(connssl->peer.hostname); /* Get the server IP address. */ #ifdef ENABLE_IPV6 if(cf->conn->bits.ipv6_ip && - Curl_inet_pton(AF_INET6, connssl->hostname, &addr)) + Curl_inet_pton(AF_INET6, connssl->peer.hostname, &addr)) addrlen = sizeof(struct in6_addr); else #endif - if(Curl_inet_pton(AF_INET, connssl->hostname, &addr)) + if(Curl_inet_pton(AF_INET, connssl->peer.hostname, &addr)) addrlen = sizeof(struct in_addr); /* Process extensions. */ @@ -1361,7 +1361,7 @@ CURLcode Curl_verifyhost(struct Curl_cfilter *cf, name.beg, name.end); if(len > 0 && (size_t)len == strlen(dnsname)) matched = Curl_cert_hostcheck(dnsname, (size_t)len, - connssl->hostname, hostlen); + connssl->peer.hostname, hostlen); else matched = 0; free(dnsname); @@ -1421,7 +1421,7 @@ CURLcode Curl_verifyhost(struct Curl_cfilter *cf, if(strlen(dnsname) != (size_t) len) /* Nul byte in string ? */ failf(data, "SSL: illegal cert name field"); else if(Curl_cert_hostcheck((const char *) dnsname, - len, connssl->hostname, hostlen)) { + len, connssl->peer.hostname, hostlen)) { infof(data, " common name: %s (matched)", dnsname); free(dnsname); return CURLE_OK; diff --git a/Utilities/cmcurl/lib/warnless.c b/Utilities/cmcurl/lib/warnless.c index 7e077f8..c80937b 100644 --- a/Utilities/cmcurl/lib/warnless.c +++ b/Utilities/cmcurl/lib/warnless.c @@ -37,7 +37,7 @@ #include "warnless.h" -#ifdef WIN32 +#ifdef _WIN32 #undef read #undef write #endif @@ -367,7 +367,7 @@ curl_socket_t curlx_sitosk(int i) #endif /* USE_WINSOCK */ -#if defined(WIN32) +#if defined(_WIN32) ssize_t curlx_read(int fd, void *buf, size_t count) { @@ -379,8 +379,8 @@ ssize_t curlx_write(int fd, const void *buf, size_t count) return (ssize_t)write(fd, buf, curlx_uztoui(count)); } -/* Ensure that warnless.h continues to have an effect in "unity" builds. */ -#undef HEADER_CURL_WARNLESS_H - -#endif /* WIN32 */ +#endif /* _WIN32 */ +/* Ensure that warnless.h redefinitions continue to have an effect + in "unity" builds. */ +#undef HEADER_CURL_WARNLESS_H_REDEFS diff --git a/Utilities/cmcurl/lib/warnless.h b/Utilities/cmcurl/lib/warnless.h index 2a53016..e5a02c8 100644 --- a/Utilities/cmcurl/lib/warnless.h +++ b/Utilities/cmcurl/lib/warnless.h @@ -69,18 +69,13 @@ curl_socket_t curlx_sitosk(int i); #endif /* USE_WINSOCK */ -#if defined(WIN32) +#if defined(_WIN32) ssize_t curlx_read(int fd, void *buf, size_t count); ssize_t curlx_write(int fd, const void *buf, size_t count); -#undef read -#define read(fd, buf, count) curlx_read(fd, buf, count) -#undef write -#define write(fd, buf, count) curlx_write(fd, buf, count) - -#endif /* WIN32 */ +#endif /* _WIN32 */ #if defined(__INTEL_COMPILER) && defined(__unix__) @@ -97,3 +92,15 @@ unsigned short curlx_ntohs(unsigned short usnum); #endif /* __INTEL_COMPILER && __unix__ */ #endif /* HEADER_CURL_WARNLESS_H */ + +#ifndef HEADER_CURL_WARNLESS_H_REDEFS +#define HEADER_CURL_WARNLESS_H_REDEFS + +#if defined(_WIN32) +#undef read +#define read(fd, buf, count) curlx_read(fd, buf, count) +#undef write +#define write(fd, buf, count) curlx_write(fd, buf, count) +#endif + +#endif /* HEADER_CURL_WARNLESS_H_REDEFS */ diff --git a/Utilities/cmcurl/lib/ws.c b/Utilities/cmcurl/lib/ws.c index 3c1964b..adde531 100644 --- a/Utilities/cmcurl/lib/ws.c +++ b/Utilities/cmcurl/lib/ws.c @@ -274,8 +274,8 @@ static CURLcode ws_dec_pass_payload(struct ws_decoder *dec, dec->payload_offset += (curl_off_t)nwritten; remain = dec->payload_len - dec->payload_offset; /* infof(data, "WS-DEC: passed %zd bytes payload, %" - CURL_FORMAT_CURL_OFF_T " remain", - nwritten, remain); */ + CURL_FORMAT_CURL_OFF_T " remain", + nwritten, remain); */ } return remain? CURLE_AGAIN : CURLE_OK; @@ -925,8 +925,8 @@ CURL_EXTERN CURLcode curl_ws_recv(struct Curl_easy *data, void *buffer, *metap = &ws->frame; *nread = ws->frame.len; /* infof(data, "curl_ws_recv(len=%zu) -> %zu bytes (frame at %" - CURL_FORMAT_CURL_OFF_T ", %" CURL_FORMAT_CURL_OFF_T " left)", - buflen, *nread, ws->frame.offset, ws->frame.bytesleft); */ + CURL_FORMAT_CURL_OFF_T ", %" CURL_FORMAT_CURL_OFF_T " left)", + buflen, *nread, ws->frame.offset, ws->frame.bytesleft); */ return CURLE_OK; } |