diff options
author | ng0 <ng0@n0.is> | 2019-02-25 11:15:00 +0000 |
---|---|---|
committer | ng0 <ng0@n0.is> | 2019-02-25 11:15:00 +0000 |
commit | 2a251478463006d1deacf1610987d75054ba4fb8 (patch) | |
tree | 41bfad1ee4f0d2cee35c88d7de02ce0356307379 /lib | |
parent | 810ed5cbb8e14bd8c1f9bfed76b3811a1cdd0207 (diff) | |
parent | f3294d9d86e6a7915a967efff2842089b8b0d071 (diff) | |
download | gnurl-2a251478463006d1deacf1610987d75054ba4fb8.tar.gz gnurl-2a251478463006d1deacf1610987d75054ba4fb8.tar.bz2 gnurl-2a251478463006d1deacf1610987d75054ba4fb8.zip |
Merge tag 'curl-7_64_0'
curl 7.64.0
Diffstat (limited to 'lib')
55 files changed, 1102 insertions, 1146 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am index d4fca4d47..8905e7107 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -5,7 +5,7 @@ # | (__| |_| | _ <| |___ # \___|\___/|_| \_\_____| # -# Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. +# Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. The terms @@ -29,8 +29,7 @@ EXTRA_DIST = Makefile.m32 config-win32.h \ makefile.amiga Makefile.netware nwlib.c nwos.c config-win32ce.h \ config-os400.h setup-os400.h config-symbian.h Makefile.Watcom \ config-tpf.h mk-ca-bundle.pl mk-ca-bundle.vbs $(CMAKE_DIST) \ - firefox-db2pem.sh config-vxworks.h Makefile.vxworks checksrc.pl \ - objnames-test08.sh objnames-test10.sh objnames.inc + firefox-db2pem.sh config-vxworks.h Makefile.vxworks checksrc.pl lib_LTLIBRARIES = libgnurl.la @@ -88,10 +87,6 @@ libgnurl_la_CPPFLAGS_EXTRA = libgnurl_la_LDFLAGS_EXTRA = libgnurl_la_CFLAGS_EXTRA = -@CODE_COVERAGE_RULES@ -libgnurl_la_LDFLAGS_EXTRA += $(CODE_COVERAGE_LDFLAGS) -libgnurl_la_CFLAGS_EXTRA += $(CODE_COVERAGE_CFLAGS) - if CURL_LT_SHLIB_USE_VERSION_INFO libgnurl_la_LDFLAGS_EXTRA += $(VERSIONINFO) endif diff --git a/lib/asyn-ares.c b/lib/asyn-ares.c index 6a49566c8..04a25b321 100644 --- a/lib/asyn-ares.c +++ b/lib/asyn-ares.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -199,6 +199,17 @@ void Curl_resolver_cancel(struct connectdata *conn) } /* + * We're equivalent to Curl_resolver_cancel() for the c-ares resolver. We + * never block. + */ +void Curl_resolver_kill(struct connectdata *conn) +{ + /* We don't need to check the resolver state because we can be called safely + at any time and we always do the same thing. */ + Curl_resolver_cancel(conn); +} + +/* * destroy_async_data() cleans up async resolver data. */ static void destroy_async_data(struct Curl_async *async) @@ -361,13 +372,13 @@ CURLcode Curl_resolver_is_resolved(struct connectdata *conn, /* * Curl_resolver_wait_resolv() * - * waits for a resolve to finish. This function should be avoided since using + * Waits for a resolve to finish. This function should be avoided since using * this risk getting the multi interface to "hang". * * If 'entry' is non-NULL, make it point to the resolved dns entry * - * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved, and - * CURLE_OPERATION_TIMEDOUT if a time-out occurred. + * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved, + * CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors. */ CURLcode Curl_resolver_wait_resolv(struct connectdata *conn, struct Curl_dns_entry **entry) diff --git a/lib/asyn-thread.c b/lib/asyn-thread.c index 74208d7ec..a9679d062 100644 --- a/lib/asyn-thread.c +++ b/lib/asyn-thread.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -462,13 +462,33 @@ static CURLcode resolver_error(struct connectdata *conn) } /* + * Until we gain a way to signal the resolver threads to stop early, we must + * simply wait for them and ignore their results. + */ +void Curl_resolver_kill(struct connectdata *conn) +{ + struct thread_data *td = (struct thread_data*) conn->async.os_specific; + + /* 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 + data. */ + if(td && td->thread_hnd != curl_thread_t_null) + (void)Curl_resolver_wait_resolv(conn, NULL); + else + Curl_resolver_cancel(conn); +} + +/* * Curl_resolver_wait_resolv() * - * waits for a resolve to finish. This function should be avoided since using + * Waits for a resolve to finish. This function should be avoided since using * this risk getting the multi interface to "hang". * * If 'entry' is non-NULL, make it point to the resolved dns entry * + * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved, + * CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors. + * * This is the version for resolves-in-a-thread. */ CURLcode Curl_resolver_wait_resolv(struct connectdata *conn, @@ -478,6 +498,7 @@ CURLcode Curl_resolver_wait_resolv(struct connectdata *conn, CURLcode result = CURLE_OK; DEBUGASSERT(conn && td); + DEBUGASSERT(td->thread_hnd != curl_thread_t_null); /* wait for the thread to resolve the name */ if(Curl_thread_join(&td->thread_hnd)) { diff --git a/lib/asyn.h b/lib/asyn.h index 43625bc3b..ccd4b1f7e 100644 --- a/lib/asyn.h +++ b/lib/asyn.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -87,10 +87,25 @@ CURLcode Curl_resolver_duphandle(struct Curl_easy *easy, void **to, * * It is called from inside other functions to cancel currently performing * resolver request. Should also free any temporary resources allocated to - * perform a request. + * perform a request. This never waits for resolver threads to complete. + * + * It is safe to call this when conn is in any state. */ void Curl_resolver_cancel(struct connectdata *conn); +/* + * Curl_resolver_kill(). + * + * This acts like Curl_resolver_cancel() except it will block until any threads + * associated with the resolver are complete. This never blocks for resolvers + * that do not use threads. This is intended to be the "last chance" function + * that cleans up an in-progress resolver completely (before its owner is about + * to die). + * + * It is safe to call this when conn is in any state. + */ +void Curl_resolver_kill(struct connectdata *conn); + /* Curl_resolver_getsock() * * This function is called from the multi_getsock() function. 'sock' is a @@ -117,14 +132,13 @@ CURLcode Curl_resolver_is_resolved(struct connectdata *conn, /* * Curl_resolver_wait_resolv() * - * waits for a resolve to finish. This function should be avoided since using + * Waits for a resolve to finish. This function should be avoided since using * this risk getting the multi interface to "hang". * * If 'entry' is non-NULL, make it point to the resolved dns entry * - * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved, and - * CURLE_OPERATION_TIMEDOUT if a time-out occurred. - + * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved, + * CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors. */ CURLcode Curl_resolver_wait_resolv(struct connectdata *conn, struct Curl_dns_entry **dnsentry); @@ -148,6 +162,7 @@ Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn, #ifndef CURLRES_ASYNCH /* convert these functions if an asynch resolver isn't used */ #define Curl_resolver_cancel(x) Curl_nop_stmt +#define Curl_resolver_kill(x) Curl_nop_stmt #define Curl_resolver_is_resolved(x,y) CURLE_COULDNT_RESOLVE_HOST #define Curl_resolver_wait_resolv(x,y) CURLE_COULDNT_RESOLVE_HOST #define Curl_resolver_getsock(x,y,z) 0 diff --git a/lib/conncache.c b/lib/conncache.c index 737e396e9..463c57a3d 100644 --- a/lib/conncache.c +++ b/lib/conncache.c @@ -6,7 +6,7 @@ * \___|\___/|_| \_\_____| * * Copyright (C) 2012 - 2016, Linus Nielsen Feltzing, <linus@haxx.se> - * Copyright (C) 2012 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2012 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -178,9 +178,9 @@ static void hashkey(struct connectdata *conn, char *buf, msnprintf(buf, len, "%ld%s", conn->port, hostname); } -void Curl_conncache_unlock(struct connectdata *conn) +void Curl_conncache_unlock(struct Curl_easy *data) { - CONN_UNLOCK(conn->data); + CONN_UNLOCK(data); } /* Returns number of connections currently held in the connection cache. @@ -302,9 +302,14 @@ CURLcode Curl_conncache_add_conn(struct conncache *connc, return result; } -void Curl_conncache_remove_conn(struct connectdata *conn, bool lock) +/* + * Removes the connectdata object from the connection cache *and* clears the + * ->data pointer association. Pass TRUE/FALSE in the 'lock' argument + * depending on if the parent function already holds the lock or not. + */ +void Curl_conncache_remove_conn(struct Curl_easy *data, + struct connectdata *conn, bool lock) { - struct Curl_easy *data = conn->data; struct connectbundle *bundle = conn->bundle; struct conncache *connc = data->state.conn_cache; @@ -323,6 +328,7 @@ void Curl_conncache_remove_conn(struct connectdata *conn, bool lock) DEBUGF(infof(data, "The cache now contains %zu members\n", connc->num_conn)); } + conn->data = NULL; /* clear the association */ if(lock) { CONN_UNLOCK(data); } @@ -566,8 +572,6 @@ void Curl_conncache_close_all_connections(struct conncache *connc) conn->data = connc->closure_handle; sigpipe_ignore(conn->data, &pipe_st); - conn->data->easy_conn = NULL; /* clear the easy handle's connection - pointer */ /* This will remove the connection from the cache */ connclose(conn, "kill all"); (void)Curl_disconnect(connc->closure_handle, conn, FALSE); diff --git a/lib/conncache.h b/lib/conncache.h index eedd7a800..0df6d4715 100644 --- a/lib/conncache.h +++ b/lib/conncache.h @@ -56,7 +56,7 @@ void Curl_conncache_destroy(struct conncache *connc); /* return the correct bundle, to a host or a proxy */ struct connectbundle *Curl_conncache_find_bundle(struct connectdata *conn, struct conncache *connc); -void Curl_conncache_unlock(struct connectdata *conn); +void Curl_conncache_unlock(struct Curl_easy *data); /* returns number of connections currently held in the connection cache */ size_t Curl_conncache_size(struct Curl_easy *data); size_t Curl_conncache_bundle_size(struct connectdata *conn); @@ -64,7 +64,8 @@ size_t Curl_conncache_bundle_size(struct connectdata *conn); bool Curl_conncache_return_conn(struct connectdata *conn); CURLcode Curl_conncache_add_conn(struct conncache *connc, struct connectdata *conn) WARN_UNUSED_RESULT; -void Curl_conncache_remove_conn(struct connectdata *conn, +void Curl_conncache_remove_conn(struct Curl_easy *data, + struct connectdata *conn, bool lock); bool Curl_conncache_foreach(struct Curl_easy *data, struct conncache *connc, diff --git a/lib/cookie.c b/lib/cookie.c index 3dc85ee5c..4fb992ac9 100644 --- a/lib/cookie.c +++ b/lib/cookie.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -223,7 +223,7 @@ static bool pathmatch(const char *cookie_path, const char *request_uri) goto pathmatched; } - /* here, cookie_path_len < url_path_len */ + /* here, cookie_path_len < uri_path_len */ if(uri_path[cookie_path_len] == '/') { ret = TRUE; goto pathmatched; @@ -433,9 +433,10 @@ Curl_cookie_add(struct Curl_easy *data, bool noexpire, /* if TRUE, skip remove_expired() */ char *lineptr, /* first character of the line */ const char *domain, /* default domain */ - const char *path) /* full path used when this cookie is set, + const char *path, /* full path used when this cookie is set, used to get default path for the cookie unless set */ + bool secure) /* TRUE if connection is over secure origin */ { struct Cookie *clist; struct Cookie *co; @@ -546,8 +547,20 @@ Curl_cookie_add(struct Curl_easy *data, /* this was a "<name>=" with no content, and we must allow 'secure' and 'httponly' specified this weirdly */ done = TRUE; - if(strcasecompare("secure", name)) - co->secure = TRUE; + /* + * secure cookies are only allowed to be set when the connection is + * using a secure protocol, or when the cookie is being set by + * reading from file + */ + if(strcasecompare("secure", name)) { + if(secure || !c->running) { + co->secure = TRUE; + } + else { + badcookie = TRUE; + break; + } + } else if(strcasecompare("httponly", name)) co->httponly = TRUE; else if(sep) @@ -790,6 +803,8 @@ Curl_cookie_add(struct Curl_easy *data, co->domain = strdup(ptr); if(!co->domain) badcookie = TRUE; + else if(bad_domain(co->domain)) + badcookie = TRUE; break; case 1: /* This field got its explanation on the 23rd of May 2001 by @@ -831,7 +846,13 @@ Curl_cookie_add(struct Curl_easy *data, fields++; /* add a field and fall down to secure */ /* FALLTHROUGH */ case 3: - co->secure = strcasecompare(ptr, "TRUE")?TRUE:FALSE; + co->secure = FALSE; + if(strcasecompare(ptr, "TRUE")) { + if(secure || c->running) + co->secure = TRUE; + else + badcookie = TRUE; + } break; case 4: if(curlx_strtoofft(ptr, NULL, 10, &co->expires)) @@ -887,18 +908,20 @@ Curl_cookie_add(struct Curl_easy *data, if(!noexpire) remove_expired(c); -#ifdef USE_LIBPSL - /* Check if the domain is a Public Suffix and if yes, ignore the cookie. */ if(domain && co->domain && !isip(co->domain)) { - const psl_ctx_t *psl = Curl_psl_use(data); int acceptable; +#ifdef USE_LIBPSL + const psl_ctx_t *psl = Curl_psl_use(data); + /* Check if the domain is a Public Suffix and if yes, ignore the cookie. */ if(psl) { acceptable = psl_is_cookie_domain_acceptable(psl, domain, co->domain); Curl_psl_release(data); } else - acceptable = !bad_domain(domain); +#endif + /* Without libpsl, do the best we can. */ + acceptable = !bad_domain(co->domain); if(!acceptable) { infof(data, "cookie '%s' dropped, domain '%s' must not " @@ -907,7 +930,6 @@ Curl_cookie_add(struct Curl_easy *data, return NULL; } } -#endif myhash = cookiehash(co->domain); clist = c->cookies[myhash]; @@ -929,9 +951,31 @@ Curl_cookie_add(struct Curl_easy *data, /* the domains were identical */ if(clist->spath && co->spath) { - if(strcasecompare(clist->spath, co->spath)) { - replace_old = TRUE; + if(clist->secure && !co->secure && !secure) { + size_t cllen; + const char *sep; + + /* + * A non-secure cookie may not overlay an existing secure cookie. + * For an existing cookie "a" with path "/login", refuse a new + * cookie "a" with for example path "/login/en", while the path + * "/loginhelper" is ok. + */ + + sep = strchr(clist->spath + 1, '/'); + + if(sep) + cllen = sep - clist->spath; + else + cllen = strlen(clist->spath); + + if(strncasecompare(clist->spath, co->spath, cllen)) { + freecookie(co); + return NULL; + } } + else if(strcasecompare(clist->spath, co->spath)) + replace_old = TRUE; else replace_old = FALSE; } @@ -1103,7 +1147,7 @@ struct CookieInfo *Curl_cookie_init(struct Curl_easy *data, while(*lineptr && ISBLANK(*lineptr)) lineptr++; - Curl_cookie_add(data, c, headerline, TRUE, lineptr, NULL, NULL); + Curl_cookie_add(data, c, headerline, TRUE, lineptr, NULL, NULL, TRUE); } free(line); /* free the line buffer */ remove_expired(c); /* run this once, not on every cookie */ diff --git a/lib/cookie.h b/lib/cookie.h index b2ec17a35..609c30b1e 100644 --- a/lib/cookie.h +++ b/lib/cookie.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -85,7 +85,8 @@ struct Curl_easy; struct Cookie *Curl_cookie_add(struct Curl_easy *data, struct CookieInfo *, bool header, bool noexpiry, char *lineptr, - const char *domain, const char *path); + const char *domain, const char *path, + bool secure); struct Cookie *Curl_cookie_getlist(struct CookieInfo *, const char *, const char *, bool); diff --git a/lib/curl_sasl.c b/lib/curl_sasl.c index 894e16c3d..437a9f66a 100644 --- a/lib/curl_sasl.c +++ b/lib/curl_sasl.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2012 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2012 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -300,8 +300,7 @@ CURLcode Curl_sasl_start(struct SASL *sasl, struct connectdata *conn, result = Curl_auth_create_gssapi_user_message(data, conn->user, conn->passwd, service, - data->easy_conn-> - host.name, + data->conn->host.name, sasl->mutual_auth, NULL, &conn->krb5, &resp, &len); @@ -517,7 +516,7 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct connectdata *conn, result = Curl_auth_create_gssapi_user_message(data, conn->user, conn->passwd, service, - data->easy_conn->host.name, + data->conn->host.name, sasl->mutual_auth, NULL, &conn->krb5, &resp, &len); @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2018, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2018 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -160,7 +160,7 @@ static int Curl_doh_done(struct Curl_easy *doh, CURLcode result) struct Curl_easy *data = doh->set.dohfor; /* so one of the DOH request done for the 'data' transfer is now complete! */ data->req.doh.pending--; - infof(data, "a DOH request is completed, %d to go\n", data->req.doh.pending); + infof(data, "a DOH request is completed, %u to go\n", data->req.doh.pending); if(result) infof(data, "DOH request %s\n", curl_easy_strerror(result)); diff --git a/lib/easy.c b/lib/easy.c index 887b47729..0574372f8 100644 --- a/lib/easy.c +++ b/lib/easy.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -1060,7 +1060,7 @@ CURLcode curl_easy_pause(struct Curl_easy *data, int action) unsigned int i; unsigned int count = data->state.tempcount; struct tempbuf writebuf[3]; /* there can only be three */ - struct connectdata *conn = data->easy_conn; + struct connectdata *conn = data->conn; struct Curl_easy *saved_data = NULL; /* copy the structs to allow for immediate re-pausing */ @@ -655,7 +655,7 @@ CURLcode Curl_GetFTPResponse(ssize_t *nreadp, /* return number of bytes read */ while(!*ftpcode && !result) { /* check and reset timeout value every lap */ - time_t timeout = Curl_pp_state_timeout(pp); /* timeout in milliseconds */ + time_t timeout = Curl_pp_state_timeout(pp, FALSE); time_t interval_ms; if(timeout <= 0) { @@ -3054,7 +3054,7 @@ static CURLcode ftp_multi_statemach(struct connectdata *conn, bool *done) { struct ftp_conn *ftpc = &conn->proto.ftpc; - CURLcode result = Curl_pp_statemach(&ftpc->pp, FALSE); + CURLcode result = Curl_pp_statemach(&ftpc->pp, FALSE, FALSE); /* Check for the state outside of the Curl_socket_check() return code checks since at times we are in fact already in this state when this function @@ -3071,7 +3071,7 @@ static CURLcode ftp_block_statemach(struct connectdata *conn) CURLcode result = CURLE_OK; while(ftpc->state != FTP_STOP) { - result = Curl_pp_statemach(pp, TRUE); + result = Curl_pp_statemach(pp, TRUE, TRUE /* disconnecting */); if(result) break; } diff --git a/lib/getinfo.c b/lib/getinfo.c index d9bd64129..a883bb497 100644 --- a/lib/getinfo.c +++ b/lib/getinfo.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -390,7 +390,7 @@ static CURLcode getinfo_slist(struct Curl_easy *data, CURLINFO info, param_slistp; struct curl_tlssessioninfo *tsi = &data->tsi; #ifdef USE_SSL - struct connectdata *conn = data->easy_conn; + struct connectdata *conn = data->conn; #endif *tsip = tsi; diff --git a/lib/gopher.c b/lib/gopher.c index d8a8ff86d..d1bcadf5b 100644 --- a/lib/gopher.c +++ b/lib/gopher.c @@ -31,9 +31,11 @@ #include "progress.h" #include "gopher.h" #include "select.h" +#include "strdup.h" #include "url.h" #include "escape.h" #include "warnless.h" +#include "curl_printf.h" #include "curl_memory.h" /* The last #include file should be: */ #include "memdebug.h" @@ -78,7 +80,9 @@ static CURLcode gopher_do(struct connectdata *conn, bool *done) curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; curl_off_t *bytecount = &data->req.bytecount; + char *gopherpath; char *path = data->state.up.path; + char *query = data->state.up.query; char *sel = NULL; char *sel_org = NULL; ssize_t amount, k; @@ -86,20 +90,30 @@ static CURLcode gopher_do(struct connectdata *conn, bool *done) *done = TRUE; /* unconditionally */ + if(path && query) + gopherpath = aprintf("%s?%s", path, query); + else + gopherpath = strdup(path); + + if(!gopherpath) + return CURLE_OUT_OF_MEMORY; + /* Create selector. Degenerate cases: / and /1 => convert to "" */ - if(strlen(path) <= 2) { + if(strlen(gopherpath) <= 2) { sel = (char *)""; len = strlen(sel); + free(gopherpath); } else { char *newp; /* Otherwise, drop / and the first character (i.e., item type) ... */ - newp = path; + newp = gopherpath; newp += 2; /* ... and finally unescape */ result = Curl_urldecode(data, newp, 0, &sel, &len, FALSE); + free(gopherpath); if(result) return result; sel_org = sel; diff --git a/lib/hostip.c b/lib/hostip.c index f589a0b2c..89b88e932 100644 --- a/lib/hostip.c +++ b/lib/hostip.c @@ -312,6 +312,26 @@ fetch_addr(struct connectdata *conn, /* See if its already in our dns cache */ dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1); + /* No entry found in cache, check if we might have a wildcard entry */ + if(!dns && data->change.wildcard_resolve) { + /* + * Free the previous entry_id before requesting a new one to avoid leaking + * memory + */ + free(entry_id); + + entry_id = create_hostcache_id("*", port); + + /* If we can't create the entry id, fail */ + if(!entry_id) + return dns; + + entry_len = strlen(entry_id); + + /* See if it's already in our dns cache */ + dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1); + } + if(dns && (data->set.dns_cache_timeout != -1)) { /* See whether the returned entry is stale. Done before we release lock */ struct hostcache_prune_data user; @@ -872,6 +892,9 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data) char hostname[256]; int port = 0; + /* Default is no wildcard found */ + data->change.wildcard_resolve = false; + for(hostp = data->change.resolve; hostp; hostp = hostp->next) { if(!hostp->data) continue; @@ -1052,6 +1075,13 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data) } infof(data, "Added %s:%d:%s to DNS cache\n", hostname, port, addresses); + + /* Wildcard hostname */ + if(hostname[0] == '*' && hostname[1] == '\0') { + infof(data, "RESOLVE %s:%d is wildcard, enabling wildcard checks\n", + hostname, port); + data->change.wildcard_resolve = true; + } } } data->change.resolve = NULL; /* dealt with now */ diff --git a/lib/http.c b/lib/http.c index 10104a26d..83d23c258 100644 --- a/lib/http.c +++ b/lib/http.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -616,6 +616,7 @@ output_auth_headers(struct connectdata *conn, result = Curl_output_negotiate(conn, proxy); if(result) return result; + authstatus->done = TRUE; negdata->state = GSS_AUTHSENT; } else @@ -1681,6 +1682,52 @@ enum proxy_use { HEADER_CONNECT /* sending CONNECT to a proxy */ }; +/* used to compile the provided trailers into one buffer + will return an error code if one of the headers is + not formatted correctly */ +CURLcode Curl_http_compile_trailers(struct curl_slist *trailers, + Curl_send_buffer *buffer, + struct Curl_easy *handle) +{ + char *ptr = NULL; + CURLcode result = CURLE_OK; + const char *endofline_native = NULL; + const char *endofline_network = NULL; + + /* TODO: Maybe split Curl_add_custom_headers to make it reusable here */ + + if( +#ifdef CURL_DO_LINEEND_CONV + (handle->set.prefer_ascii) || +#endif + (handle->set.crlf)) { + /* \n will become \r\n later on */ + endofline_native = "\n"; + endofline_network = "\x0a"; + } + else { + endofline_native = "\r\n"; + endofline_network = "\x0d\x0a"; + } + + while(trailers) { + /* only add correctly formatted trailers */ + ptr = strchr(trailers->data, ':'); + if(ptr && *(ptr + 1) == ' ') { + result = Curl_add_bufferf(&buffer, "%s%s", trailers->data, + endofline_native); + if(result) + return result; + } + else + infof(handle, "Malformatted trailing header ! Skipping trailer."); + trailers = trailers->next; + } + result = Curl_add_buffer(&buffer, endofline_network, + strlen(endofline_network)); + return result; +} + CURLcode Curl_add_custom_headers(struct connectdata *conn, bool is_connect, Curl_send_buffer *req_buffer) @@ -1788,7 +1835,8 @@ CURLcode Curl_add_custom_headers(struct connectdata *conn, checkprefix("Transfer-Encoding:", headers->data)) /* HTTP/2 doesn't support chunked requests */ ; - else if(checkprefix("Authorization:", headers->data) && + else if((checkprefix("Authorization:", headers->data) || + checkprefix("Cookie:", headers->data)) && /* be careful of sending this potentially sensitive header to other hosts */ (data->state.this_is_a_follow && @@ -3175,6 +3223,10 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, k->header = FALSE; k->badheader = HEADER_ALLBAD; streamclose(conn, "bad HTTP: No end-of-message indicator"); + if(!data->set.http09_allowed) { + failf(data, "Received HTTP/0.9 when not allowed\n"); + return CURLE_UNSUPPORTED_PROTOCOL; + } break; } } @@ -3208,6 +3260,10 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, if(st == STATUS_BAD) { streamclose(conn, "bad HTTP: No end-of-message indicator"); /* this is not the beginning of a protocol first header line */ + if(!data->set.http09_allowed) { + failf(data, "Received HTTP/0.9 when not allowed\n"); + return CURLE_UNSUPPORTED_PROTOCOL; + } k->header = FALSE; if(*nread) /* since there's more, this is a partial bad header */ @@ -3873,7 +3929,9 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, here, or else use real peer host name. */ conn->allocptr.cookiehost? conn->allocptr.cookiehost:conn->host.name, - data->state.up.path); + data->state.up.path, + (conn->handler->protocol&CURLPROTO_HTTPS)? + TRUE:FALSE); Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); } #endif diff --git a/lib/http.h b/lib/http.h index b0dd1fe7d..c265cb71c 100644 --- a/lib/http.h +++ b/lib/http.h @@ -74,6 +74,9 @@ CURLcode Curl_add_timecondition(struct Curl_easy *data, CURLcode Curl_add_custom_headers(struct connectdata *conn, bool is_connect, Curl_send_buffer *req_buffer); +CURLcode Curl_http_compile_trailers(struct curl_slist *trailers, + Curl_send_buffer *buffer, + struct Curl_easy *handle); /* protocol-specific functions set up to be called by the main engine */ CURLcode Curl_http(struct connectdata *conn, bool *done); diff --git a/lib/http2.c b/lib/http2.c index 3e31cfe55..d8030b6fe 100644 --- a/lib/http2.c +++ b/lib/http2.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -800,7 +800,7 @@ static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags, H2BUGF(infof(data_s, "NGHTTP2_ERR_PAUSE - %zu bytes out of buffer" ", stream %u\n", len - nread, stream_id)); - data_s->easy_conn->proto.httpc.pause_stream_id = stream_id; + data_s->conn->proto.httpc.pause_stream_id = stream_id; return NGHTTP2_ERR_PAUSE; } @@ -808,7 +808,7 @@ static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags, /* pause execution of nghttp2 if we received data for another handle in order to process them first. */ if(conn->data != data_s) { - data_s->easy_conn->proto.httpc.pause_stream_id = stream_id; + data_s->conn->proto.httpc.pause_stream_id = stream_id; return NGHTTP2_ERR_PAUSE; } @@ -854,6 +854,10 @@ static int on_stream_close(nghttp2_session *session, int32_t stream_id, stream_id); DEBUGASSERT(0); } + if(stream_id == httpc->pause_stream_id) { + H2BUGF(infof(data_s, "Stopped the pause stream!\n")); + httpc->pause_stream_id = 0; + } H2BUGF(infof(data_s, "Removed stream %u hash!\n", stream_id)); stream->stream_id = 0; /* cleared */ } diff --git a/lib/http_negotiate.c b/lib/http_negotiate.c index 444265d11..2a97707eb 100644 --- a/lib/http_negotiate.c +++ b/lib/http_negotiate.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -49,7 +49,6 @@ CURLcode Curl_input_negotiate(struct connectdata *conn, bool proxy, /* Point to the correct struct with this */ struct negotiatedata *neg_ctx; - struct auth *authp; if(proxy) { userp = conn->http_proxy.user; @@ -58,7 +57,6 @@ CURLcode Curl_input_negotiate(struct connectdata *conn, bool proxy, data->set.str[STRING_PROXY_SERVICE_NAME] : "HTTP"; host = conn->http_proxy.host.name; neg_ctx = &data->state.proxyneg; - authp = &conn->data->state.authproxy; } else { userp = conn->user; @@ -67,7 +65,6 @@ CURLcode Curl_input_negotiate(struct connectdata *conn, bool proxy, data->set.str[STRING_SERVICE_NAME] : "HTTP"; host = conn->host.name; neg_ctx = &data->state.negotiate; - authp = &conn->data->state.authhost; } /* Not set means empty */ @@ -92,17 +89,17 @@ CURLcode Curl_input_negotiate(struct connectdata *conn, bool proxy, } } + /* Supports SSL channel binding for Windows ISS extended protection */ +#if defined(USE_WINDOWS_SSPI) && defined(SECPKG_ATTR_ENDPOINT_BINDINGS) + neg_ctx->sslContext = conn->sslContext; +#endif + /* Initialize the security context and decode our challenge */ result = Curl_auth_decode_spnego_message(data, userp, passwdp, service, host, header, neg_ctx); if(result) Curl_auth_spnego_cleanup(neg_ctx); - else - /* If the status is different than 0 and we encountered no errors - it means we have to continue. 0 is the OK value for both GSSAPI - (GSS_S_COMPLETE) and SSPI (SEC_E_OK) */ - authp->done = !neg_ctx->status; return result; } diff --git a/lib/http_ntlm.c b/lib/http_ntlm.c index a9b33f98e..aaf8a3deb 100644 --- a/lib/http_ntlm.c +++ b/lib/http_ntlm.c @@ -175,6 +175,9 @@ CURLcode Curl_output_ntlm(struct connectdata *conn, bool proxy) if(s_hSecDll == NULL) return err; } +#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS + ntlm->sslContext = conn->sslContext; +#endif #endif switch(ntlm->state) { diff --git a/lib/http_proxy.c b/lib/http_proxy.c index 56b57b172..1b4294a3c 100644 --- a/lib/http_proxy.c +++ b/lib/http_proxy.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -643,7 +643,7 @@ static CURLcode CONNECT(struct connectdata *conn, void Curl_connect_free(struct Curl_easy *data) { - struct connectdata *conn = data->easy_conn; + struct connectdata *conn = data->conn; struct http_connect_state *s = conn->connect_state; if(s) { free(s); diff --git a/lib/if2ip.c b/lib/if2ip.c index 566061a56..acbcff71e 100644 --- a/lib/if2ip.c +++ b/lib/if2ip.c @@ -96,24 +96,6 @@ unsigned int Curl_ipv6_scope(const struct sockaddr *sa) #if defined(HAVE_GETIFADDRS) -bool Curl_if_is_interface_name(const char *interf) -{ - bool result = FALSE; - - struct ifaddrs *iface, *head; - - if(getifaddrs(&head) >= 0) { - for(iface = head; iface != NULL; iface = iface->ifa_next) { - if(strcasecompare(iface->ifa_name, interf)) { - result = TRUE; - break; - } - } - freeifaddrs(head); - } - return result; -} - if2ip_result_t Curl_if2ip(int af, unsigned int remote_scope, unsigned int remote_scope_id, const char *interf, char *buf, int buf_size) @@ -196,15 +178,6 @@ if2ip_result_t Curl_if2ip(int af, unsigned int remote_scope, #elif defined(HAVE_IOCTL_SIOCGIFADDR) -bool Curl_if_is_interface_name(const char *interf) -{ - /* This is here just to support the old interfaces */ - char buf[256]; - - return (Curl_if2ip(AF_INET, 0 /* unused */, 0, interf, buf, sizeof(buf)) == - IF2IP_NOT_FOUND) ? FALSE : TRUE; -} - if2ip_result_t Curl_if2ip(int af, unsigned int remote_scope, unsigned int remote_scope_id, const char *interf, char *buf, int buf_size) @@ -251,13 +224,6 @@ if2ip_result_t Curl_if2ip(int af, unsigned int remote_scope, #else -bool Curl_if_is_interface_name(const char *interf) -{ - (void) interf; - - return FALSE; -} - if2ip_result_t Curl_if2ip(int af, unsigned int remote_scope, unsigned int remote_scope_id, const char *interf, char *buf, int buf_size) diff --git a/lib/if2ip.h b/lib/if2ip.h index a90e66216..a11b1c222 100644 --- a/lib/if2ip.h +++ b/lib/if2ip.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -32,8 +32,6 @@ unsigned int Curl_ipv6_scope(const struct sockaddr *sa); -bool Curl_if_is_interface_name(const char *interf); - typedef enum { IF2IP_NOT_FOUND = 0, /* Interface not found */ IF2IP_AF_NOT_SUPPORTED = 1, /* Int. exists but has no address for this af */ diff --git a/lib/imap.c b/lib/imap.c index 8d1849df3..dab893cf7 100644 --- a/lib/imap.c +++ b/lib/imap.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -316,7 +316,7 @@ static bool imap_endofresp(struct connectdata *conn, char *line, size_t len, a space and optionally some text as per RFC-3501 for the AUTHENTICATE and APPEND commands and as outlined in Section 4. Examples of RFC-4959 but some e-mail servers ignore this and only send a single + instead. */ - if(imap && !imap->custom && ((len == 3 && !memcmp("+", line, 1)) || + if(imap && !imap->custom && ((len == 3 && line[0] == '+') || (len >= 2 && !memcmp("+ ", line, 2)))) { switch(imapc->state) { /* States which are interested in continuation responses */ @@ -1362,19 +1362,20 @@ static CURLcode imap_multi_statemach(struct connectdata *conn, bool *done) return result; } - result = Curl_pp_statemach(&imapc->pp, FALSE); + result = Curl_pp_statemach(&imapc->pp, FALSE, FALSE); *done = (imapc->state == IMAP_STOP) ? TRUE : FALSE; return result; } -static CURLcode imap_block_statemach(struct connectdata *conn) +static CURLcode imap_block_statemach(struct connectdata *conn, + bool disconnecting) { CURLcode result = CURLE_OK; struct imap_conn *imapc = &conn->proto.imapc; while(imapc->state != IMAP_STOP && !result) - result = Curl_pp_statemach(&imapc->pp, TRUE); + result = Curl_pp_statemach(&imapc->pp, TRUE, disconnecting); return result; } @@ -1497,7 +1498,7 @@ static CURLcode imap_done(struct connectdata *conn, CURLcode status, non-blocking DONE operations! */ if(!result) - result = imap_block_statemach(conn); + result = imap_block_statemach(conn, FALSE); } /* Cleanup our per-request based variables */ @@ -1635,7 +1636,7 @@ static CURLcode imap_disconnect(struct connectdata *conn, bool dead_connection) point! */ if(!dead_connection && imapc->pp.conn && imapc->pp.conn->bits.protoconnstart) if(!imap_perform_logout(conn)) - (void)imap_block_statemach(conn); /* ignore errors on LOGOUT */ + (void)imap_block_statemach(conn, TRUE); /* ignore errors on LOGOUT */ /* Disconnect from the server */ Curl_pp_disconnect(&imapc->pp); diff --git a/lib/multi.c b/lib/multi.c index 311bde09c..5c09a04f5 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -114,7 +114,7 @@ static void Curl_init_completed(struct Curl_easy *data) /* Important: reset the conn pointer so that we don't point to memory that could be freed anytime */ - data->easy_conn = NULL; + Curl_detach_connnection(data); Curl_expire_clear(data); /* stop all timers */ } @@ -163,8 +163,8 @@ static void mstate(struct Curl_easy *data, CURLMstate state data->mstate < CURLM_STATE_COMPLETED) { long connection_id = -5000; - if(data->easy_conn) - connection_id = data->easy_conn->connection_id; + if(data->conn) + connection_id = data->conn->connection_id; infof(data, "STATE: %s => %s handle %p; line %d (connection #%ld)\n", @@ -189,14 +189,17 @@ static void mstate(struct Curl_easy *data, CURLMstate state #endif /* - * We add one of these structs to the sockhash for a particular socket + * We add one of these structs to the sockhash for each socket */ struct Curl_sh_entry { - struct Curl_easy *easy; - int action; /* what action READ/WRITE this socket waits for */ - curl_socket_t socket; /* mainly to ease debugging */ + struct curl_llist list; /* list of easy handles using this socket */ + unsigned int action; /* what combined action READ/WRITE this socket waits + for */ void *socketp; /* settable by users with curl_multi_assign() */ + unsigned int users; /* number of transfers using this */ + 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 */ @@ -215,8 +218,7 @@ static struct Curl_sh_entry *sh_getentry(struct curl_hash *sh, /* make sure this socket is present in the hash for this handle */ static struct Curl_sh_entry *sh_addentry(struct curl_hash *sh, - curl_socket_t s, - struct Curl_easy *data) + curl_socket_t s) { struct Curl_sh_entry *there = sh_getentry(sh, s); struct Curl_sh_entry *check; @@ -230,8 +232,7 @@ static struct Curl_sh_entry *sh_addentry(struct curl_hash *sh, if(!check) return NULL; /* major failure */ - check->easy = data; - check->socket = s; + Curl_llist_init(&check->list, NULL); /* make/add new hash entry */ if(!Curl_hash_add(sh, (char *)&s, sizeof(curl_socket_t), check)) { @@ -516,31 +517,23 @@ static void debug_print_sock_hash(void *p) } #endif -static CURLcode multi_done(struct connectdata **connp, - CURLcode status, /* an error if this is called - after an error was detected */ - bool premature) +static CURLcode multi_done(struct Curl_easy *data, + CURLcode status, /* an error if this is called + after an error was detected */ + bool premature) { CURLcode result; - struct connectdata *conn; - struct Curl_easy *data; + struct connectdata *conn = data->conn; unsigned int i; - DEBUGASSERT(*connp); - - conn = *connp; - data = conn->data; - DEBUGF(infof(data, "multi_done\n")); if(data->state.done) /* Stop if multi_done() has already been called */ return CURLE_OK; - if(data->mstate == CURLM_STATE_WAITRESOLVE) { - /* still waiting for the resolve to complete */ - (void)Curl_resolver_wait_resolv(conn, NULL); - } + /* Stop the resolver and free its own resources (but not dns_entry yet). */ + Curl_resolver_kill(conn); Curl_getoff_all_pipelines(data, conn); @@ -579,7 +572,7 @@ static CURLcode multi_done(struct connectdata **connp, if(conn->send_pipe.size || conn->recv_pipe.size) { /* Stop if pipeline is not empty . */ - data->easy_conn = NULL; + Curl_detach_connnection(data); DEBUGF(infof(data, "Connection still in use %zu/%zu, " "no more multi_done now!\n", conn->send_pipe.size, conn->recv_pipe.size)); @@ -587,7 +580,6 @@ static CURLcode multi_done(struct connectdata **connp, } data->state.done = TRUE; /* called just now! */ - Curl_resolver_cancel(conn); if(conn->dns_entry) { Curl_resolv_unlock(data, conn->dns_entry); /* done with this */ @@ -653,10 +645,7 @@ static CURLcode multi_done(struct connectdata **connp, data->state.lastconnect = NULL; } - *connp = NULL; /* to make the caller of this function better detect that - this was either closed or handed over to the connection - cache here, and therefore cannot be used from this point on - */ + Curl_detach_connnection(data); Curl_free_request_state(data); return result; } @@ -685,7 +674,7 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi, return CURLM_RECURSIVE_API_CALL; premature = (data->mstate < CURLM_STATE_COMPLETED) ? TRUE : FALSE; - easy_owns_conn = (data->easy_conn && (data->easy_conn->data == easy)) ? + easy_owns_conn = (data->conn && (data->conn->data == easy)) ? TRUE : FALSE; /* If the 'state' is not INIT or COMPLETED, we might need to do something @@ -696,16 +685,16 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi, multi->num_alive--; } - if(data->easy_conn && + if(data->conn && data->mstate > CURLM_STATE_DO && data->mstate < CURLM_STATE_COMPLETED) { /* Set connection owner so that the DONE function closes it. We can safely do this here since connection is killed. */ - data->easy_conn->data = easy; + data->conn->data = easy; /* If the handle is in a pipeline and has started sending off its request but not received its response yet, we need to close connection. */ - streamclose(data->easy_conn, "Removed with partial response"); + streamclose(data->conn, "Removed with partial response"); easy_owns_conn = TRUE; } @@ -714,7 +703,7 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi, curl_easy_cleanup is called. */ Curl_expire_clear(data); - if(data->easy_conn) { + if(data->conn) { /* we must call multi_done() here (if we still own the connection) so that we don't leave a half-baked one around */ @@ -725,11 +714,11 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi, Note that this ignores the return code simply because there's nothing really useful to do with it anyway! */ - (void)multi_done(&data->easy_conn, data->result, premature); + (void)multi_done(data, data->result, premature); } else /* Clear connection pipelines, if multi_done above was not called */ - Curl_getoff_all_pipelines(data, data->easy_conn); + Curl_getoff_all_pipelines(data, data->conn); } if(data->connect_queue.ptr) @@ -761,9 +750,9 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi, vanish with this handle */ /* Remove the association between the connection and the handle */ - if(data->easy_conn) { - data->easy_conn->data = NULL; - data->easy_conn = NULL; + if(data->conn) { + data->conn->data = NULL; + Curl_detach_connnection(data); } #ifdef USE_LIBPSL @@ -813,9 +802,19 @@ bool Curl_pipeline_wanted(const struct Curl_multi *multi, int bits) return (multi && (multi->pipelining & bits)) ? TRUE : FALSE; } -void Curl_multi_handlePipeBreak(struct Curl_easy *data) +/* This is the only function that should clear data->conn. This will + occasionally be called with the pointer already cleared. */ +void Curl_detach_connnection(struct Curl_easy *data) +{ + data->conn = NULL; +} + +/* This is the only function that should assign data->conn */ +void Curl_attach_connnection(struct Curl_easy *data, + struct connectdata *conn) { - data->easy_conn = NULL; + DEBUGASSERT(!data->conn); + data->conn = conn; } static int waitconnect_getsock(struct connectdata *conn, @@ -879,13 +878,13 @@ static int multi_getsock(struct Curl_easy *data, /* The no connection case can happen when this is called from curl_multi_remove_handle() => singlesocket() => multi_getsock(). */ - if(!data->easy_conn) + if(!data->conn) return 0; if(data->mstate > CURLM_STATE_CONNECT && data->mstate < CURLM_STATE_COMPLETED) { /* Set up ownership correctly */ - data->easy_conn->data = data; + data->conn->data = data; } switch(data->mstate) { @@ -906,31 +905,31 @@ static int multi_getsock(struct Curl_easy *data, return 0; case CURLM_STATE_WAITRESOLVE: - return Curl_resolv_getsock(data->easy_conn, socks, numsocks); + return Curl_resolv_getsock(data->conn, socks, numsocks); case CURLM_STATE_PROTOCONNECT: case CURLM_STATE_SENDPROTOCONNECT: - return Curl_protocol_getsock(data->easy_conn, socks, numsocks); + return Curl_protocol_getsock(data->conn, socks, numsocks); case CURLM_STATE_DO: case CURLM_STATE_DOING: - return Curl_doing_getsock(data->easy_conn, socks, numsocks); + return Curl_doing_getsock(data->conn, socks, numsocks); case CURLM_STATE_WAITPROXYCONNECT: - return waitproxyconnect_getsock(data->easy_conn, socks, numsocks); + return waitproxyconnect_getsock(data->conn, socks, numsocks); case CURLM_STATE_WAITCONNECT: - return waitconnect_getsock(data->easy_conn, socks, numsocks); + return waitconnect_getsock(data->conn, socks, numsocks); case CURLM_STATE_DO_MORE: - return domore_getsock(data->easy_conn, socks, numsocks); + return domore_getsock(data->conn, socks, numsocks); case CURLM_STATE_DO_DONE: /* since is set after DO is completed, we switch to waiting for the same as the *PERFORM states */ case CURLM_STATE_PERFORM: case CURLM_STATE_WAITPERFORM: - return Curl_single_getsock(data->easy_conn, socks, numsocks); + return Curl_single_getsock(data->conn, socks, numsocks); } } @@ -1202,17 +1201,16 @@ CURLMcode Curl_multi_add_perform(struct Curl_multi *multi, /* take this handle to the perform state right away */ multistate(data, CURLM_STATE_PERFORM); - data->easy_conn = conn; + Curl_attach_connnection(data, conn); k->keepon |= KEEP_RECV; /* setup to receive! */ } return rc; } -static CURLcode multi_reconnect_request(struct connectdata **connp) +static CURLcode multi_reconnect_request(struct Curl_easy *data) { CURLcode result = CURLE_OK; - struct connectdata *conn = *connp; - struct Curl_easy *data = conn->data; + struct connectdata *conn = data->conn; /* This was a re-use of a connection and we got a write error in the * DO-phase. Then we DISCONNECT this connection and have another attempt to @@ -1223,11 +1221,9 @@ static CURLcode multi_reconnect_request(struct connectdata **connp) infof(data, "Re-used connection seems dead, get a new one\n"); connclose(conn, "Reconnect dead connection"); /* enforce close */ - result = multi_done(&conn, result, FALSE); /* we are so done with this */ + result = multi_done(data, result, FALSE); /* we are so done with this */ - /* conn may no longer be a good pointer, clear it to avoid mistakes by - parent functions */ - *connp = NULL; + /* data->conn was detached in multi_done() */ /* * We need to check for CURLE_SEND_ERROR here as well. This could happen @@ -1239,11 +1235,11 @@ static CURLcode multi_reconnect_request(struct connectdata **connp) bool protocol_done = TRUE; /* Now, redo the connect and get a new connection */ - result = Curl_connect(data, connp, &async, &protocol_done); + result = Curl_connect(data, &async, &protocol_done); if(!result) { /* We have connected or sent away a name resolve query fine */ - conn = *connp; /* setup conn to again point to something nice */ + conn = data->conn; /* in case it was updated */ if(async) { /* Now, if async is TRUE here, we need to wait for the name to resolve */ @@ -1276,11 +1272,10 @@ static void do_complete(struct connectdata *conn) Curl_pgrsTime(conn->data, TIMER_PRETRANSFER); } -static CURLcode multi_do(struct connectdata **connp, bool *done) +static CURLcode multi_do(struct Curl_easy *data, bool *done) { CURLcode result = CURLE_OK; - struct connectdata *conn = *connp; - struct Curl_easy *data = conn->data; + struct connectdata *conn = data->conn; if(conn->handler->do_it) { /* generic protocol-specific function pointer set in curl_connect() */ @@ -1294,12 +1289,12 @@ static CURLcode multi_do(struct connectdata **connp, bool *done) * figure out how to re-establish the connection. */ if(!data->multi) { - result = multi_reconnect_request(connp); + result = multi_reconnect_request(data); if(!result) { /* ... finally back to actually retry the DO phase */ - conn = *connp; /* re-assign conn since multi_reconnect_request - creates a new connection */ + conn = data->conn; /* re-assign conn since multi_reconnect_request + creates a new connection */ result = conn->handler->do_it(conn, done); } } @@ -1368,13 +1363,13 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, bool stream_error = FALSE; rc = CURLM_OK; - if(!data->easy_conn && + if(!data->conn && data->mstate > CURLM_STATE_CONNECT && data->mstate < CURLM_STATE_DONE) { - /* In all these states, the code will blindly access 'data->easy_conn' + /* In all these states, the code will blindly access 'data->conn' so this is precaution that it isn't NULL. And it silences static analyzers. */ - failf(data, "In state %d with no easy_conn, bail out!\n", data->mstate); + failf(data, "In state %d with no conn, bail out!\n", data->mstate); return CURLM_INTERNAL_ERROR; } @@ -1383,13 +1378,13 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, process_pending_handles(multi); /* pipelined/multiplexed */ } - if(data->easy_conn && data->mstate > CURLM_STATE_CONNECT && + if(data->conn && data->mstate > CURLM_STATE_CONNECT && data->mstate < CURLM_STATE_COMPLETED) { /* Make sure we set the connection's current owner */ - data->easy_conn->data = data; + data->conn->data = data; } - if(data->easy_conn && + if(data->conn && (data->mstate >= CURLM_STATE_CONNECT) && (data->mstate < CURLM_STATE_COMPLETED)) { /* we need to wait for the connect state as only then is the start time @@ -1401,23 +1396,26 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, if(timeout_ms < 0) { /* Handle timed out */ if(data->mstate == CURLM_STATE_WAITRESOLVE) - failf(data, "Resolving timed out after %ld milliseconds", + failf(data, "Resolving timed out after %" CURL_FORMAT_TIMEDIFF_T + " milliseconds", Curl_timediff(now, data->progress.t_startsingle)); else if(data->mstate == CURLM_STATE_WAITCONNECT) - failf(data, "Connection timed out after %ld milliseconds", + failf(data, "Connection timed out after %" CURL_FORMAT_TIMEDIFF_T + " milliseconds", Curl_timediff(now, data->progress.t_startsingle)); else { k = &data->req; if(k->size != -1) { - failf(data, "Operation timed out after %ld milliseconds with %" - CURL_FORMAT_CURL_OFF_T " out of %" + failf(data, "Operation timed out after %" CURL_FORMAT_TIMEDIFF_T + " milliseconds with %" CURL_FORMAT_CURL_OFF_T " out of %" CURL_FORMAT_CURL_OFF_T " bytes received", Curl_timediff(now, data->progress.t_startsingle), k->bytecount, k->size); } else { - failf(data, "Operation timed out after %ld milliseconds with %" - CURL_FORMAT_CURL_OFF_T " bytes received", + failf(data, "Operation timed out after %" CURL_FORMAT_TIMEDIFF_T + " milliseconds with %" CURL_FORMAT_CURL_OFF_T + " bytes received", Curl_timediff(now, data->progress.t_startsingle), k->bytecount); } @@ -1425,11 +1423,11 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* Force connection closed if the connection has indeed been used */ if(data->mstate > CURLM_STATE_DO) { - streamclose(data->easy_conn, "Disconnected with pending data"); + streamclose(data->conn, "Disconnected with pending data"); stream_error = TRUE; } result = CURLE_OPERATION_TIMEDOUT; - (void)multi_done(&data->easy_conn, result, TRUE); + (void)multi_done(data, result, TRUE); /* Skip the statemachine and go directly to error handling section. */ goto statemachine_end; } @@ -1456,8 +1454,13 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, case CURLM_STATE_CONNECT: /* Connect. We want to get a connection identifier filled in. */ Curl_pgrsTime(data, TIMER_STARTSINGLE); - result = Curl_connect(data, &data->easy_conn, - &async, &protocol_connect); + if(data->set.timeout) + Curl_expire(data, data->set.timeout, EXPIRE_TIMEOUT); + + if(data->set.connecttimeout) + Curl_expire(data, data->set.connecttimeout, EXPIRE_CONNECTTIMEOUT); + + result = Curl_connect(data, &async, &protocol_connect); if(CURLE_NO_CONNECTION_AVAILABLE == result) { /* There was no connection available. We will go to the pending state and wait for an available connection. */ @@ -1472,7 +1475,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, if(!result) { /* Add this handle to the send or pend pipeline */ - result = Curl_add_handle_to_pipeline(data, data->easy_conn); + result = Curl_add_handle_to_pipeline(data, data->conn); if(result) stream_error = TRUE; else { @@ -1490,7 +1493,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, CURLM_STATE_WAITDO:CURLM_STATE_DO); else { #ifndef CURL_DISABLE_HTTP - if(Curl_connect_ongoing(data->easy_conn)) + if(Curl_connect_ongoing(data->conn)) multistate(data, CURLM_STATE_WAITPROXYCONNECT); else #endif @@ -1505,7 +1508,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* awaiting an asynch name resolve to complete */ { struct Curl_dns_entry *dns = NULL; - struct connectdata *conn = data->easy_conn; + struct connectdata *conn = data->conn; const char *hostname; if(conn->bits.httpproxy) @@ -1528,7 +1531,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, } if(!dns) - result = Curl_resolv_check(data->easy_conn, &dns); + result = Curl_resolv_check(data->conn, &dns); /* Update sockets here, because the socket(s) may have been closed and the application thus needs to be told, even if it @@ -1541,12 +1544,12 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, if(dns) { /* Perform the next step in the connection phase, and then move on to the WAITCONNECT state */ - result = Curl_once_resolved(data->easy_conn, &protocol_connect); + result = Curl_once_resolved(data->conn, &protocol_connect); if(result) /* if Curl_once_resolved() returns failure, the connection struct is already freed and gone */ - data->easy_conn = NULL; /* no more connection */ + Curl_detach_connnection(data); /* no more connection */ else { /* call again please so that we get the next socket setup */ rc = CURLM_CALL_MULTI_PERFORM; @@ -1555,7 +1558,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, CURLM_STATE_WAITDO:CURLM_STATE_DO); else { #ifndef CURL_DISABLE_HTTP - if(Curl_connect_ongoing(data->easy_conn)) + if(Curl_connect_ongoing(data->conn)) multistate(data, CURLM_STATE_WAITPROXYCONNECT); else #endif @@ -1575,19 +1578,19 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, #ifndef CURL_DISABLE_HTTP case CURLM_STATE_WAITPROXYCONNECT: /* this is HTTP-specific, but sending CONNECT to a proxy is HTTP... */ - result = Curl_http_connect(data->easy_conn, &protocol_connect); + result = Curl_http_connect(data->conn, &protocol_connect); - if(data->easy_conn->bits.proxy_connect_closed) { + if(data->conn->bits.proxy_connect_closed) { rc = CURLM_CALL_MULTI_PERFORM; /* connect back to proxy again */ result = CURLE_OK; - multi_done(&data->easy_conn, CURLE_OK, FALSE); + multi_done(data, CURLE_OK, FALSE); multistate(data, CURLM_STATE_CONNECT); } else if(!result) { - if((data->easy_conn->http_proxy.proxytype != CURLPROXY_HTTPS || - data->easy_conn->bits.proxy_ssl_connected[FIRSTSOCKET]) && - Curl_connect_complete(data->easy_conn)) { + if((data->conn->http_proxy.proxytype != CURLPROXY_HTTPS || + data->conn->bits.proxy_ssl_connected[FIRSTSOCKET]) && + Curl_connect_complete(data->conn)) { rc = CURLM_CALL_MULTI_PERFORM; /* initiate protocol connect phase */ multistate(data, CURLM_STATE_SENDPROTOCONNECT); @@ -1600,18 +1603,18 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, case CURLM_STATE_WAITCONNECT: /* awaiting a completion of an asynch TCP connect */ - result = Curl_is_connected(data->easy_conn, FIRSTSOCKET, &connected); + result = Curl_is_connected(data->conn, FIRSTSOCKET, &connected); if(connected && !result) { #ifndef CURL_DISABLE_HTTP - if((data->easy_conn->http_proxy.proxytype == CURLPROXY_HTTPS && - !data->easy_conn->bits.proxy_ssl_connected[FIRSTSOCKET]) || - Curl_connect_ongoing(data->easy_conn)) { + if((data->conn->http_proxy.proxytype == CURLPROXY_HTTPS && + !data->conn->bits.proxy_ssl_connected[FIRSTSOCKET]) || + Curl_connect_ongoing(data->conn)) { multistate(data, CURLM_STATE_WAITPROXYCONNECT); break; } #endif rc = CURLM_CALL_MULTI_PERFORM; - multistate(data, data->easy_conn->bits.tunnel_proxy? + multistate(data, data->conn->bits.tunnel_proxy? CURLM_STATE_WAITPROXYCONNECT: CURLM_STATE_SENDPROTOCONNECT); } @@ -1624,7 +1627,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, break; case CURLM_STATE_SENDPROTOCONNECT: - result = Curl_protocol_connect(data->easy_conn, &protocol_connect); + result = Curl_protocol_connect(data->conn, &protocol_connect); if(!result && !protocol_connect) /* switch to waiting state */ multistate(data, CURLM_STATE_PROTOCONNECT); @@ -1637,14 +1640,14 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, else if(result) { /* failure detected */ Curl_posttransfer(data); - multi_done(&data->easy_conn, result, TRUE); + multi_done(data, result, TRUE); stream_error = TRUE; } break; case CURLM_STATE_PROTOCONNECT: /* protocol-specific connect phase */ - result = Curl_protocol_connecting(data->easy_conn, &protocol_connect); + result = Curl_protocol_connecting(data->conn, &protocol_connect); if(!result && protocol_connect) { /* after the connect has completed, go WAITDO or DO */ multistate(data, Curl_pipeline_wanted(multi, CURLPIPE_HTTP1)? @@ -1654,14 +1657,14 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, else if(result) { /* failure detected */ Curl_posttransfer(data); - multi_done(&data->easy_conn, result, TRUE); + multi_done(data, result, TRUE); stream_error = TRUE; } break; case CURLM_STATE_WAITDO: /* Wait for our turn to DO when we're pipelining requests */ - if(Curl_pipeline_checkget_write(data, data->easy_conn)) { + if(Curl_pipeline_checkget_write(data, data->conn)) { /* Grabbed the channel */ multistate(data, CURLM_STATE_DO); rc = CURLM_CALL_MULTI_PERFORM; @@ -1671,16 +1674,16 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, case CURLM_STATE_DO: if(data->set.connect_only) { /* keep connection open for application to use the socket */ - connkeep(data->easy_conn, "CONNECT_ONLY"); + connkeep(data->conn, "CONNECT_ONLY"); multistate(data, CURLM_STATE_DONE); result = CURLE_OK; rc = CURLM_CALL_MULTI_PERFORM; } else { /* Perform the protocol's DO action */ - result = multi_do(&data->easy_conn, &dophase_done); + result = multi_do(data, &dophase_done); - /* When multi_do() returns failure, data->easy_conn might be NULL! */ + /* When multi_do() returns failure, data->conn might be NULL! */ if(!result) { if(!dophase_done) { @@ -1689,7 +1692,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, struct WildcardData *wc = &data->wildcard; if(wc->state == CURLWC_DONE || wc->state == CURLWC_SKIP) { /* skip some states if it is important */ - multi_done(&data->easy_conn, CURLE_OK, FALSE); + multi_done(data, CURLE_OK, FALSE); multistate(data, CURLM_STATE_DONE); rc = CURLM_CALL_MULTI_PERFORM; break; @@ -1702,7 +1705,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, } /* after DO, go DO_DONE... or DO_MORE */ - else if(data->easy_conn->bits.do_more) { + else if(data->conn->bits.do_more) { /* we're supposed to do more, but we need to sit down, relax and wait a little while first */ multistate(data, CURLM_STATE_DO_MORE); @@ -1715,7 +1718,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, } } else if((CURLE_SEND_ERROR == result) && - data->easy_conn->bits.reuse) { + data->conn->bits.reuse) { /* * In this situation, a connection that we were trying to use * may have unexpectedly died. If possible, send the connection @@ -1725,7 +1728,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, followtype follow = FOLLOW_NONE; CURLcode drc; - drc = Curl_retry_request(data->easy_conn, &newurl); + drc = Curl_retry_request(data->conn, &newurl); if(drc) { /* a failure here pretty much implies an out of memory */ result = drc; @@ -1733,7 +1736,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, } Curl_posttransfer(data); - drc = multi_done(&data->easy_conn, result, FALSE); + drc = multi_done(data, result, FALSE); /* When set to retry the connection, we must to go back to * the CONNECT state */ @@ -1765,8 +1768,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, else { /* failure detected */ Curl_posttransfer(data); - if(data->easy_conn) - multi_done(&data->easy_conn, result, FALSE); + if(data->conn) + multi_done(data, result, FALSE); stream_error = TRUE; } } @@ -1774,12 +1777,12 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, case CURLM_STATE_DOING: /* we continue DOING until the DO phase is complete */ - result = Curl_protocol_doing(data->easy_conn, + result = Curl_protocol_doing(data->conn, &dophase_done); if(!result) { if(dophase_done) { /* after DO, go DO_DONE or DO_MORE */ - multistate(data, data->easy_conn->bits.do_more? + multistate(data, data->conn->bits.do_more? CURLM_STATE_DO_MORE: CURLM_STATE_DO_DONE); rc = CURLM_CALL_MULTI_PERFORM; @@ -1788,7 +1791,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, else { /* failure detected */ Curl_posttransfer(data); - multi_done(&data->easy_conn, result, FALSE); + multi_done(data, result, FALSE); stream_error = TRUE; } break; @@ -1797,7 +1800,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* * When we are connected, DO MORE and then go DO_DONE */ - result = multi_do_more(data->easy_conn, &control); + result = multi_do_more(data->conn, &control); /* No need to remove this handle from the send pipeline here since that is done in multi_done() */ @@ -1817,27 +1820,27 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, else { /* failure detected */ Curl_posttransfer(data); - multi_done(&data->easy_conn, result, FALSE); + multi_done(data, result, FALSE); stream_error = TRUE; } break; case CURLM_STATE_DO_DONE: /* Move ourselves from the send to recv pipeline */ - Curl_move_handle_from_send_to_recv_pipe(data, data->easy_conn); + Curl_move_handle_from_send_to_recv_pipe(data, data->conn); - if(data->easy_conn->bits.multiplex || data->easy_conn->send_pipe.size) + if(data->conn->bits.multiplex || data->conn->send_pipe.size) /* Check if we can move pending requests to send pipe */ process_pending_handles(multi); /* pipelined/multiplexed */ /* Only perform the transfer if there's a good socket to work with. Having both BAD is a signal to skip immediately to DONE */ - if((data->easy_conn->sockfd != CURL_SOCKET_BAD) || - (data->easy_conn->writesockfd != CURL_SOCKET_BAD)) + if((data->conn->sockfd != CURL_SOCKET_BAD) || + (data->conn->writesockfd != CURL_SOCKET_BAD)) multistate(data, CURLM_STATE_WAITPERFORM); else { if(data->state.wildcardmatch && - ((data->easy_conn->handler->flags & PROTOPT_WILDCARD) == 0)) { + ((data->conn->handler->flags & PROTOPT_WILDCARD) == 0)) { data->wildcard.state = CURLWC_DONE; } multistate(data, CURLM_STATE_DONE); @@ -1847,7 +1850,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, case CURLM_STATE_WAITPERFORM: /* Wait for our turn to PERFORM */ - if(Curl_pipeline_checkget_read(data, data->easy_conn)) { + if(Curl_pipeline_checkget_read(data, data->conn)) { /* Grabbed the channel */ multistate(data, CURLM_STATE_PERFORM); rc = CURLM_CALL_MULTI_PERFORM; @@ -1856,7 +1859,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, case CURLM_STATE_TOOFAST: /* limit-rate exceeded in either direction */ /* if both rates are within spec, resume transfer */ - if(Curl_pgrsUpdate(data->easy_conn)) + if(Curl_pgrsUpdate(data->conn)) result = CURLE_ABORTED_BY_CALLBACK; else result = Curl_speedcheck(data, now); @@ -1926,24 +1929,24 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, } /* read/write data if it is ready to do so */ - result = Curl_readwrite(data->easy_conn, data, &done, &comeback); + result = Curl_readwrite(data->conn, data, &done, &comeback); k = &data->req; if(!(k->keepon & KEEP_RECV)) /* We're done receiving */ - Curl_pipeline_leave_read(data->easy_conn); + Curl_pipeline_leave_read(data->conn); if(!(k->keepon & KEEP_SEND)) /* We're done sending */ - Curl_pipeline_leave_write(data->easy_conn); + Curl_pipeline_leave_write(data->conn); if(done || (result == CURLE_RECV_ERROR)) { /* If CURLE_RECV_ERROR happens early enough, we assume it was a race * condition and the server closed the re-used connection exactly when * we wanted to use it, so figure out if that is indeed the case. */ - CURLcode ret = Curl_retry_request(data->easy_conn, &newurl); + CURLcode ret = Curl_retry_request(data->conn, &newurl); if(!ret) retry = (newurl)?TRUE:FALSE; else if(!result) @@ -1957,8 +1960,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, } } else if((CURLE_HTTP2_STREAM == result) && - Curl_h2_http_1_1_error(data->easy_conn)) { - CURLcode ret = Curl_retry_request(data->easy_conn, &newurl); + Curl_h2_http_1_1_error(data->conn)) { + CURLcode ret = Curl_retry_request(data->conn, &newurl); infof(data, "Forcing HTTP/1.1 for NTLM"); data->set.httpversion = CURL_HTTP_VERSION_1_1; @@ -1985,12 +1988,12 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, * happened in the data connection. */ - if(!(data->easy_conn->handler->flags & PROTOPT_DUAL) && + if(!(data->conn->handler->flags & PROTOPT_DUAL) && result != CURLE_HTTP2_STREAM) - streamclose(data->easy_conn, "Transfer returned error"); + streamclose(data->conn, "Transfer returned error"); Curl_posttransfer(data); - multi_done(&data->easy_conn, result, TRUE); + multi_done(data, result, TRUE); } else if(done) { followtype follow = FOLLOW_NONE; @@ -1999,11 +2002,11 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, Curl_posttransfer(data); /* we're no longer receiving */ - Curl_removeHandleFromPipeline(data, &data->easy_conn->recv_pipe); + Curl_removeHandleFromPipeline(data, &data->conn->recv_pipe); /* expire the new receiving pipeline head */ - if(data->easy_conn->recv_pipe.head) - Curl_expire(data->easy_conn->recv_pipe.head->ptr, 0, EXPIRE_RUN_NOW); + if(data->conn->recv_pipe.head) + Curl_expire(data->conn->recv_pipe.head->ptr, 0, EXPIRE_RUN_NOW); /* When we follow redirects or is set to retry the connection, we must to go back to the CONNECT state */ @@ -2018,7 +2021,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, } else follow = FOLLOW_RETRY; - result = multi_done(&data->easy_conn, CURLE_OK, FALSE); + result = multi_done(data, CURLE_OK, FALSE); if(!result) { result = Curl_follow(data, newurl, follow); if(!result) { @@ -2041,7 +2044,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, free(newurl); if(result) { stream_error = TRUE; - result = multi_done(&data->easy_conn, result, TRUE); + result = multi_done(data, result, TRUE); } } @@ -2060,18 +2063,18 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* this state is highly transient, so run another loop after this */ rc = CURLM_CALL_MULTI_PERFORM; - if(data->easy_conn) { + if(data->conn) { CURLcode res; /* Remove ourselves from the receive pipeline, if we are there. */ - Curl_removeHandleFromPipeline(data, &data->easy_conn->recv_pipe); + Curl_removeHandleFromPipeline(data, &data->conn->recv_pipe); - if(data->easy_conn->bits.multiplex || data->easy_conn->send_pipe.size) + if(data->conn->bits.multiplex || data->conn->send_pipe.size) /* Check if we can move pending requests to connection */ process_pending_handles(multi); /* pipelined/multiplexing */ /* post-transfer command */ - res = multi_done(&data->easy_conn, result, FALSE); + res = multi_done(data, result, FALSE); /* allow a previously set error code take precedence */ if(!result) @@ -2079,12 +2082,12 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* * If there are other handles on the pipeline, multi_done won't set - * easy_conn to NULL. In such a case, curl_multi_remove_handle() can + * conn to NULL. In such a case, curl_multi_remove_handle() can * access free'd data, if the connection is free'd and the handle * removed before we perform the processing in CURLM_STATE_COMPLETED */ - if(data->easy_conn) - data->easy_conn = NULL; + if(data->conn) + Curl_detach_connnection(data); } if(data->state.wildcardmatch) { @@ -2126,23 +2129,23 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* Check if we can move pending requests to send pipe */ process_pending_handles(multi); /* connection */ - if(data->easy_conn) { + if(data->conn) { /* if this has a connection, unsubscribe from the pipelines */ - Curl_pipeline_leave_write(data->easy_conn); - Curl_pipeline_leave_read(data->easy_conn); - Curl_removeHandleFromPipeline(data, &data->easy_conn->send_pipe); - Curl_removeHandleFromPipeline(data, &data->easy_conn->recv_pipe); + Curl_pipeline_leave_write(data->conn); + Curl_pipeline_leave_read(data->conn); + Curl_removeHandleFromPipeline(data, &data->conn->send_pipe); + Curl_removeHandleFromPipeline(data, &data->conn->recv_pipe); if(stream_error) { /* Don't attempt to send data over a connection that timed out */ bool dead_connection = result == CURLE_OPERATION_TIMEDOUT; /* disconnect properly */ - Curl_disconnect(data, data->easy_conn, dead_connection); + Curl_disconnect(data, data->conn, dead_connection); - /* This is where we make sure that the easy_conn pointer is reset. + /* This is where we make sure that the conn pointer is reset. We don't have to do this in every case block above where a failure is detected */ - data->easy_conn = NULL; + Curl_detach_connnection(data); } } else if(data->mstate == CURLM_STATE_CONNECT) { @@ -2154,11 +2157,11 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, rc = CURLM_CALL_MULTI_PERFORM; } /* if there's still a connection to use, call the progress function */ - else if(data->easy_conn && Curl_pgrsUpdate(data->easy_conn)) { + else if(data->conn && Curl_pgrsUpdate(data->conn)) { /* aborted due to progress callback return code must close the connection */ result = CURLE_ABORTED_BY_CALLBACK; - streamclose(data->easy_conn, "Aborted by callback"); + streamclose(data->conn, "Aborted by callback"); /* if not yet in DONE state, go there, otherwise COMPLETED */ multistate(data, (data->mstate < CURLM_STATE_DONE)? @@ -2181,7 +2184,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, msg->extmsg.data.result = result; rc = multi_addmsg(multi, msg); - DEBUGASSERT(!data->easy_conn); + DEBUGASSERT(!data->conn); } multistate(data, CURLM_STATE_MSGSENT); } @@ -2261,9 +2264,9 @@ CURLMcode curl_multi_cleanup(struct Curl_multi *multi) data = multi->easyp; while(data) { nextdata = data->next; - if(!data->state.done && data->easy_conn) + if(!data->state.done && data->conn) /* if DONE was never called for this handle */ - (void)multi_done(&data->easy_conn, CURLE_OK, TRUE); + (void)multi_done(data, CURLE_OK, TRUE); if(data->dns.hostcachetype == HCACHE_MULTI) { /* clear out the usage of the shared DNS cache */ Curl_hostcache_clean(data, data->dns.hostcache); @@ -2356,6 +2359,9 @@ static CURLMcode singlesocket(struct Curl_multi *multi, curl_socket_t s; int num; unsigned int curraction; + int actions[MAX_SOCKSPEREASYHANDLE]; + unsigned int comboaction; + bool sincebefore = FALSE; for(i = 0; i< MAX_SOCKSPEREASYHANDLE; i++) socks[i] = CURL_SOCKET_BAD; @@ -2372,7 +2378,8 @@ static CURLMcode singlesocket(struct Curl_multi *multi, for(i = 0; (i< MAX_SOCKSPEREASYHANDLE) && (curraction & (GETSOCK_READSOCK(i) | GETSOCK_WRITESOCK(i))); i++) { - int action = CURL_POLL_NONE; + unsigned int action = CURL_POLL_NONE; + unsigned int prevaction = 0; s = socks[i]; @@ -2384,29 +2391,70 @@ static CURLMcode singlesocket(struct Curl_multi *multi, if(curraction & GETSOCK_WRITESOCK(i)) action |= CURL_POLL_OUT; + actions[i] = action; if(entry) { - /* yeps, already present so check if it has the same action set */ - if(entry->action == action) - /* same, continue */ - continue; + /* check if new for this transfer */ + for(i = 0; i< data->numsocks; i++) { + if(s == data->sockets[i]) { + prevaction = data->actions[i]; + sincebefore = TRUE; + break; + } + } + } else { - /* this is a socket we didn't have before, add it! */ - entry = sh_addentry(&multi->sockhash, s, data); + /* this is a socket we didn't have before, add it to the hash! */ + entry = sh_addentry(&multi->sockhash, s); if(!entry) /* fatal */ return CURLM_OUT_OF_MEMORY; } + if(sincebefore && (prevaction != action)) { + /* Socket was used already, but different action now */ + if(prevaction & CURL_POLL_IN) + entry->readers--; + if(prevaction & CURL_POLL_OUT) + entry->writers--; + if(action & CURL_POLL_IN) + entry->readers++; + if(action & CURL_POLL_OUT) + entry->writers++; + } + else if(!sincebefore) { + /* a new user */ + entry->users++; + if(action & CURL_POLL_IN) + entry->readers++; + if(action & CURL_POLL_OUT) + entry->writers++; + + /* add 'data' to the list of handles using this socket! */ + Curl_llist_insert_next(&entry->list, entry->list.tail, + data, &data->sh_queue); + } + + comboaction = (entry->writers? CURL_POLL_OUT : 0) | + (entry->readers ? CURL_POLL_IN : 0); + +#if 0 + infof(data, "--- Comboaction: %u readers %u writers\n", + entry->readers, entry->writers); +#endif + /* check if it has the same action set */ + if(entry->action == comboaction) + /* same, continue */ + continue; /* we know (entry != NULL) at this point, see the logic above */ if(multi->socket_cb) multi->socket_cb(data, s, - action, + comboaction, multi->socket_userp, entry->socketp); - entry->action = action; /* store the current action state */ + entry->action = comboaction; /* store the current action state */ } num = i; /* number of sockets */ @@ -2415,73 +2463,45 @@ static CURLMcode singlesocket(struct Curl_multi *multi, make sure to detect sockets that are removed */ for(i = 0; i< data->numsocks; i++) { int j; + bool stillused = FALSE; s = data->sockets[i]; - for(j = 0; j<num; j++) { + for(j = 0; j < num; j++) { if(s == socks[j]) { /* this is still supervised */ - s = CURL_SOCKET_BAD; + stillused = TRUE; break; } } + if(stillused) + continue; entry = sh_getentry(&multi->sockhash, s); + /* if this is NULL here, the socket has been closed and notified so + already by Curl_multi_closed() */ if(entry) { - /* this socket has been removed. Tell the app to remove it */ - bool remove_sock_from_hash = TRUE; - - /* check if the socket to be removed serves a connection which has - other easy-s in a pipeline. In this case the socket should not be - removed. */ - struct connectdata *easy_conn = data->easy_conn; - if(easy_conn) { - if(easy_conn->recv_pipe.size > 1) { - /* the handle should not be removed from the pipe yet */ - remove_sock_from_hash = FALSE; - - /* Update the sockhash entry to instead point to the next in line - for the recv_pipe, or the first (in case this particular easy - isn't already) */ - if(entry->easy == data) { - if(Curl_recvpipe_head(data, easy_conn)) - entry->easy = easy_conn->recv_pipe.head->next->ptr; - else - entry->easy = easy_conn->recv_pipe.head->ptr; - } - } - if(easy_conn->send_pipe.size > 1) { - /* the handle should not be removed from the pipe yet */ - remove_sock_from_hash = FALSE; - - /* Update the sockhash entry to instead point to the next in line - for the send_pipe, or the first (in case this particular easy - isn't already) */ - if(entry->easy == data) { - if(Curl_sendpipe_head(data, easy_conn)) - entry->easy = easy_conn->send_pipe.head->next->ptr; - else - entry->easy = easy_conn->send_pipe.head->ptr; - } - } - /* Don't worry about overwriting recv_pipe head with send_pipe_head, - when action will be asked on the socket (see multi_socket()), the - head of the correct pipe will be taken according to the - action. */ - } - - if(remove_sock_from_hash) { - /* in this case 'entry' is always non-NULL */ + int oldactions = data->actions[i]; + /* this socket has been removed. Decrease user count */ + entry->users--; + if(oldactions & CURL_POLL_OUT) + entry->writers--; + if(oldactions & CURL_POLL_IN) + entry->readers--; + if(!entry->users) { if(multi->socket_cb) - multi->socket_cb(data, - s, - CURL_POLL_REMOVE, + multi->socket_cb(data, s, CURL_POLL_REMOVE, multi->socket_userp, entry->socketp); sh_delentry(&multi->sockhash, s); } - } /* if sockhash entry existed */ + else { + /* remove this transfer as a user of this socket */ + Curl_llist_remove(&entry->list, &data->sh_queue, NULL); + } + } } /* for loop over numsocks */ memcpy(data->sockets, socks, num*sizeof(curl_socket_t)); + memcpy(data->actions, actions, num*sizeof(int)); data->numsocks = num; return CURLM_OK; } @@ -2621,46 +2641,50 @@ static CURLMcode multi_socket(struct Curl_multi *multi, and just move on. */ ; else { + struct curl_llist *list = &entry->list; + struct curl_llist_element *e; SIGPIPE_VARIABLE(pipe_st); - data = entry->easy; - - if(data->magic != CURLEASY_MAGIC_NUMBER) - /* bad bad bad bad bad bad bad */ - return CURLM_INTERNAL_ERROR; - - /* If the pipeline is enabled, take the handle which is in the head of - the pipeline. If we should write into the socket, take the send_pipe - head. If we should read from the socket, take the recv_pipe head. */ - if(data->easy_conn) { - if((ev_bitmask & CURL_POLL_OUT) && - data->easy_conn->send_pipe.head) - data = data->easy_conn->send_pipe.head->ptr; - else if((ev_bitmask & CURL_POLL_IN) && - data->easy_conn->recv_pipe.head) - data = data->easy_conn->recv_pipe.head->ptr; - } + /* the socket can be shared by many transfers, iterate */ + for(e = list->head; e; e = e->next) { + data = (struct Curl_easy *)e->ptr; + + if(data->magic != CURLEASY_MAGIC_NUMBER) + /* bad bad bad bad bad bad bad */ + return CURLM_INTERNAL_ERROR; + + /* If the pipeline is enabled, take the handle which is in the head of + the pipeline. If we should write into the socket, take the + send_pipe head. If we should read from the socket, take the + recv_pipe head. */ + if(data->conn) { + if((ev_bitmask & CURL_POLL_OUT) && + data->conn->send_pipe.head) + data = data->conn->send_pipe.head->ptr; + else if((ev_bitmask & CURL_POLL_IN) && + data->conn->recv_pipe.head) + data = data->conn->recv_pipe.head->ptr; + } - if(data->easy_conn && - !(data->easy_conn->handler->flags & PROTOPT_DIRLOCK)) - /* set socket event bitmask if they're not locked */ - data->easy_conn->cselect_bits = ev_bitmask; + if(data->conn && !(data->conn->handler->flags & PROTOPT_DIRLOCK)) + /* set socket event bitmask if they're not locked */ + data->conn->cselect_bits = ev_bitmask; - sigpipe_ignore(data, &pipe_st); - result = multi_runsingle(multi, now, data); - sigpipe_restore(&pipe_st); + sigpipe_ignore(data, &pipe_st); + result = multi_runsingle(multi, now, data); + sigpipe_restore(&pipe_st); - if(data->easy_conn && - !(data->easy_conn->handler->flags & PROTOPT_DIRLOCK)) - /* clear the bitmask only if not locked */ - data->easy_conn->cselect_bits = 0; + if(data->conn && !(data->conn->handler->flags & PROTOPT_DIRLOCK)) + /* clear the bitmask only if not locked */ + data->conn->cselect_bits = 0; - if(CURLM_OK >= result) { - /* get the socket(s) and check if the state has been changed since - last */ - result = singlesocket(multi, data); - if(result) - return result; + if(CURLM_OK >= result) { + /* get the socket(s) and check if the state has been changed since + last */ + result = singlesocket(multi, data); + if(result) + return result; + } } /* Now we fall-through and do the timer-based stuff, since we don't want @@ -3004,6 +3028,9 @@ void Curl_expire(struct Curl_easy *data, time_t milli, expire_id id) DEBUGASSERT(id < EXPIRE_LAST); + infof(data, "Expire in %ld ms for %x (transfer %p)\n", + (long)milli, id, data); + set = Curl_now(); set.tv_sec += milli/1000; set.tv_usec += (unsigned int)(milli%1000)*1000; @@ -3095,7 +3122,7 @@ void Curl_expire_clear(struct Curl_easy *data) } #ifdef DEBUGBUILD - infof(data, "Expire cleared\n"); + infof(data, "Expire cleared (transfer %p)\n", data); #endif nowp->tv_sec = 0; nowp->tv_usec = 0; diff --git a/lib/multiif.h b/lib/multiif.h index e44646bf9..ed35ef427 100644 --- a/lib/multiif.h +++ b/lib/multiif.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -31,7 +31,9 @@ void Curl_expire(struct Curl_easy *data, time_t milli, expire_id); void Curl_expire_clear(struct Curl_easy *data); void Curl_expire_done(struct Curl_easy *data, expire_id id); bool Curl_pipeline_wanted(const struct Curl_multi* multi, int bits); -void Curl_multi_handlePipeBreak(struct Curl_easy *data); +void Curl_detach_connnection(struct Curl_easy *data); +void Curl_attach_connnection(struct Curl_easy *data, + struct connectdata *conn); void Curl_set_in_callback(struct Curl_easy *data, bool value); bool Curl_is_in_callback(struct Curl_easy *easy); diff --git a/lib/objnames-test08.sh b/lib/objnames-test08.sh deleted file mode 100755 index 485975765..000000000 --- a/lib/objnames-test08.sh +++ /dev/null @@ -1,217 +0,0 @@ -#!/bin/sh -# *************************************************************************** -# * _ _ ____ _ -# * Project ___| | | | _ \| | -# * / __| | | | |_) | | -# * | (__| |_| | _ <| |___ -# * \___|\___/|_| \_\_____| -# * -# * Copyright (C) 2013, Daniel Stenberg, <daniel@haxx.se>, et al. -# * -# * This software is licensed as described in the file COPYING, which -# * you should have received as part of this distribution. The terms -# * are also available at https://curl.haxx.se/docs/copyright.html. -# * -# * You may opt to use, copy, modify, merge, publish, distribute and/or sell -# * copies of the Software, and permit persons to whom the Software is -# * furnished to do so, under the terms of the COPYING file. -# * -# * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY -# * KIND, either express or implied. -# * -# *************************************************************************** - -# -# This Bourne shell script file is used by test case 1222 to do -# unit testing of curl_8char_object_name() shell function which -# is defined in file objnames.inc and sourced by this file and -# any other shell script that may use it. -# - -# -# argument validation -# - -if test $# -eq 1; then - : -else - echo "Usage: ${0} srcdir" - exit 1 -fi - -if test -f "${1}/runtests.pl"; then - : -else - echo "${0}: Wrong srcdir" - exit 1 -fi - -srcdir=${1} - -if test -f "$srcdir/../lib/objnames.inc"; then - : -else - echo "$0: Missing objnames.inc" - exit 1 -fi - -# -# Some variables -# - -logdir=log -tstnum=1222 - -list_c=$logdir/${tstnum}_list_c -list_obj=$logdir/${tstnum}_list_obj -list_obj_c=$logdir/${tstnum}_list_obj_c -list_obj_uniq=$logdir/${tstnum}_list_obj_uniq - - -# -# Source curl_8char_object_name() function definition -# - -. $srcdir/../lib/objnames.inc - -# -# Some curl_8char_object_name() unit tests -# - -echo 'Testing curl_8char_object_name...' -echo "" - -argstr=123__678__ABC__FGH__KLM__PQRSTUV -expect=16AFKPQR -outstr=`curl_8char_object_name $argstr` -echo "result: $outstr expected: $expect input: $argstr" - -argstr=123__678__ABC__FGH__KLM__PQ.S.UV -expect=16AFKPQ -outstr=`curl_8char_object_name $argstr` -echo "result: $outstr expected: $expect input: $argstr" - -argstr=123__678__ABC..FGH..KLM..PQRSTUV -expect=16ABC -outstr=`curl_8char_object_name $argstr` -echo "result: $outstr expected: $expect input: $argstr" - -argstr=123__678_.ABC._FGH__KLM__PQRSTUV -expect=16 -outstr=`curl_8char_object_name $argstr` -echo "result: $outstr expected: $expect input: $argstr" - -argstr=123.567.90ABCDEFGHIJKLMNOPQRSTUV -expect=123 -outstr=`curl_8char_object_name $argstr` -echo "result: $outstr expected: $expect input: $argstr" - -argstr=1234567.90A.CDEFGHIJKLMNOPQRSTUV -expect=1234567 -outstr=`curl_8char_object_name $argstr` -echo "result: $outstr expected: $expect input: $argstr" - -argstr=1234567890.BCD.FGHIJKLMNOPQRSTUV -expect=12345678 -outstr=`curl_8char_object_name $argstr` -echo "result: $outstr expected: $expect input: $argstr" - -argstr=12=45-78+0AB.DE.GHIJKLMNOPQRSTUV -expect=1470AB -outstr=`curl_8char_object_name $argstr` -echo "result: $outstr expected: $expect input: $argstr" - -argstr=1234567890ABCDEFGHIJKLMNOPQRSTUV -expect=12345678 -outstr=`curl_8char_object_name $argstr` -echo "result: $outstr expected: $expect input: $argstr" - -argstr=123_567_90A_CDE_GHIJKLMNOPQRSTUV -expect=159CGHIJ -outstr=`curl_8char_object_name $argstr` -echo "result: $outstr expected: $expect input: $argstr" - -argstr=123_567_90A_CDEFGHIJKLMNOPQRSTUV -expect=159CDEFG -outstr=`curl_8char_object_name $argstr` -echo "result: $outstr expected: $expect input: $argstr" - -argstr=123_567_90ABCDEFGHIJKLMNOPQRSTUV -expect=1590ABCD -outstr=`curl_8char_object_name $argstr` -echo "result: $outstr expected: $expect input: $argstr" - -argstr=123_567890ABCDEFGHIJKLMNOPQRSTUV -expect=1567890A -outstr=`curl_8char_object_name $argstr` -echo "result: $outstr expected: $expect input: $argstr" - -argstr=1234567890ABCDEFGHIJKLMNOPQRSTUV -expect=12345678 -outstr=`curl_8char_object_name $argstr` -echo "result: $outstr expected: $expect input: $argstr" - -# -# Verify that generated object name is distinct for -# all *.c source files in lib and src subdirectories. -# - -ls $srcdir/../lib/*.c > $list_c -ls $srcdir/../src/*.c >> $list_c - -rm -f $list_obj - -for c_fname in `cat $list_c`; do - obj_name=`curl_8char_object_name $c_fname` - echo "$obj_name" >> $list_obj -done - -sort -u $list_obj > $list_obj_uniq - -cnt_c=`cat $list_c | wc -l` -cnt_u=`cat $list_obj_uniq | wc -l` - -echo "" -echo "" -echo "" -if test $cnt_c -eq $cnt_u; then - echo "8-characters-or-less generated object names are unique." - obj_name_clash="no" -else - echo "8-characters-or-less generated object names are clashing..." - obj_name_clash="yes" -fi - -if test $obj_name_clash = "yes"; then - # - # Show clashing object names and respective source file names - # - echo "" - paste $list_obj $list_c | sort > $list_obj_c - prev_match="no" - prev_line="unknown" - prev_obj_name="unknown" - while read this_line; do - obj_name=`echo "$this_line" | cut -f1` - if test "x$obj_name" = "x$prev_obj_name"; then - if test "x$prev_match" != "xyes"; then - echo "$prev_line" - echo "$this_line" - prev_match="yes" - else - echo "$this_line" - fi - else - prev_match="no" - fi - prev_line=$this_line - prev_obj_name=$obj_name - done < $list_obj_c -fi - -rm -f $list_c -rm -f $list_obj -rm -f $list_obj_c -rm -f $list_obj_uniq - -# end of objnames-test.sh diff --git a/lib/objnames-test10.sh b/lib/objnames-test10.sh deleted file mode 100755 index 62184b864..000000000 --- a/lib/objnames-test10.sh +++ /dev/null @@ -1,217 +0,0 @@ -#!/bin/sh -# *************************************************************************** -# * _ _ ____ _ -# * Project ___| | | | _ \| | -# * / __| | | | |_) | | -# * | (__| |_| | _ <| |___ -# * \___|\___/|_| \_\_____| -# * -# * Copyright (C) 2013, Daniel Stenberg, <daniel@haxx.se>, et al. -# * -# * This software is licensed as described in the file COPYING, which -# * you should have received as part of this distribution. The terms -# * are also available at https://curl.haxx.se/docs/copyright.html. -# * -# * You may opt to use, copy, modify, merge, publish, distribute and/or sell -# * copies of the Software, and permit persons to whom the Software is -# * furnished to do so, under the terms of the COPYING file. -# * -# * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY -# * KIND, either express or implied. -# * -# *************************************************************************** - -# -# This Bourne shell script file is used by test case 1221 to do -# unit testing of curl_10char_object_name() shell function which -# is defined in file objnames.inc and sourced by this file and -# any other shell script that may use it. -# - -# -# argument validation -# - -if test $# -eq 1; then - : -else - echo "Usage: ${0} srcdir" - exit 1 -fi - -if test -f "${1}/runtests.pl"; then - : -else - echo "${0}: Wrong srcdir" - exit 1 -fi - -srcdir=${1} - -if test -f "$srcdir/../lib/objnames.inc"; then - : -else - echo "$0: Missing objnames.inc" - exit 1 -fi - -# -# Some variables -# - -logdir=log -tstnum=1221 - -list_c=$logdir/${tstnum}_list_c -list_obj=$logdir/${tstnum}_list_obj -list_obj_c=$logdir/${tstnum}_list_obj_c -list_obj_uniq=$logdir/${tstnum}_list_obj_uniq - - -# -# Source curl_10char_object_name() function definition -# - -. $srcdir/../lib/objnames.inc - -# -# Some curl_10char_object_name() unit tests -# - -echo 'Testing curl_10char_object_name...' -echo "" - -argstr=123__678__ABC__FGH__KLM__PQRSTUV -expect=16AFKPQRST -outstr=`curl_10char_object_name $argstr` -echo "result: $outstr expected: $expect input: $argstr" - -argstr=123__678__ABC__FGH__KLM__PQ.S.UV -expect=16AFKPQ -outstr=`curl_10char_object_name $argstr` -echo "result: $outstr expected: $expect input: $argstr" - -argstr=123__678__ABC..FGH..KLM..PQRSTUV -expect=16ABC -outstr=`curl_10char_object_name $argstr` -echo "result: $outstr expected: $expect input: $argstr" - -argstr=123__678_.ABC._FGH__KLM__PQRSTUV -expect=16 -outstr=`curl_10char_object_name $argstr` -echo "result: $outstr expected: $expect input: $argstr" - -argstr=123.567.90ABCDEFGHIJKLMNOPQRSTUV -expect=123 -outstr=`curl_10char_object_name $argstr` -echo "result: $outstr expected: $expect input: $argstr" - -argstr=1234567.90A.CDEFGHIJKLMNOPQRSTUV -expect=1234567 -outstr=`curl_10char_object_name $argstr` -echo "result: $outstr expected: $expect input: $argstr" - -argstr=1234567890.BCD.FGHIJKLMNOPQRSTUV -expect=1234567890 -outstr=`curl_10char_object_name $argstr` -echo "result: $outstr expected: $expect input: $argstr" - -argstr=12=45-78+0AB.DE.GHIJKLMNOPQRSTUV -expect=1470AB -outstr=`curl_10char_object_name $argstr` -echo "result: $outstr expected: $expect input: $argstr" - -argstr=1234567890ABCDEFGHIJKLMNOPQRSTUV -expect=1234567890 -outstr=`curl_10char_object_name $argstr` -echo "result: $outstr expected: $expect input: $argstr" - -argstr=123_567_90A_CDE_GHIJKLMNOPQRSTUV -expect=159CGHIJKL -outstr=`curl_10char_object_name $argstr` -echo "result: $outstr expected: $expect input: $argstr" - -argstr=123_567_90A_CDEFGHIJKLMNOPQRSTUV -expect=159CDEFGHI -outstr=`curl_10char_object_name $argstr` -echo "result: $outstr expected: $expect input: $argstr" - -argstr=123_567_90ABCDEFGHIJKLMNOPQRSTUV -expect=1590ABCDEF -outstr=`curl_10char_object_name $argstr` -echo "result: $outstr expected: $expect input: $argstr" - -argstr=123_567890ABCDEFGHIJKLMNOPQRSTUV -expect=1567890ABC -outstr=`curl_10char_object_name $argstr` -echo "result: $outstr expected: $expect input: $argstr" - -argstr=1234567890ABCDEFGHIJKLMNOPQRSTUV -expect=1234567890 -outstr=`curl_10char_object_name $argstr` -echo "result: $outstr expected: $expect input: $argstr" - -# -# Verify that generated object name is distinct for -# all *.c source files in lib and src subdirectories. -# - -ls $srcdir/../lib/*.c > $list_c -ls $srcdir/../src/*.c >> $list_c - -rm -f $list_obj - -for c_fname in `cat $list_c`; do - obj_name=`curl_10char_object_name $c_fname` - echo "$obj_name" >> $list_obj -done - -sort -u $list_obj > $list_obj_uniq - -cnt_c=`cat $list_c | wc -l` -cnt_u=`cat $list_obj_uniq | wc -l` - -echo "" -echo "" -echo "" -if test $cnt_c -eq $cnt_u; then - echo "10-characters-or-less generated object names are unique." - obj_name_clash="no" -else - echo "10-characters-or-less generated object names are clashing..." - obj_name_clash="yes" -fi - -if test $obj_name_clash = "yes"; then - # - # Show clashing object names and respective source file names - # - echo "" - paste $list_obj $list_c | sort > $list_obj_c - prev_match="no" - prev_line="unknown" - prev_obj_name="unknown" - while read this_line; do - obj_name=`echo "$this_line" | cut -f1` - if test "x$obj_name" = "x$prev_obj_name"; then - if test "x$prev_match" != "xyes"; then - echo "$prev_line" - echo "$this_line" - prev_match="yes" - else - echo "$this_line" - fi - else - prev_match="no" - fi - prev_line=$this_line - prev_obj_name=$obj_name - done < $list_obj_c -fi - -rm -f $list_c -rm -f $list_obj -rm -f $list_obj_c -rm -f $list_obj_uniq - -# end of objnames-test10.sh diff --git a/lib/objnames.inc b/lib/objnames.inc deleted file mode 100644 index e362f6e8e..000000000 --- a/lib/objnames.inc +++ /dev/null @@ -1,107 +0,0 @@ -# *************************************************************************** -# * _ _ ____ _ -# * Project ___| | | | _ \| | -# * / __| | | | |_) | | -# * | (__| |_| | _ <| |___ -# * \___|\___/|_| \_\_____| -# * -# * Copyright (C) 2012 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al. -# * -# * This software is licensed as described in the file COPYING, which -# * you should have received as part of this distribution. The terms -# * are also available at https://curl.haxx.se/docs/copyright.html. -# * -# * You may opt to use, copy, modify, merge, publish, distribute and/or sell -# * copies of the Software, and permit persons to whom the Software is -# * furnished to do so, under the terms of the COPYING file. -# * -# * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY -# * KIND, either express or implied. -# * -# *************************************************************************** - -# -# This file is sourced from curl/packages/OS400/initscript.sh and -# other Bourne shell scripts. Keep it as portable as possible. -# - -# -# curl_10char_object_name -# -# This shell function accepts a single string argument with unspecified -# length representing a (*.c) source file name and returns a string which -# is a transformation of given argument. -# -# The intended purpose of this function is to transliterate a (*.c) source -# file name that may be longer than 10 characters, or not, into a string -# with at most 10 characters which may be used as an OS/400 object name. -# -# This function might not be universally useful, nor we care about it. -# -# It is intended to be used with libcurl's (*.c) source file names, so -# dependency on libcurl's source file naming scheme is acceptable and -# good enough for its intended use. Specifically it makes use of the fact -# that libcurl's (*.c) source file names which may be longer than 10 chars -# are conformed with underscore '_' separated substrings, or separated by -# other character which does not belong to the [0-9], [a-z] or [A-Z] sets. -# -# This allows repeatable and automatic short object name generation with -# no need for a hardcoded mapping table. -# -# Transformation is done in the following way: -# -# 1) Leading directory components are removed. -# 2) Leftmost dot character and any other char following it are removed. -# 3) Lowercase characters are transliterated to uppercase. -# 4) Characters not in [A-Z] or [0-9] are transliterated to underscore '_'. -# 5) Every sequence of one or more underscores is replaced with a single one. -# 6) Five leftmost substrings which end in an underscore character are -# replaced by the first character of each substring, while retaining -# the rest of the string. -# 7) Finally the result is truncated to 10 characters. -# -# Resulting object name may be shorter than 10 characters. -# -# Test case 1221 does unit testng of this function and also verifies -# that it is possible to generate distinct short object names for all -# curl and libcurl *.c source file names. -# - -curl_10char_object_name() { - echo "${1}" | \ - sed -e 's:.*/::' \ - -e 's:[.].*::' \ - -e 'y:abcdefghijklmnopqrstuvwxyz:ABCDEFGHIJKLMNOPQRSTUVWXYZ:' \ - -e 's:[^ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_]:_:g' \ - -e 's:__*:_:g' \ - -e 's:\([^_]\)[^_]*_\(.*\):\1\2:' \ - -e 's:\([^_]\)\([^_]\)[^_]*_\(.*\):\1\2\3:' \ - -e 's:\([^_]\)\([^_]\)\([^_]\)[^_]*_\(.*\):\1\2\3\4:' \ - -e 's:\([^_]\)\([^_]\)\([^_]\)\([^_]\)[^_]*_\(.*\):\1\2\3\4\5:' \ - -e 's:\([^_]\)\([^_]\)\([^_]\)\([^_]\)\([^_]\)[^_]*_\(.*\):\1\2\3\4\5\6:' \ - -e 's:^\(..........\).*:\1:' -} - -# -# curl_8char_object_name -# -# Same as curl_10char_object_name() description and details above, except -# that object name is limited to 8 characters maximum. -# - -curl_8char_object_name() { - echo "${1}" | \ - sed -e 's:.*/::' \ - -e 's:[.].*::' \ - -e 'y:abcdefghijklmnopqrstuvwxyz:ABCDEFGHIJKLMNOPQRSTUVWXYZ:' \ - -e 's:[^ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_]:_:g' \ - -e 's:__*:_:g' \ - -e 's:\([^_]\)[^_]*_\(.*\):\1\2:' \ - -e 's:\([^_]\)\([^_]\)[^_]*_\(.*\):\1\2\3:' \ - -e 's:\([^_]\)\([^_]\)\([^_]\)[^_]*_\(.*\):\1\2\3\4:' \ - -e 's:\([^_]\)\([^_]\)\([^_]\)\([^_]\)[^_]*_\(.*\):\1\2\3\4\5:' \ - -e 's:\([^_]\)\([^_]\)\([^_]\)\([^_]\)\([^_]\)[^_]*_\(.*\):\1\2\3\4\5\6:' \ - -e 's:^\(........\).*:\1:' -} - -# end of objectname.inc diff --git a/lib/pingpong.c b/lib/pingpong.c index 2e93d201f..e9568ee3d 100644 --- a/lib/pingpong.c +++ b/lib/pingpong.c @@ -44,7 +44,7 @@ /* Returns timeout in ms. 0 or negative number means the timeout has already triggered */ -time_t Curl_pp_state_timeout(struct pingpong *pp) +time_t Curl_pp_state_timeout(struct pingpong *pp, bool disconnecting) { struct connectdata *conn = pp->conn; struct Curl_easy *data = conn->data; @@ -62,7 +62,7 @@ time_t Curl_pp_state_timeout(struct pingpong *pp) timeout_ms = response_time - Curl_timediff(Curl_now(), pp->response); /* spent time */ - if(data->set.timeout) { + if(data->set.timeout && !disconnecting) { /* if timeout is requested, find out how much remaining time we have */ time_t timeout2_ms = data->set.timeout - /* timeout time */ Curl_timediff(Curl_now(), conn->now); /* spent time */ @@ -77,13 +77,14 @@ time_t Curl_pp_state_timeout(struct pingpong *pp) /* * Curl_pp_statemach() */ -CURLcode Curl_pp_statemach(struct pingpong *pp, bool block) +CURLcode Curl_pp_statemach(struct pingpong *pp, bool block, + bool disconnecting) { struct connectdata *conn = pp->conn; curl_socket_t sock = conn->sock[FIRSTSOCKET]; int rc; time_t interval_ms; - time_t timeout_ms = Curl_pp_state_timeout(pp); + time_t timeout_ms = Curl_pp_state_timeout(pp, disconnecting); struct Curl_easy *data = conn->data; CURLcode result = CURLE_OK; diff --git a/lib/pingpong.h b/lib/pingpong.h index 5ac8df876..dbe1f8d3d 100644 --- a/lib/pingpong.h +++ b/lib/pingpong.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -81,14 +81,15 @@ struct pingpong { * called repeatedly until done. Set 'wait' to make it wait a while on the * socket if there's no traffic. */ -CURLcode Curl_pp_statemach(struct pingpong *pp, bool block); +CURLcode Curl_pp_statemach(struct pingpong *pp, bool block, + bool disconnecting); /* initialize stuff to prepare for reading a fresh new response */ void Curl_pp_init(struct pingpong *pp); /* Returns timeout in ms. 0 or negative number means the timeout has already triggered */ -time_t Curl_pp_state_timeout(struct pingpong *pp); +time_t Curl_pp_state_timeout(struct pingpong *pp, bool disconnecting); /*********************************************************************** diff --git a/lib/pop3.c b/lib/pop3.c index 6af4626d4..49474157e 100644 --- a/lib/pop3.c +++ b/lib/pop3.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -208,7 +208,7 @@ static bool pop3_endofresp(struct connectdata *conn, char *line, size_t len, /* Are we processing CAPA command responses? */ if(pop3c->state == POP3_CAPA) { /* Do we have the terminating line? */ - if(len >= 1 && !memcmp(line, ".", 1)) + if(len >= 1 && line[0] == '.') /* Treat the response as a success */ *resp = '+'; else @@ -226,7 +226,7 @@ static bool pop3_endofresp(struct connectdata *conn, char *line, size_t len, } /* Do we have a continuation response? */ - if(len >= 1 && !memcmp("+", line, 1)) { + if(len >= 1 && line[0] == '+') { *resp = '*'; return TRUE; @@ -1025,19 +1025,20 @@ static CURLcode pop3_multi_statemach(struct connectdata *conn, bool *done) return result; } - result = Curl_pp_statemach(&pop3c->pp, FALSE); + result = Curl_pp_statemach(&pop3c->pp, FALSE, FALSE); *done = (pop3c->state == POP3_STOP) ? TRUE : FALSE; return result; } -static CURLcode pop3_block_statemach(struct connectdata *conn) +static CURLcode pop3_block_statemach(struct connectdata *conn, + bool disconnecting) { CURLcode result = CURLE_OK; struct pop3_conn *pop3c = &conn->proto.pop3c; while(pop3c->state != POP3_STOP && !result) - result = Curl_pp_statemach(&pop3c->pp, TRUE); + result = Curl_pp_statemach(&pop3c->pp, TRUE, disconnecting); return result; } @@ -1235,7 +1236,7 @@ static CURLcode pop3_disconnect(struct connectdata *conn, bool dead_connection) point! */ if(!dead_connection && pop3c->pp.conn && pop3c->pp.conn->bits.protoconnstart) if(!pop3_perform_quit(conn)) - (void)pop3_block_statemach(conn); /* ignore errors on QUIT */ + (void)pop3_block_statemach(conn, TRUE); /* ignore errors on QUIT */ /* Disconnect from the server */ Curl_pp_disconnect(&pop3c->pp); diff --git a/lib/setopt.c b/lib/setopt.c index 1627aba6d..d98ca66c9 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -803,12 +803,12 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, if(checkprefix("Set-Cookie:", argptr)) /* HTTP Header format line */ Curl_cookie_add(data, data->cookies, TRUE, FALSE, argptr + 11, NULL, - NULL); + NULL, TRUE); else /* Netscape format line */ Curl_cookie_add(data, data->cookies, FALSE, FALSE, argptr, NULL, - NULL); + NULL, TRUE); Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); free(argptr); @@ -860,6 +860,12 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, data->set.expect_100_timeout = arg; break; + case CURLOPT_HTTP09_ALLOWED: + arg = va_arg(param, unsigned long); + if(arg > 1L) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.http09_allowed = arg ? TRUE : FALSE; + break; #endif /* CURL_DISABLE_HTTP */ case CURLOPT_HTTPAUTH: @@ -1693,8 +1699,8 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, TRUE : FALSE; /* Update the current connection ssl_config. */ - if(data->easy_conn) { - data->easy_conn->ssl_config.verifypeer = + if(data->conn) { + data->conn->ssl_config.verifypeer = data->set.ssl.primary.verifypeer; } break; @@ -1706,8 +1712,8 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, (0 != va_arg(param, long))?TRUE:FALSE; /* Update the current connection proxy_ssl_config. */ - if(data->easy_conn) { - data->easy_conn->proxy_ssl_config.verifypeer = + if(data->conn) { + data->conn->proxy_ssl_config.verifypeer = data->set.proxy_ssl.primary.verifypeer; } break; @@ -1730,8 +1736,8 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, data->set.ssl.primary.verifyhost = (0 != arg) ? TRUE : FALSE; /* Update the current connection ssl_config. */ - if(data->easy_conn) { - data->easy_conn->ssl_config.verifyhost = + if(data->conn) { + data->conn->ssl_config.verifyhost = data->set.ssl.primary.verifyhost; } break; @@ -1754,8 +1760,8 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, data->set.proxy_ssl.primary.verifyhost = (0 != arg)?TRUE:FALSE; /* Update the current connection proxy_ssl_config. */ - if(data->easy_conn) { - data->easy_conn->proxy_ssl_config.verifyhost = + if(data->conn) { + data->conn->proxy_ssl_config.verifyhost = data->set.proxy_ssl.primary.verifyhost; } break; @@ -1772,8 +1778,8 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, TRUE : FALSE; /* Update the current connection ssl_config. */ - if(data->easy_conn) { - data->easy_conn->ssl_config.verifystatus = + if(data->conn) { + data->conn->ssl_config.verifystatus = data->set.ssl.primary.verifystatus; } break; @@ -2231,7 +2237,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, result = Curl_setstropt(&data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5], va_arg(param, char *)); break; -#ifdef HAVE_LIBSSH2_KNOWNHOST_API + case CURLOPT_SSH_KNOWNHOSTS: /* * Store the file name to read known hosts from. @@ -2252,7 +2258,6 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, */ data->set.ssh_keyfunc_userp = va_arg(param, void *); break; -#endif /* HAVE_LIBSSH2_KNOWNHOST_API */ #endif /* USE_LIBSSH2 */ case CURLOPT_HTTP_TRANSFER_DECODING: @@ -2636,6 +2641,16 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, return CURLE_BAD_FUNCTION_ARGUMENT; data->set.upkeep_interval_ms = arg; break; + case CURLOPT_TRAILERFUNCTION: +#ifndef CURL_DISABLE_HTTP + data->set.trailer_callback = va_arg(param, curl_trailer_callback); +#endif + break; + case CURLOPT_TRAILERDATA: +#ifndef CURL_DISABLE_HTTP + data->set.trailer_data = va_arg(param, void *); +#endif + break; default: /* unknown tag and its companion, just ignore: */ result = CURLE_UNKNOWN_OPTION; diff --git a/lib/sigpipe.h b/lib/sigpipe.h index 800f9d3b4..3960a139d 100644 --- a/lib/sigpipe.h +++ b/lib/sigpipe.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -23,7 +23,8 @@ ***************************************************************************/ #include "curl_setup.h" -#if defined(HAVE_SIGNAL_H) && defined(HAVE_SIGACTION) && defined(USE_OPENSSL) +#if defined(HAVE_SIGNAL_H) && defined(HAVE_SIGACTION) && \ + (defined(USE_OPENSSL) || defined(USE_MBEDTLS)) #include <signal.h> struct sigpipe_ignore { @@ -947,15 +947,10 @@ static int smb_getsock(struct connectdata *conn, curl_socket_t *socks, static CURLcode smb_do(struct connectdata *conn, bool *done) { struct smb_conn *smbc = &conn->proto.smbc; - struct smb_request *req = conn->data->req.protop; *done = FALSE; if(smbc->share) { - req->path = strchr(smbc->share, '\0'); - if(req->path) { - req->path++; - return CURLE_OK; - } + return CURLE_OK; } return CURLE_URL_MALFORMAT; } @@ -964,6 +959,7 @@ static CURLcode smb_parse_url_path(struct connectdata *conn) { CURLcode result = CURLE_OK; struct Curl_easy *data = conn->data; + struct smb_request *req = data->req.protop; struct smb_conn *smbc = &conn->proto.smbc; char *path; char *slash; @@ -992,6 +988,7 @@ static CURLcode smb_parse_url_path(struct connectdata *conn) /* Parse the path for the file path converting any forward slashes into backslashes */ *slash++ = 0; + req->path = slash; for(; *slash; slash++) { if(*slash == '/') diff --git a/lib/smtp.c b/lib/smtp.c index 4911ece7b..63f4d468f 100644 --- a/lib/smtp.c +++ b/lib/smtp.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -207,8 +207,12 @@ static bool smtp_endofresp(struct connectdata *conn, char *line, size_t len, Section 4. Examples of RFC-4954 but some e-mail servers ignore this and only send the response code instead as per Section 4.2. */ if(line[3] == ' ' || len == 5) { + char tmpline[6]; + result = TRUE; - *resp = curlx_sltosi(strtol(line, NULL, 10)); + memset(tmpline, '\0', sizeof(tmpline)); + memcpy(tmpline, line, (len == 5 ? 5 : 3)); + *resp = curlx_sltosi(strtol(tmpline, NULL, 10)); /* Make sure real server never sends internal value */ if(*resp == 1) @@ -1080,19 +1084,20 @@ static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done) return result; } - result = Curl_pp_statemach(&smtpc->pp, FALSE); + result = Curl_pp_statemach(&smtpc->pp, FALSE, FALSE); *done = (smtpc->state == SMTP_STOP) ? TRUE : FALSE; return result; } -static CURLcode smtp_block_statemach(struct connectdata *conn) +static CURLcode smtp_block_statemach(struct connectdata *conn, + bool disconnecting) { CURLcode result = CURLE_OK; struct smtp_conn *smtpc = &conn->proto.smtpc; while(smtpc->state != SMTP_STOP && !result) - result = Curl_pp_statemach(&smtpc->pp, TRUE); + result = Curl_pp_statemach(&smtpc->pp, TRUE, disconnecting); return result; } @@ -1253,7 +1258,7 @@ static CURLcode smtp_done(struct connectdata *conn, CURLcode status, the smtp_multi_statemach function but we have no general support for non-blocking DONE operations! */ - result = smtp_block_statemach(conn); + result = smtp_block_statemach(conn, FALSE); } /* Clear the transfer mode for the next request */ @@ -1360,7 +1365,7 @@ static CURLcode smtp_disconnect(struct connectdata *conn, bool dead_connection) point! */ if(!dead_connection && smtpc->pp.conn && smtpc->pp.conn->bits.protoconnstart) if(!smtp_perform_quit(conn)) - (void)smtp_block_statemach(conn); /* ignore errors on QUIT */ + (void)smtp_block_statemach(conn, TRUE); /* ignore errors on QUIT */ /* Disconnect from the server */ Curl_pp_disconnect(&smtpc->pp); diff --git a/lib/ssh-libssh.c b/lib/ssh-libssh.c index 2d7b1cfa2..718c0d343 100644 --- a/lib/ssh-libssh.c +++ b/lib/ssh-libssh.c @@ -95,6 +95,13 @@ #include "memdebug.h" #include "curl_path.h" +/* A recent macro provided by libssh. Or make our own. */ +#ifndef SSH_STRING_FREE_CHAR +/* !checksrc! disable ASSIGNWITHINCONDITION 1 */ +#define SSH_STRING_FREE_CHAR(x) \ + do { if((x) != NULL) { ssh_string_free_char(x); x = NULL; } } while(0) +#endif + /* Local functions: */ static CURLcode myssh_connect(struct connectdata *conn, bool *done); static CURLcode myssh_multi_statemach(struct connectdata *conn, @@ -549,6 +556,7 @@ static CURLcode myssh_statemach_act(struct connectdata *conn, bool *block) struct Curl_easy *data = conn->data; struct SSHPROTO *protop = data->req.protop; struct ssh_conn *sshc = &conn->proto.sshc; + curl_socket_t sock = conn->sock[FIRSTSOCKET]; int rc = SSH_NO_ERROR, err; char *new_readdir_line; int seekerr = CURL_SEEKFUNC_OK; @@ -792,7 +800,7 @@ static CURLcode myssh_statemach_act(struct connectdata *conn, bool *block) Curl_pgrsTime(conn->data, TIMER_APPCONNECT); /* SSH is connected */ - conn->sockfd = ssh_get_fd(sshc->ssh_session); + conn->sockfd = sock; conn->writesockfd = CURL_SOCKET_BAD; if(conn->handler->protocol == CURLPROTO_SFTP) { @@ -1661,7 +1669,7 @@ static CURLcode myssh_statemach_act(struct connectdata *conn, bool *block) sshc->sftp_session = NULL; } - Curl_safefree(sshc->homedir); + SSH_STRING_FREE_CHAR(sshc->homedir); conn->data->state.most_recent_ftp_entrypath = NULL; state(conn, SSH_SESSION_DISCONNECT); @@ -1829,7 +1837,7 @@ static CURLcode myssh_statemach_act(struct connectdata *conn, bool *block) ssh_disconnect(sshc->ssh_session); - Curl_safefree(sshc->homedir); + SSH_STRING_FREE_CHAR(sshc->homedir); conn->data->state.most_recent_ftp_entrypath = NULL; state(conn, SSH_SESSION_FREE); @@ -1866,14 +1874,11 @@ static CURLcode myssh_statemach_act(struct connectdata *conn, bool *block) Curl_safefree(sshc->rsa_pub); Curl_safefree(sshc->rsa); - Curl_safefree(sshc->quote_path1); Curl_safefree(sshc->quote_path2); - - Curl_safefree(sshc->homedir); - Curl_safefree(sshc->readdir_line); Curl_safefree(sshc->readdir_linkPath); + SSH_STRING_FREE_CHAR(sshc->homedir); /* the code we are about to return */ result = sshc->actualcode; @@ -2048,6 +2053,7 @@ static CURLcode myssh_connect(struct connectdata *conn, bool *done) { struct ssh_conn *ssh; CURLcode result; + curl_socket_t sock = conn->sock[FIRSTSOCKET]; struct Curl_easy *data = conn->data; int rc; @@ -2076,6 +2082,8 @@ static CURLcode myssh_connect(struct connectdata *conn, bool *done) return CURLE_FAILED_INIT; } + ssh_options_set(ssh->ssh_session, SSH_OPTIONS_FD, &sock); + if(conn->user) { infof(data, "User: %s\n", conn->user); ssh_options_set(ssh->ssh_session, SSH_OPTIONS_USER, conn->user); @@ -667,7 +667,10 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) break; } if(rc) { - failf(data, "Failure establishing ssh session"); + char *err_msg = NULL; + (void)libssh2_session_last_error(sshc->ssh_session, &err_msg, NULL, 0); + failf(data, "Failure establishing ssh session: %d, %s", rc, err_msg); + state(conn, SSH_SESSION_FREE); sshc->actualcode = CURLE_FAILED_INIT; break; diff --git a/lib/timeval.c b/lib/timeval.c index dce1a761e..2569f175c 100644 --- a/lib/timeval.c +++ b/lib/timeval.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -21,29 +21,45 @@ ***************************************************************************/ #include "timeval.h" +#include "system_win32.h" #if defined(WIN32) && !defined(MSDOS) struct curltime Curl_now(void) { - /* - ** GetTickCount() is available on _all_ Windows versions from W95 up - ** to nowadays. Returns milliseconds elapsed since last system boot, - ** increases monotonically and wraps once 49.7 days have elapsed. - */ struct curltime now; -#if !defined(_WIN32_WINNT) || !defined(_WIN32_WINNT_VISTA) || \ - (_WIN32_WINNT < _WIN32_WINNT_VISTA) || \ - (defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)) - DWORD milliseconds = GetTickCount(); - now.tv_sec = milliseconds / 1000; - now.tv_usec = (milliseconds % 1000) * 1000; -#else - ULONGLONG milliseconds = GetTickCount64(); - now.tv_sec = (time_t) (milliseconds / 1000); - now.tv_usec = (unsigned int) (milliseconds % 1000) * 1000; + static LARGE_INTEGER freq; + static int isVistaOrGreater = -1; + if(isVistaOrGreater == -1) { + if(Curl_verify_windows_version(6, 0, PLATFORM_WINNT, + VERSION_GREATER_THAN_EQUAL)) { + isVistaOrGreater = 1; + QueryPerformanceFrequency(&freq); + } + else + isVistaOrGreater = 0; + } + if(isVistaOrGreater == 1) { /* QPC timer might have issues pre-Vista */ + LARGE_INTEGER count; + QueryPerformanceCounter(&count); + now.tv_sec = (time_t)(count.QuadPart / freq.QuadPart); + now.tv_usec = + (int)((count.QuadPart % freq.QuadPart) * 1000000 / freq.QuadPart); + } + else { + /* Disable /analyze warning that GetTickCount64 is preferred */ +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:28159) +#endif + DWORD milliseconds = GetTickCount(); +#if defined(_MSC_VER) +#pragma warning(pop) #endif + now.tv_sec = milliseconds / 1000; + now.tv_usec = (milliseconds % 1000) * 1000; + } return now; } @@ -180,7 +196,7 @@ struct curltime Curl_now(void) */ timediff_t Curl_timediff(struct curltime newer, struct curltime older) { - timediff_t diff = newer.tv_sec-older.tv_sec; + timediff_t diff = (timediff_t)newer.tv_sec-older.tv_sec; if(diff >= (TIME_MAX/1000)) return TIME_MAX; else if(diff <= (TIME_MIN/1000)) @@ -194,7 +210,7 @@ timediff_t Curl_timediff(struct curltime newer, struct curltime older) */ timediff_t Curl_timediff_us(struct curltime newer, struct curltime older) { - timediff_t diff = newer.tv_sec-older.tv_sec; + timediff_t diff = (timediff_t)newer.tv_sec-older.tv_sec; if(diff >= (TIME_MAX/1000000)) return TIME_MAX; else if(diff <= (TIME_MIN/1000000)) diff --git a/lib/timeval.h b/lib/timeval.h index fb3f680c4..96867d713 100644 --- a/lib/timeval.h +++ b/lib/timeval.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -26,8 +26,10 @@ #if SIZEOF_TIME_T < 8 typedef int timediff_t; +#define CURL_FORMAT_TIMEDIFF_T "d" #else typedef curl_off_t timediff_t; +#define CURL_FORMAT_TIMEDIFF_T CURL_FORMAT_CURL_OFF_T #endif struct curltime { diff --git a/lib/transfer.c b/lib/transfer.c index c50809785..5d85f3d6b 100644 --- a/lib/transfer.c +++ b/lib/transfer.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -117,6 +117,35 @@ CURLcode Curl_get_upload_buffer(struct Curl_easy *data) return CURLE_OK; } +#ifndef CURL_DISABLE_HTTP +/* + * This function will be called to loop through the trailers buffer + * until no more data is available for sending. + */ +static size_t Curl_trailers_read(char *buffer, size_t size, size_t nitems, + void *raw) +{ + struct Curl_easy *data = (struct Curl_easy *)raw; + Curl_send_buffer *trailers_buf = data->state.trailers_buf; + size_t bytes_left = trailers_buf->size_used-data->state.trailers_bytes_sent; + size_t to_copy = (size*nitems < bytes_left) ? size*nitems : bytes_left; + if(to_copy) { + memcpy(buffer, + &trailers_buf->buffer[data->state.trailers_bytes_sent], + to_copy); + data->state.trailers_bytes_sent += to_copy; + } + return to_copy; +} + +static size_t Curl_trailers_left(void *raw) +{ + struct Curl_easy *data = (struct Curl_easy *)raw; + Curl_send_buffer *trailers_buf = data->state.trailers_buf; + return trailers_buf->size_used - data->state.trailers_bytes_sent; +} +#endif + /* * This function will call the read callback to fill our buffer with data * to upload. @@ -127,6 +156,17 @@ CURLcode Curl_fillreadbuffer(struct connectdata *conn, size_t bytes, struct Curl_easy *data = conn->data; size_t buffersize = bytes; size_t nread; + +#ifndef CURL_DISABLE_HTTP + struct curl_slist *trailers = NULL; + CURLcode c; + int trailers_ret_code; +#endif + + curl_read_callback readfunc = NULL; + void *extra_data = NULL; + bool added_crlf = FALSE; + #ifdef CURL_DOES_CONVERSIONS bool sending_http_headers = FALSE; @@ -140,15 +180,71 @@ CURLcode Curl_fillreadbuffer(struct connectdata *conn, size_t bytes, } #endif - if(data->req.upload_chunky) { +#ifndef CURL_DISABLE_HTTP + if(data->state.trailers_state == TRAILERS_INITIALIZED) { + /* at this point we already verified that the callback exists + so we compile and store the trailers buffer, then proceed */ + infof(data, + "Moving trailers state machine from initialized to sending.\n"); + data->state.trailers_state = TRAILERS_SENDING; + data->state.trailers_buf = Curl_add_buffer_init(); + if(!data->state.trailers_buf) { + failf(data, "Unable to allocate trailing headers buffer !"); + return CURLE_OUT_OF_MEMORY; + } + data->state.trailers_bytes_sent = 0; + Curl_set_in_callback(data, true); + trailers_ret_code = data->set.trailer_callback(&trailers, + data->set.trailer_data); + Curl_set_in_callback(data, false); + if(trailers_ret_code == CURL_TRAILERFUNC_OK) { + c = Curl_http_compile_trailers(trailers, data->state.trailers_buf, data); + } + else { + failf(data, "operation aborted by trailing headers callback"); + *nreadp = 0; + c = CURLE_ABORTED_BY_CALLBACK; + } + if(c != CURLE_OK) { + Curl_add_buffer_free(&data->state.trailers_buf); + curl_slist_free_all(trailers); + return c; + } + infof(data, "Successfully compiled trailers.\r\n"); + curl_slist_free_all(trailers); + } +#endif + + /* if we are transmitting trailing data, we don't need to write + a chunk size so we skip this */ + if(data->req.upload_chunky && + data->state.trailers_state == TRAILERS_NONE) { /* if chunked Transfer-Encoding */ buffersize -= (8 + 2 + 2); /* 32bit hex + CRLF + CRLF */ data->req.upload_fromhere += (8 + 2); /* 32bit hex + CRLF */ } +#ifndef CURL_DISABLE_HTTP + if(data->state.trailers_state == TRAILERS_SENDING) { + /* if we're here then that means that we already sent the last empty chunk + but we didn't send a final CR LF, so we sent 0 CR LF. We then start + pulling trailing data until we ²have no more at which point we + simply return to the previous point in the state machine as if + nothing happened. + */ + readfunc = Curl_trailers_read; + extra_data = (void *)data; + } + else +#endif + { + readfunc = data->state.fread_func; + extra_data = data->state.in; + } + Curl_set_in_callback(data, true); - nread = data->state.fread_func(data->req.upload_fromhere, 1, - buffersize, data->state.in); + nread = readfunc(data->req.upload_fromhere, 1, + buffersize, extra_data); Curl_set_in_callback(data, false); if(nread == CURL_READFUNC_ABORT) { @@ -203,7 +299,7 @@ CURLcode Curl_fillreadbuffer(struct connectdata *conn, size_t bytes, char hexbuffer[11]; const char *endofline_native; const char *endofline_network; - int hexlen; + int hexlen = 0; if( #ifdef CURL_DO_LINEEND_CONV @@ -218,20 +314,36 @@ CURLcode Curl_fillreadbuffer(struct connectdata *conn, size_t bytes, endofline_native = "\r\n"; endofline_network = "\x0d\x0a"; } - hexlen = msnprintf(hexbuffer, sizeof(hexbuffer), - "%x%s", nread, endofline_native); - /* move buffer pointer */ - data->req.upload_fromhere -= hexlen; - nread += hexlen; + /* if we're not handling trailing data, proceed as usual */ + if(data->state.trailers_state != TRAILERS_SENDING) { + hexlen = msnprintf(hexbuffer, sizeof(hexbuffer), + "%zx%s", nread, endofline_native); - /* copy the prefix to the buffer, leaving out the NUL */ - memcpy(data->req.upload_fromhere, hexbuffer, hexlen); + /* move buffer pointer */ + data->req.upload_fromhere -= hexlen; + nread += hexlen; - /* always append ASCII CRLF to the data */ - memcpy(data->req.upload_fromhere + nread, - endofline_network, - strlen(endofline_network)); + /* copy the prefix to the buffer, leaving out the NUL */ + memcpy(data->req.upload_fromhere, hexbuffer, hexlen); + + /* always append ASCII CRLF to the data unless + we have a valid trailer callback */ +#ifndef CURL_DISABLE_HTTP + if((nread-hexlen) == 0 && + data->set.trailer_callback != NULL && + data->state.trailers_state == TRAILERS_NONE) { + data->state.trailers_state = TRAILERS_INITIALIZED; + } + else +#endif + { + memcpy(data->req.upload_fromhere + nread, + endofline_network, + strlen(endofline_network)); + added_crlf = TRUE; + } + } #ifdef CURL_DOES_CONVERSIONS { @@ -251,13 +363,29 @@ CURLcode Curl_fillreadbuffer(struct connectdata *conn, size_t bytes, } #endif /* CURL_DOES_CONVERSIONS */ - if((nread - hexlen) == 0) { - /* mark this as done once this chunk is transferred */ +#ifndef CURL_DISABLE_HTTP + if(data->state.trailers_state == TRAILERS_SENDING && + !Curl_trailers_left(data)) { + Curl_add_buffer_free(&data->state.trailers_buf); + data->state.trailers_state = TRAILERS_DONE; + data->set.trailer_data = NULL; + data->set.trailer_callback = NULL; + /* mark the transfer as done */ data->req.upload_done = TRUE; - infof(data, "Signaling end of chunked upload via terminating chunk.\n"); + infof(data, "Signaling end of chunked upload after trailers.\n"); } + else +#endif + if((nread - hexlen) == 0 && + data->state.trailers_state != TRAILERS_INITIALIZED) { + /* mark this as done once this chunk is transferred */ + data->req.upload_done = TRUE; + infof(data, + "Signaling end of chunked upload via terminating chunk.\n"); + } - nread += strlen(endofline_native); /* for the added end of line */ + if(added_crlf) + nread += strlen(endofline_network); /* for the added end of line */ } #ifdef CURL_DOES_CONVERSIONS else if((data->set.prefer_ascii) && (!sending_http_headers)) { @@ -925,7 +1053,6 @@ static CURLcode readwrite_upload(struct Curl_easy *data, *didwhat |= KEEP_SEND; do { - /* only read more data if there's no upload data already present in the upload buffer */ if(0 == k->upload_present) { @@ -950,7 +1077,6 @@ static CURLcode readwrite_upload(struct Curl_easy *data, k->keepon &= ~KEEP_SEND; /* disable writing */ k->start100 = Curl_now(); /* timeout count starts now */ *didwhat &= ~KEEP_SEND; /* we didn't write anything actually */ - /* set a timeout for the multi interface */ Curl_expire(data, data->set.expect_100_timeout, EXPIRE_100_TIMEOUT); break; @@ -1224,15 +1350,15 @@ CURLcode Curl_readwrite(struct connectdata *conn, if(k->keepon) { if(0 > Curl_timeleft(data, &k->now, FALSE)) { if(k->size != -1) { - failf(data, "Operation timed out after %ld milliseconds with %" - CURL_FORMAT_CURL_OFF_T " out of %" + failf(data, "Operation timed out after %" CURL_FORMAT_TIMEDIFF_T + " milliseconds with %" CURL_FORMAT_CURL_OFF_T " out of %" CURL_FORMAT_CURL_OFF_T " bytes received", Curl_timediff(k->now, data->progress.t_startsingle), k->bytecount, k->size); } else { - failf(data, "Operation timed out after %ld milliseconds with %" - CURL_FORMAT_CURL_OFF_T " bytes received", + failf(data, "Operation timed out after %" CURL_FORMAT_TIMEDIFF_T + " milliseconds with %" CURL_FORMAT_CURL_OFF_T " bytes received", Curl_timediff(k->now, data->progress.t_startsingle), k->bytecount); } @@ -1432,12 +1558,6 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) Curl_pgrsResetTransferSizes(data); Curl_pgrsStartNow(data); - if(data->set.timeout) - Curl_expire(data, data->set.timeout, EXPIRE_TIMEOUT); - - if(data->set.connecttimeout) - Curl_expire(data, data->set.connecttimeout, EXPIRE_CONNECTTIMEOUT); - /* In case the handle is re-used and an authentication method was picked in the session we need to make sure we only use the one(s) we now consider to be fine */ @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -492,9 +492,9 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data) /* Set the default CA cert bundle/path detected/specified at build time. * - * If Schannel (WinSSL) is the selected SSL backend then these locations - * are ignored. We allow setting CA location for schannel only when - * explicitly specified by the user via CURLOPT_CAINFO / --cacert. + * If Schannel is the selected SSL backend then these locations are + * ignored. We allow setting CA location for schannel only when explicitly + * specified by the user via CURLOPT_CAINFO / --cacert. */ if(Curl_ssl_backend() != CURLSSLBACKEND_SCHANNEL) { #if defined(CURL_CA_BUNDLE) @@ -536,6 +536,7 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data) set->fnmatch = ZERO_NULL; set->upkeep_interval_ms = CURL_UPKEEP_INTERVAL_DEFAULT; set->maxconnects = DEFAULT_CONNCACHE_SIZE; /* for easy handles */ + set->http09_allowed = TRUE; set->httpversion = #ifdef USE_NGHTTP2 CURL_HTTP_VERSION_2TLS @@ -768,7 +769,6 @@ CURLcode Curl_disconnect(struct Curl_easy *data, return CURLE_OK; } - conn->data = data; if(conn->dns_entry != NULL) { Curl_resolv_unlock(data, conn->dns_entry); conn->dns_entry = NULL; @@ -781,20 +781,22 @@ CURLcode Curl_disconnect(struct Curl_easy *data, Curl_http_ntlm_cleanup(conn); #endif + /* the protocol specific disconnect handler needs a transfer for its + connection! */ + conn->data = data; if(conn->handler->disconnect) /* This is set if protocol-specific cleanups should be made */ conn->handler->disconnect(conn, dead_connection); /* unlink ourselves! */ infof(data, "Closing connection %ld\n", conn->connection_id); - Curl_conncache_remove_conn(conn, TRUE); + Curl_conncache_remove_conn(data, conn, TRUE); free_idnconverted_hostname(&conn->host); free_idnconverted_hostname(&conn->conn_to_host); free_idnconverted_hostname(&conn->http_proxy.host); free_idnconverted_hostname(&conn->socks_proxy.host); - DEBUGASSERT(conn->data == data); /* this assumes that the pointer is still there after the connection was detected from the cache */ Curl_ssl_close(conn, FIRSTSOCKET); @@ -959,13 +961,10 @@ static bool extract_if_dead(struct connectdata *conn, handles in pipeline and the connection isn't already marked in use */ bool dead; - - conn->data = data; if(conn->handler->connection_check) { /* The protocol has a special method for checking the state of the connection. Use it to check if the connection is dead. */ unsigned int state; - state = conn->handler->connection_check(conn, CONNCHECK_ISDEAD); dead = (state & CONNRESULT_DEAD); } @@ -976,8 +975,7 @@ static bool extract_if_dead(struct connectdata *conn, if(dead) { infof(data, "Connection %ld seems to be dead!\n", conn->connection_id); - Curl_conncache_remove_conn(conn, FALSE); - conn->data = NULL; /* detach */ + Curl_conncache_remove_conn(data, conn, FALSE); return TRUE; } } @@ -996,6 +994,7 @@ struct prunedead { static int call_extract_if_dead(struct connectdata *conn, void *param) { struct prunedead *p = (struct prunedead *)param; + conn->data = p->data; /* transfer to use for this check */ if(extract_if_dead(conn, p->data)) { /* stop the iteration here, pass back the connection that was extracted */ p->extracted = conn; @@ -1101,7 +1100,7 @@ ConnectionExists(struct Curl_easy *data, if((bundle->multiuse == BUNDLE_UNKNOWN) && data->set.pipewait) { infof(data, "Server doesn't support multi-use yet, wait\n"); *waitpipe = TRUE; - Curl_conncache_unlock(needle); + Curl_conncache_unlock(data); return FALSE; /* no re-use */ } @@ -1461,11 +1460,11 @@ ConnectionExists(struct Curl_easy *data, if(chosen) { /* mark it as used before releasing the lock */ chosen->data = data; /* own it! */ - Curl_conncache_unlock(needle); + Curl_conncache_unlock(data); *usethis = chosen; return TRUE; /* yes, we found one to use! */ } - Curl_conncache_unlock(needle); + Curl_conncache_unlock(data); if(foundPendingCandidate && data->set.pipewait) { infof(data, @@ -2066,7 +2065,7 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, if(uc) { DEBUGF(infof(data, "curl_url_set rejected %s\n", data->change.url)); return Curl_uc_to_curlcode(uc); - } + } } uc = curl_url_get(uh, CURLUPART_SCHEME, &data->state.up.scheme, 0); @@ -2997,7 +2996,7 @@ static CURLcode parse_remote_port(struct Curl_easy *data, char portbuf[16]; CURLUcode uc; conn->remote_port = (unsigned short)data->set.use_port; - msnprintf(portbuf, sizeof(portbuf), "%u", conn->remote_port); + msnprintf(portbuf, sizeof(portbuf), "%d", conn->remote_port); uc = curl_url_set(data->state.uh, CURLUPART_PORT, portbuf, 0); if(uc) return CURLE_OUT_OF_MEMORY; @@ -3608,6 +3607,7 @@ static CURLcode create_conn(struct Curl_easy *data, size_t max_total_connections = Curl_multi_max_total_connections(data->multi); *async = FALSE; + *in_connect = NULL; /************************************************************* * Check input data @@ -3773,7 +3773,6 @@ static CURLcode create_conn(struct Curl_easy *data, /* Setup a "faked" transfer that'll do nothing */ if(!result) { - conn->data = data; conn->bits.tcpconnect[FIRSTSOCKET] = TRUE; /* we are "connected */ result = Curl_conncache_add_conn(data->state.conn_cache, conn); @@ -3954,7 +3953,7 @@ static CURLcode create_conn(struct Curl_easy *data, /* The bundle is full. Extract the oldest connection. */ conn_candidate = Curl_conncache_extract_bundle(data, bundle); - Curl_conncache_unlock(conn); + Curl_conncache_unlock(data); if(conn_candidate) (void)Curl_disconnect(data, conn_candidate, @@ -3966,7 +3965,7 @@ static CURLcode create_conn(struct Curl_easy *data, } } else - Curl_conncache_unlock(conn); + Curl_conncache_unlock(data); } @@ -4135,11 +4134,11 @@ CURLcode Curl_setup_conn(struct connectdata *conn, } CURLcode Curl_connect(struct Curl_easy *data, - struct connectdata **in_connect, bool *asyncp, bool *protocol_done) { CURLcode result; + struct connectdata *conn; *asyncp = FALSE; /* assume synchronous resolves by default */ @@ -4149,30 +4148,30 @@ CURLcode Curl_connect(struct Curl_easy *data, data->req.maxdownload = -1; /* call the stuff that needs to be called */ - result = create_conn(data, in_connect, asyncp); + result = create_conn(data, &conn, asyncp); if(!result) { - if(CONN_INUSE(*in_connect)) + if(CONN_INUSE(conn)) /* pipelining */ *protocol_done = TRUE; else if(!*asyncp) { /* DNS resolution is done: that's either because this is a reused connection, in which case DNS was unnecessary, or because DNS really did finish already (synch resolver/fast async resolve) */ - result = Curl_setup_conn(*in_connect, protocol_done); + result = Curl_setup_conn(conn, protocol_done); } } if(result == CURLE_NO_CONNECTION_AVAILABLE) { - *in_connect = NULL; return result; } - else if(result && *in_connect) { + else if(result && conn) { /* We're not allowed to return failure with memory left allocated in the connectdata struct, free those here */ - Curl_disconnect(data, *in_connect, TRUE); - *in_connect = NULL; /* return a NULL */ + Curl_disconnect(data, conn, TRUE); } + else + Curl_attach_connnection(data, conn); return result; } @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -52,8 +52,7 @@ void Curl_freeset(struct Curl_easy * data); void Curl_up_free(struct Curl_easy *data); CURLcode Curl_uc_to_curlcode(CURLUcode uc); CURLcode Curl_close(struct Curl_easy *data); /* opposite of curl_open() */ -CURLcode Curl_connect(struct Curl_easy *, struct connectdata **, - bool *async, bool *protocol_connect); +CURLcode Curl_connect(struct Curl_easy *, bool *async, bool *protocol_connect); CURLcode Curl_disconnect(struct Curl_easy *data, struct connectdata *, bool dead_connection); CURLcode Curl_protocol_connect(struct connectdata *conn, bool *done); diff --git a/lib/urlapi.c b/lib/urlapi.c index 5cbda6a98..3af8e9399 100644 --- a/lib/urlapi.c +++ b/lib/urlapi.c @@ -510,8 +510,11 @@ UNITTEST CURLUcode Curl_parse_port(struct Curl_URL *u, char *hostname) portptr = &hostname[len]; else if('%' == endbracket) { int zonelen = len; - if(1 == sscanf(hostname + zonelen, "25%*[^]]]%c%n", &endbracket, &len)) - portptr = &hostname[--zonelen + len]; + if(1 == sscanf(hostname + zonelen, "25%*[^]]%c%n", &endbracket, &len)) { + if(']' != endbracket) + return CURLUE_MALFORMED_INPUT; + portptr = &hostname[--zonelen + len + 1]; + } else return CURLUE_MALFORMED_INPUT; } @@ -534,6 +537,14 @@ UNITTEST CURLUcode Curl_parse_port(struct Curl_URL *u, char *hostname) long port; char portbuf[7]; + /* Browser behavior adaptation. If there's a colon with no digits after, + just cut off the name there which makes us ignore the colon and just + use the default port. Firefox, Chrome and Safari all do that. */ + if(!portptr[1]) { + *portptr = '\0'; + return CURLUE_OK; + } + if(!ISDIGIT(portptr[1])) return CURLUE_BAD_PORT_NUMBER; @@ -547,22 +558,14 @@ UNITTEST CURLUcode Curl_parse_port(struct Curl_URL *u, char *hostname) if(rest[0]) return CURLUE_BAD_PORT_NUMBER; - if(rest != &portptr[1]) { - *portptr++ = '\0'; /* cut off the name there */ - *rest = 0; - /* generate a new to get rid of leading zeroes etc */ - msnprintf(portbuf, sizeof(portbuf), "%ld", port); - u->portnum = port; - u->port = strdup(portbuf); - if(!u->port) - return CURLUE_OUT_OF_MEMORY; - } - else { - /* Browser behavior adaptation. If there's a colon with no digits after, - just cut off the name there which makes us ignore the colon and just - use the default port. Firefox and Chrome both do that. */ - *portptr = '\0'; - } + *portptr++ = '\0'; /* cut off the name there */ + *rest = 0; + /* generate a new port number string to get rid of leading zeroes etc */ + msnprintf(portbuf, sizeof(portbuf), "%ld", port); + u->portnum = port; + u->port = strdup(portbuf); + if(!u->port) + return CURLUE_OUT_OF_MEMORY; } return CURLUE_OK; @@ -864,7 +867,7 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags) return CURLUE_OUT_OF_MEMORY; } - if(query && query[0]) { + if(query) { u->query = strdup(query); if(!u->query) return CURLUE_OUT_OF_MEMORY; @@ -1071,8 +1074,8 @@ CURLUcode curl_url_get(CURLU *u, CURLUPart what, port ? port : "", (u->path && (u->path[0] != '/')) ? "/": "", u->path ? u->path : "/", - u->query? "?": "", - u->query? u->query : "", + (u->query && u->query[0]) ? "?": "", + (u->query && u->query[0]) ? u->query : "", u->fragment? "#": "", u->fragment? u->fragment : ""); } diff --git a/lib/urldata.h b/lib/urldata.h index b713e883b..73195c7d3 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -77,7 +77,7 @@ /* Default FTP/IMAP etc response timeout in milliseconds. Symbian OS panics when given a timeout much greater than 1/2 hour. */ -#define RESP_TIMEOUT (1800*1000) +#define RESP_TIMEOUT (120*1000) #include "cookie.h" #include "psl.h" @@ -329,6 +329,12 @@ struct kerberos5data { struct ntlmdata { curlntlm state; #ifdef USE_WINDOWS_SSPI +/* The sslContext is used for the Schannel bindings. The + * api is available on the Windows 7 SDK and later. + */ +#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS + CtxtHandle *sslContext; +#endif CredHandle *credentials; CtxtHandle *context; SEC_WINNT_AUTH_IDENTITY identity; @@ -359,6 +365,9 @@ struct negotiatedata { gss_buffer_desc output_token; #else #ifdef USE_WINDOWS_SSPI +#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS + CtxtHandle *sslContext; +#endif DWORD status; CredHandle *credentials; CtxtHandle *context; @@ -975,6 +984,9 @@ struct connectdata { void *seek_client; /* pointer to pass to the seek() above */ /*************** Request - specific items ************/ +#if defined(USE_WINDOWS_SSPI) && defined(SECPKG_ATTR_ENDPOINT_BINDINGS) + CtxtHandle *sslContext; +#endif #if defined(USE_NTLM) struct ntlmdata ntlm; /* NTLM differs from other authentication schemes @@ -1217,6 +1229,15 @@ typedef enum { EXPIRE_LAST /* not an actual timer, used as a marker only */ } expire_id; + +typedef enum { + TRAILERS_NONE, + TRAILERS_INITIALIZED, + TRAILERS_SENDING, + TRAILERS_DONE +} trailers_state; + + /* * One instance for each timeout an easy handle can set. */ @@ -1363,6 +1384,13 @@ struct UrlState { #endif CURLU *uh; /* URL handle for the current parsed URL */ struct urlpieces up; +#ifndef CURL_DISABLE_HTTP + size_t trailers_bytes_sent; + Curl_send_buffer *trailers_buf; /* a buffer containing the compiled trailing + headers */ +#endif + trailers_state trailers_state; /* whether we are sending trailers + and what stage are we at */ }; @@ -1382,6 +1410,7 @@ struct DynamicStatic { curl_easy_setopt(COOKIEFILE) calls */ struct curl_slist *resolve; /* set to point to the set.resolve list when this should be dealt with in pretransfer */ + bool wildcard_resolve; /* Set to true if any resolve change is a wildcard */ }; /* @@ -1728,9 +1757,12 @@ struct UserDefined { long upkeep_interval_ms; /* Time between calls for connection upkeep. */ bool doh; /* DNS-over-HTTPS enabled */ bool doh_get; /* use GET for DoH requests, instead of POST */ + bool http09_allowed; /* allow HTTP/0.9 responses */ multidone_func fmultidone; struct Curl_easy *dohfor; /* this is a DoH request for that transfer */ CURLU *uh; /* URL handle for the current parsed URL */ + void *trailer_data; /* pointer to pass to trailer data callback */ + curl_trailer_callback trailer_callback; /* trailing data callback */ }; struct Names { @@ -1758,9 +1790,10 @@ struct Curl_easy { struct Curl_easy *next; struct Curl_easy *prev; - struct connectdata *easy_conn; /* the "unit's" connection */ + struct connectdata *conn; struct curl_llist_element connect_queue; struct curl_llist_element pipeline_queue; + struct curl_llist_element sh_queue; /* list per Curl_sh_entry */ CURLMstate mstate; /* the handle's state */ CURLcode result; /* previous result */ @@ -1772,6 +1805,8 @@ struct Curl_easy { 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]; + int actions[MAX_SOCKSPEREASYHANDLE]; /* action for each socket in + sockets[] */ int numsocks; struct Names dns; diff --git a/lib/vauth/digest_sspi.c b/lib/vauth/digest_sspi.c index 2df1ac2db..d9db534c4 100644 --- a/lib/vauth/digest_sspi.c +++ b/lib/vauth/digest_sspi.c @@ -6,7 +6,7 @@ * \___|\___/|_| \_\_____| * * Copyright (C) 2014 - 2016, Steve Holme, <steve_holme@hotmail.com>. - * Copyright (C) 2015 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2015 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -146,7 +146,7 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, } /* Generate our SPN */ - spn = Curl_auth_build_spn(service, data->easy_conn->host.name, NULL); + spn = Curl_auth_build_spn(service, data->conn->host.name, NULL); if(!spn) { free(output_token); free(input_token); diff --git a/lib/vauth/ntlm.c b/lib/vauth/ntlm.c index 458b27253..6a8fc5ab3 100644 --- a/lib/vauth/ntlm.c +++ b/lib/vauth/ntlm.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -182,10 +182,11 @@ static CURLcode ntlm_decode_type2_target(struct Curl_easy *data, target_info_len = Curl_read16_le(&buffer[40]); target_info_offset = Curl_read32_le(&buffer[44]); if(target_info_len > 0) { - if(((target_info_offset + target_info_len) > size) || + if((target_info_offset >= size) || + ((target_info_offset + target_info_len) > size) || (target_info_offset < 48)) { infof(data, "NTLM handshake failure (bad type-2 message). " - "Target Info Offset Len is set incorrect by the peer\n"); + "Target Info Offset Len is set incorrect by the peer\n"); return CURLE_BAD_CONTENT_ENCODING; } @@ -562,7 +563,7 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, } #if defined(USE_NTRESPONSES) && defined(USE_NTLM_V2) - if(ntlm->target_info_len) { + if(ntlm->flags & NTLMFLAG_NEGOTIATE_NTLM2_KEY) { unsigned char ntbuffer[0x18]; unsigned char entropy[8]; unsigned char ntlmv2hash[0x18]; @@ -599,7 +600,7 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, #if defined(USE_NTRESPONSES) && defined(USE_NTLM2SESSION) /* We don't support NTLM2 if we don't have USE_NTRESPONSES */ - if(ntlm->flags & NTLMFLAG_NEGOTIATE_NTLM2_KEY) { + if(ntlm->flags & NTLMFLAG_NEGOTIATE_NTLM_KEY) { unsigned char ntbuffer[0x18]; unsigned char tmp[0x18]; unsigned char md5sum[MD5_DIGEST_LENGTH]; @@ -631,7 +632,9 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, Curl_ntlm_core_lm_resp(ntbuffer, md5sum, ntresp); /* End of NTLM2 Session code */ - + /* NTLM v2 session security is a misnomer because it is not NTLM v2. + It is NTLM v1 using the extended session security that is also + in NTLM v2 */ } else #endif @@ -776,11 +779,14 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, }); #ifdef USE_NTRESPONSES - if(size < (NTLM_BUFSIZE - ntresplen)) { - DEBUGASSERT(size == (size_t)ntrespoff); - memcpy(&ntlmbuf[size], ptr_ntresp, ntresplen); - size += ntresplen; + /* ntresplen + size should not be risking an integer overflow here */ + if(ntresplen + size > sizeof(ntlmbuf)) { + failf(data, "incoming NTLM message too big"); + return CURLE_OUT_OF_MEMORY; } + DEBUGASSERT(size == (size_t)ntrespoff); + memcpy(&ntlmbuf[size], ptr_ntresp, ntresplen); + size += ntresplen; DEBUG_OUT({ fprintf(stderr, "\n ntresp="); diff --git a/lib/vauth/ntlm_sspi.c b/lib/vauth/ntlm_sspi.c index 80520a251..177327b7a 100644 --- a/lib/vauth/ntlm_sspi.c +++ b/lib/vauth/ntlm_sspi.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -249,7 +249,7 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, char **outptr, size_t *outlen) { CURLcode result = CURLE_OK; - SecBuffer type_2_buf; + SecBuffer type_2_bufs[2]; SecBuffer type_3_buf; SecBufferDesc type_2_desc; SecBufferDesc type_3_desc; @@ -261,12 +261,39 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, (void) userp; /* Setup the type-2 "input" security buffer */ - type_2_desc.ulVersion = SECBUFFER_VERSION; - type_2_desc.cBuffers = 1; - type_2_desc.pBuffers = &type_2_buf; - type_2_buf.BufferType = SECBUFFER_TOKEN; - type_2_buf.pvBuffer = ntlm->input_token; - type_2_buf.cbBuffer = curlx_uztoul(ntlm->input_token_len); + type_2_desc.ulVersion = SECBUFFER_VERSION; + type_2_desc.cBuffers = 1; + type_2_desc.pBuffers = &type_2_bufs[0]; + type_2_bufs[0].BufferType = SECBUFFER_TOKEN; + type_2_bufs[0].pvBuffer = ntlm->input_token; + type_2_bufs[0].cbBuffer = curlx_uztoul(ntlm->input_token_len); + +#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS + /* ssl context comes from schannel. + * When extended protection is used in IIS server, + * we have to pass a second SecBuffer to the SecBufferDesc + * otherwise IIS will not pass the authentication (401 response). + * Minimum supported version is Windows 7. + * https://docs.microsoft.com/en-us/security-updates + * /SecurityAdvisories/2009/973811 + */ + if(ntlm->sslContext) { + SEC_CHANNEL_BINDINGS channelBindings; + SecPkgContext_Bindings pkgBindings; + pkgBindings.Bindings = &channelBindings; + status = s_pSecFn->QueryContextAttributes( + ntlm->sslContext, + SECPKG_ATTR_ENDPOINT_BINDINGS, + &pkgBindings + ); + if(status == SEC_E_OK) { + type_2_desc.cBuffers++; + type_2_bufs[1].BufferType = SECBUFFER_CHANNEL_BINDINGS; + type_2_bufs[1].cbBuffer = pkgBindings.BindingsLength; + type_2_bufs[1].pvBuffer = pkgBindings.Bindings; + } + } +#endif /* Setup the type-3 "output" security buffer */ type_3_desc.ulVersion = SECBUFFER_VERSION; diff --git a/lib/vauth/spnego_sspi.c b/lib/vauth/spnego_sspi.c index f807c49c4..aacc5d272 100644 --- a/lib/vauth/spnego_sspi.c +++ b/lib/vauth/spnego_sspi.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -92,7 +92,7 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, size_t chlglen = 0; unsigned char *chlg = NULL; PSecPkgInfo SecurityPackage; - SecBuffer chlg_buf; + SecBuffer chlg_buf[2]; SecBuffer resp_buf; SecBufferDesc chlg_desc; SecBufferDesc resp_desc; @@ -189,12 +189,39 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, } /* Setup the challenge "input" security buffer */ - chlg_desc.ulVersion = SECBUFFER_VERSION; - chlg_desc.cBuffers = 1; - chlg_desc.pBuffers = &chlg_buf; - chlg_buf.BufferType = SECBUFFER_TOKEN; - chlg_buf.pvBuffer = chlg; - chlg_buf.cbBuffer = curlx_uztoul(chlglen); + chlg_desc.ulVersion = SECBUFFER_VERSION; + chlg_desc.cBuffers = 1; + chlg_desc.pBuffers = &chlg_buf[0]; + chlg_buf[0].BufferType = SECBUFFER_TOKEN; + chlg_buf[0].pvBuffer = chlg; + chlg_buf[0].cbBuffer = curlx_uztoul(chlglen); + +#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS + /* ssl context comes from Schannel. + * When extended protection is used in IIS server, + * we have to pass a second SecBuffer to the SecBufferDesc + * otherwise IIS will not pass the authentication (401 response). + * Minimum supported version is Windows 7. + * https://docs.microsoft.com/en-us/security-updates + * /SecurityAdvisories/2009/973811 + */ + if(nego->sslContext) { + SEC_CHANNEL_BINDINGS channelBindings; + SecPkgContext_Bindings pkgBindings; + pkgBindings.Bindings = &channelBindings; + nego->status = s_pSecFn->QueryContextAttributes( + nego->sslContext, + SECPKG_ATTR_ENDPOINT_BINDINGS, + &pkgBindings + ); + if(nego->status == SEC_E_OK) { + chlg_desc.cBuffers++; + chlg_buf[1].BufferType = SECBUFFER_CHANNEL_BINDINGS; + chlg_buf[1].cbBuffer = pkgBindings.BindingsLength; + chlg_buf[1].pvBuffer = pkgBindings.Bindings; + } + } +#endif } /* Setup the response "output" security buffer */ @@ -222,7 +249,7 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, if(GSS_ERROR(nego->status)) { failf(data, "InitializeSecurityContext failed: %s", - Curl_sspi_strerror(data->easy_conn, nego->status)); + Curl_sspi_strerror(data->conn, nego->status)); return CURLE_OUT_OF_MEMORY; } diff --git a/lib/vtls/cyassl.c b/lib/vtls/cyassl.c index 0d45afbf0..ea96cf65e 100644 --- a/lib/vtls/cyassl.c +++ b/lib/vtls/cyassl.c @@ -794,6 +794,12 @@ static int Curl_cyassl_init(void) } +static void Curl_cyassl_cleanup(void) +{ + CyaSSL_Cleanup(); +} + + static bool Curl_cyassl_data_pending(const struct connectdata* conn, int connindex) { @@ -1004,7 +1010,7 @@ const struct Curl_ssl Curl_ssl_cyassl = { sizeof(struct ssl_backend_data), Curl_cyassl_init, /* init */ - Curl_none_cleanup, /* cleanup */ + Curl_cyassl_cleanup, /* cleanup */ Curl_cyassl_version, /* version */ Curl_none_check_cxn, /* check_cxn */ Curl_cyassl_shutdown, /* shutdown */ diff --git a/lib/vtls/darwinssl.c b/lib/vtls/darwinssl.c index 25b101282..bb251cdb3 100644 --- a/lib/vtls/darwinssl.c +++ b/lib/vtls/darwinssl.c @@ -1298,7 +1298,6 @@ set_ssl_version_min_max(struct connectdata *conn, int sockindex) case CURL_SSLVERSION_DEFAULT: case CURL_SSLVERSION_TLSv1: ssl_version = CURL_SSLVERSION_TLSv1_0; - ssl_version_max = max_supported_version_by_os; break; } @@ -1430,7 +1429,6 @@ static CURLcode darwinssl_connect_step1(struct connectdata *conn, #if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS if(SSLSetProtocolVersionMax != NULL) { switch(conn->ssl_config.version) { - case CURL_SSLVERSION_DEFAULT: case CURL_SSLVERSION_TLSv1: (void)SSLSetProtocolVersionMin(BACKEND->ssl_ctx, kTLSProtocol1); #if (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && HAVE_BUILTIN_AVAILABLE == 1 @@ -1445,6 +1443,7 @@ static CURLcode darwinssl_connect_step1(struct connectdata *conn, #endif /* (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && HAVE_BUILTIN_AVAILABLE == 1 */ break; + case CURL_SSLVERSION_DEFAULT: case CURL_SSLVERSION_TLSv1_0: case CURL_SSLVERSION_TLSv1_1: case CURL_SSLVERSION_TLSv1_2: diff --git a/lib/vtls/mbedtls.c b/lib/vtls/mbedtls.c index 6a20e276e..bb6a757bf 100644 --- a/lib/vtls/mbedtls.c +++ b/lib/vtls/mbedtls.c @@ -6,7 +6,7 @@ * \___|\___/|_| \_\_____| * * Copyright (C) 2010 - 2011, Hoi-Ho Chan, <hoiho.chan@gmail.com> - * Copyright (C) 2012 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2012 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -373,7 +373,7 @@ mbed_connect_step1(struct connectdata *conn, } } - infof(data, "mbedTLS: Connecting to %s:%d\n", hostname, port); + infof(data, "mbedTLS: Connecting to %s:%ld\n", hostname, port); mbedtls_ssl_config_init(&BACKEND->config); @@ -574,19 +574,21 @@ mbed_connect_step2(struct connectdata *conn, ret = mbedtls_ssl_get_verify_result(&BACKEND->ssl); + if(!SSL_CONN_CONFIG(verifyhost)) + /* Ignore hostname errors if verifyhost is disabled */ + ret &= ~MBEDTLS_X509_BADCERT_CN_MISMATCH; + if(ret && SSL_CONN_CONFIG(verifypeer)) { if(ret & MBEDTLS_X509_BADCERT_EXPIRED) failf(data, "Cert verify failed: BADCERT_EXPIRED"); - if(ret & MBEDTLS_X509_BADCERT_REVOKED) { + else if(ret & MBEDTLS_X509_BADCERT_REVOKED) failf(data, "Cert verify failed: BADCERT_REVOKED"); - return CURLE_PEER_FAILED_VERIFICATION; - } - if(ret & MBEDTLS_X509_BADCERT_CN_MISMATCH) + else if(ret & MBEDTLS_X509_BADCERT_CN_MISMATCH) failf(data, "Cert verify failed: BADCERT_CN_MISMATCH"); - if(ret & MBEDTLS_X509_BADCERT_NOT_TRUSTED) + else if(ret & MBEDTLS_X509_BADCERT_NOT_TRUSTED) failf(data, "Cert verify failed: BADCERT_NOT_TRUSTED"); return CURLE_PEER_FAILED_VERIFICATION; diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index 8bddb9a8c..9d11b89e5 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -1692,6 +1692,7 @@ static CURLcode verifystatus(struct connectdata *conn, struct ssl_connect_data *connssl) { int i, ocsp_status; + unsigned char *status; const unsigned char *p; CURLcode result = CURLE_OK; struct Curl_easy *data = conn->data; @@ -1701,14 +1702,14 @@ static CURLcode verifystatus(struct connectdata *conn, X509_STORE *st = NULL; STACK_OF(X509) *ch = NULL; - long len = SSL_get_tlsext_status_ocsp_resp(BACKEND->handle, &p); + long len = SSL_get_tlsext_status_ocsp_resp(BACKEND->handle, &status); - if(!p) { + if(!status) { failf(data, "No OCSP response received"); result = CURLE_SSL_INVALIDCERTSTATUS; goto end; } - + p = status; rsp = d2i_OCSP_RESPONSE(NULL, &p, len); if(!rsp) { failf(data, "Invalid OCSP response"); @@ -3774,7 +3775,12 @@ static size_t Curl_ossl_version(char *buffer, size_t size) { #ifdef OPENSSL_IS_BORINGSSL return msnprintf(buffer, size, OSSL_PACKAGE); -#else /* OPENSSL_IS_BORINGSSL */ +#elif defined(HAVE_OPENSSL_VERSION) && defined(OPENSSL_VERSION_STRING) + return msnprintf(buffer, size, "%s/%s", + OSSL_PACKAGE, OpenSSL_version(OPENSSL_VERSION_STRING)); +#else + /* not BoringSSL and not using OpenSSL_version */ + char sub[3]; unsigned long ssleay_value; sub[2]='\0'; diff --git a/lib/vtls/schannel.c b/lib/vtls/schannel.c index 56fd93e1e..c8574f56c 100644 --- a/lib/vtls/schannel.c +++ b/lib/vtls/schannel.c @@ -7,7 +7,7 @@ * * Copyright (C) 2012 - 2016, Marc Hoersken, <info@marc-hoersken.de> * Copyright (C) 2012, Mark Salisbury, <mark.salisbury@hp.com> - * Copyright (C) 2012 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2012 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -356,6 +356,7 @@ get_cert_location(TCHAR *path, DWORD *store_name, TCHAR **store_path, TCHAR **thumbprint) { TCHAR *sep; + TCHAR *store_path_start; size_t store_name_len; sep = _tcschr(path, TEXT('\\')); @@ -386,13 +387,17 @@ get_cert_location(TCHAR *path, DWORD *store_name, TCHAR **store_path, else return CURLE_SSL_CERTPROBLEM; - *store_path = sep + 1; + store_path_start = sep + 1; - sep = _tcschr(*store_path, TEXT('\\')); + sep = _tcschr(store_path_start, TEXT('\\')); if(sep == NULL) return CURLE_SSL_CERTPROBLEM; - *sep = 0; + *sep = TEXT('\0'); + *store_path = _tcsdup(store_path_start); + *sep = TEXT('\\'); + if(*store_path == NULL) + return CURLE_OUT_OF_MEMORY; *thumbprint = sep + 1; if(_tcslen(*thumbprint) != CERT_THUMBPRINT_STR_LEN) @@ -435,7 +440,7 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) VERSION_LESS_THAN_EQUAL)) { /* Schannel in Windows XP (OS version 5.1) uses legacy handshakes and algorithms that may not be supported by all servers. */ - infof(data, "schannel: WinSSL version is old and may not be able to " + infof(data, "schannel: Windows version is old and may not be able to " "connect to some servers due to lack of SNI, algorithms, etc.\n"); } @@ -608,9 +613,11 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) failf(data, "schannel: Failed to open cert store %x %s, " "last error is %x", cert_store_name, cert_store_path, GetLastError()); + free(cert_store_path); Curl_unicodefree(cert_path); return CURLE_SSL_CERTPROBLEM; } + free(cert_store_path); cert_thumbprint.pbData = cert_thumbprint_data; cert_thumbprint.cbData = CERT_THUMBPRINT_DATA_LEN; @@ -1414,6 +1421,16 @@ schannel_connect_common(struct connectdata *conn, int sockindex, connssl->state = ssl_connection_complete; conn->recv[sockindex] = schannel_recv; conn->send[sockindex] = schannel_send; + +#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS + /* When SSPI is used in combination with Schannel + * we need the Schannel context to create the Schannel + * binding to pass the IIS extended protection checks. + * Available on Windows 7 or later. + */ + conn->sslContext = &BACKEND->ctxt->ctxt_handle; +#endif + *done = TRUE; } else @@ -2013,9 +2030,16 @@ static int Curl_schannel_shutdown(struct connectdata *conn, int sockindex) /* free SSPI Schannel API credential handle */ if(BACKEND->cred) { - Curl_ssl_sessionid_lock(conn); + /* + * When this function is called from Curl_schannel_close() the connection + * might not have an associated transfer so the check for conn->data is + * necessary. + */ + if(conn->data) + Curl_ssl_sessionid_lock(conn); Curl_schannel_session_free(BACKEND->cred); - Curl_ssl_sessionid_unlock(conn); + if(conn->data) + Curl_ssl_sessionid_unlock(conn); BACKEND->cred = NULL; } @@ -2049,7 +2073,7 @@ static void Curl_schannel_cleanup(void) static size_t Curl_schannel_version(char *buffer, size_t size) { - size = msnprintf(buffer, size, "WinSSL"); + size = msnprintf(buffer, size, "Schannel"); return size; } @@ -2137,11 +2161,11 @@ static CURLcode pkp_pin_peer_pubkey(struct connectdata *conn, int sockindex, } static void Curl_schannel_checksum(const unsigned char *input, - size_t inputlen, - unsigned char *checksum, - size_t checksumlen, - DWORD provType, - const unsigned int algId) + size_t inputlen, + unsigned char *checksum, + size_t checksumlen, + DWORD provType, + const unsigned int algId) { HCRYPTPROV hProv = 0; HCRYPTHASH hHash = 0; @@ -2191,9 +2215,9 @@ static CURLcode Curl_schannel_md5sum(unsigned char *input, unsigned char *md5sum, size_t md5len) { - Curl_schannel_checksum(input, inputlen, md5sum, md5len, - PROV_RSA_FULL, CALG_MD5); - return CURLE_OK; + Curl_schannel_checksum(input, inputlen, md5sum, md5len, + PROV_RSA_FULL, CALG_MD5); + return CURLE_OK; } static CURLcode Curl_schannel_sha256sum(const unsigned char *input, @@ -2201,9 +2225,9 @@ static CURLcode Curl_schannel_sha256sum(const unsigned char *input, unsigned char *sha256sum, size_t sha256len) { - Curl_schannel_checksum(input, inputlen, sha256sum, sha256len, - PROV_RSA_AES, CALG_SHA_256); - return CURLE_OK; + Curl_schannel_checksum(input, inputlen, sha256sum, sha256len, + PROV_RSA_AES, CALG_SHA_256); + return CURLE_OK; } static void *Curl_schannel_get_internals(struct ssl_connect_data *connssl, diff --git a/lib/vtls/schannel_verify.c b/lib/vtls/schannel_verify.c index 8b21624ba..680f6ec5d 100644 --- a/lib/vtls/schannel_verify.c +++ b/lib/vtls/schannel_verify.c @@ -7,7 +7,7 @@ * * Copyright (C) 2012 - 2016, Marc Hoersken, <info@marc-hoersken.de> * Copyright (C) 2012, Mark Salisbury, <mark.salisbury@hp.com> - * Copyright (C) 2012 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2012 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -87,14 +87,14 @@ static CURLcode add_certs_to_store(HCERTSTORE trust_store, LARGE_INTEGER file_size; char *ca_file_buffer = NULL; char *current_ca_file_ptr = NULL; - const TCHAR *ca_file_tstr = NULL; + TCHAR *ca_file_tstr = NULL; size_t ca_file_bufsize = 0; DWORD total_bytes_read = 0; bool more_certs = 0; int num_certs = 0; size_t END_CERT_LEN; - ca_file_tstr = Curl_convert_UTF8_to_tchar(ca_file); + ca_file_tstr = Curl_convert_UTF8_to_tchar((char *)ca_file); if(!ca_file_tstr) { failf(data, "schannel: invalid path name for CA file '%s': %s", |