diff options
Diffstat (limited to 'libs/libcurl/src/http.c')
-rw-r--r-- | libs/libcurl/src/http.c | 246 |
1 files changed, 148 insertions, 98 deletions
diff --git a/libs/libcurl/src/http.c b/libs/libcurl/src/http.c index dd98e4a126..a0520b40ec 100644 --- a/libs/libcurl/src/http.c +++ b/libs/libcurl/src/http.c @@ -77,6 +77,7 @@ #include "http2.h" #include "connect.h" #include "strdup.h" +#include "altsvc.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" @@ -102,13 +103,14 @@ static int https_getsock(struct connectdata *conn, #else #define https_connecting(x,y) CURLE_COULDNT_CONNECT #endif +static CURLcode http_setup_conn(struct connectdata *conn); /* * HTTP handler interface. */ const struct Curl_handler Curl_handler_http = { "HTTP", /* scheme */ - Curl_http_setup_conn, /* setup_connection */ + http_setup_conn, /* setup_connection */ Curl_http, /* do_it */ Curl_http_done, /* done */ ZERO_NULL, /* do_more */ @@ -133,7 +135,7 @@ const struct Curl_handler Curl_handler_http = { */ const struct Curl_handler Curl_handler_https = { "HTTPS", /* scheme */ - Curl_http_setup_conn, /* setup_connection */ + http_setup_conn, /* setup_connection */ Curl_http, /* do_it */ Curl_http_done, /* done */ ZERO_NULL, /* do_more */ @@ -153,7 +155,7 @@ const struct Curl_handler Curl_handler_https = { }; #endif -CURLcode Curl_http_setup_conn(struct connectdata *conn) +static CURLcode http_setup_conn(struct connectdata *conn) { /* allocate the HTTP-specific struct for the Curl_easy, only to survive during this request */ @@ -415,7 +417,7 @@ static CURLcode http_perhapsrewind(struct connectdata *conn) break; } - bytessent = http->writebytecount; + bytessent = data->req.writebytecount; if(conn->bits.authneg) { /* This is a state where we are known to be negotiating and we don't send @@ -479,8 +481,36 @@ static CURLcode http_perhapsrewind(struct connectdata *conn) (curl_off_t)(expectsend - bytessent)); } #endif +#if defined(USE_SPNEGO) + /* There is still data left to send */ + if((data->state.authproxy.picked == CURLAUTH_NEGOTIATE) || + (data->state.authhost.picked == CURLAUTH_NEGOTIATE)) { + if(((expectsend - bytessent) < 2000) || + (conn->negotiate.state != GSS_AUTHNONE) || + (conn->proxyneg.state != GSS_AUTHNONE)) { + /* The NEGOTIATE-negotiation has started *OR* + there is just a little (<2K) data left to send, keep on sending. */ + + /* rewind data when completely done sending! */ + if(!conn->bits.authneg && (conn->writesockfd != CURL_SOCKET_BAD)) { + conn->bits.rewindaftersend = TRUE; + infof(data, "Rewind stream after send\n"); + } + + return CURLE_OK; + } + + if(conn->bits.close) + /* this is already marked to get closed */ + return CURLE_OK; - /* This is not NTLM or many bytes left to send: close */ + infof(data, "NEGOTIATE send, close instead of sending %" + CURL_FORMAT_CURL_OFF_T " bytes\n", + (curl_off_t)(expectsend - bytessent)); + } +#endif + + /* This is not NEGOTIATE/NTLM or many bytes left to send: close */ streamclose(conn, "Mid-auth HTTP and much data left to send"); data->req.size = 0; /* don't download any more than 0 bytes */ @@ -598,10 +628,6 @@ output_auth_headers(struct connectdata *conn, #if !defined(CURL_DISABLE_VERBOSE_STRINGS) || defined(USE_SPNEGO) struct Curl_easy *data = conn->data; #endif -#ifdef USE_SPNEGO - struct negotiatedata *negdata = proxy ? - &data->state.proxyneg : &data->state.negotiate; -#endif #ifdef CURL_DISABLE_CRYPTO_AUTH (void)request; @@ -609,15 +635,11 @@ output_auth_headers(struct connectdata *conn, #endif #ifdef USE_SPNEGO - negdata->state = GSS_AUTHNONE; - if((authstatus->picked == CURLAUTH_NEGOTIATE) && - negdata->context && !GSS_ERROR(negdata->status)) { + if((authstatus->picked == CURLAUTH_NEGOTIATE)) { auth = "Negotiate"; result = Curl_output_negotiate(conn, proxy); if(result) return result; - authstatus->done = TRUE; - negdata->state = GSS_AUTHSENT; } else #endif @@ -750,7 +772,7 @@ Curl_http_output_auth(struct connectdata *conn, #ifndef CURL_DISABLE_PROXY /* Send proxy authentication header if needed */ if(conn->bits.httpproxy && - (conn->bits.tunnel_proxy == proxytunnel)) { + (conn->bits.tunnel_proxy == (bit)proxytunnel)) { result = output_auth_headers(conn, authproxy, request, path, TRUE); if(result) return result; @@ -794,7 +816,7 @@ CURLcode Curl_http_input_auth(struct connectdata *conn, bool proxy, #ifdef USE_SPNEGO struct negotiatedata *negdata = proxy? - &data->state.proxyneg:&data->state.negotiate; + &conn->proxyneg:&conn->negotiate; #endif unsigned long *availp; struct auth *authp; @@ -833,21 +855,18 @@ CURLcode Curl_http_input_auth(struct connectdata *conn, bool proxy, authp->avail |= CURLAUTH_NEGOTIATE; if(authp->picked == CURLAUTH_NEGOTIATE) { - if(negdata->state == GSS_AUTHSENT || - negdata->state == GSS_AUTHNONE) { - CURLcode result = Curl_input_negotiate(conn, proxy, auth); - if(!result) { - DEBUGASSERT(!data->req.newurl); - data->req.newurl = strdup(data->change.url); - if(!data->req.newurl) - return CURLE_OUT_OF_MEMORY; - data->state.authproblem = FALSE; - /* we received a GSS auth token and we dealt with it fine */ - negdata->state = GSS_AUTHRECV; - } - else - data->state.authproblem = TRUE; + CURLcode result = Curl_input_negotiate(conn, proxy, auth); + if(!result) { + DEBUGASSERT(!data->req.newurl); + data->req.newurl = strdup(data->change.url); + if(!data->req.newurl) + return CURLE_OUT_OF_MEMORY; + data->state.authproblem = FALSE; + /* we received a GSS auth token and we dealt with it fine */ + negdata->state = GSS_AUTHRECV; } + else + data->state.authproblem = TRUE; } } } @@ -1117,14 +1136,13 @@ void Curl_add_buffer_free(Curl_send_buffer **inp) CURLcode Curl_add_buffer_send(Curl_send_buffer **inp, struct connectdata *conn, - /* add the number of sent bytes to this - counter */ - long *bytes_written, + /* add the number of sent bytes to this + counter */ + curl_off_t *bytes_written, - /* how much of the buffer contains body data */ + /* how much of the buffer contains body data */ size_t included_body_bytes, int socketindex) - { ssize_t amount; CURLcode result; @@ -1220,7 +1238,8 @@ CURLcode Curl_add_buffer_send(Curl_send_buffer **inp, if(http) { /* if we sent a piece of the body here, up the byte counter for it accordingly */ - http->writebytecount += bodylen; + data->req.writebytecount += bodylen; + Curl_pgrsSetUploadCounter(data, data->req.writebytecount); if((size_t)amount != size) { /* The whole request could not be sent in one system call. We must @@ -1553,20 +1572,6 @@ CURLcode Curl_http_done(struct connectdata *conn, Curl_unencode_cleanup(conn); -#ifdef USE_SPNEGO - if(data->state.proxyneg.state == GSS_AUTHSENT || - data->state.negotiate.state == GSS_AUTHSENT) { - /* add forbid re-use if http-code != 401/407 as a WA only needed for - * 401/407 that signal auth failure (empty) otherwise state will be RECV - * with current code. - * Do not close CONNECT_ONLY connections. */ - if((data->req.httpcode != 401) && (data->req.httpcode != 407) && - !data->set.connect_only) - streamclose(conn, "Negotiate transfer completed"); - Curl_cleanup_negotiate(data); - } -#endif - /* set the proper values (possibly modified on POST) */ conn->seek_func = data->set.seek_func; /* restore */ conn->seek_client = data->set.seek_client; /* restore */ @@ -1582,16 +1587,6 @@ CURLcode Curl_http_done(struct connectdata *conn, Curl_mime_cleanpart(&http->form); - switch(data->set.httpreq) { - case HTTPREQ_PUT: - case HTTPREQ_POST_FORM: - case HTTPREQ_POST_MIME: - data->req.bytecount = http->readbytecount + http->writebytecount; - break; - default: - break; - } - if(status) return status; @@ -1599,7 +1594,7 @@ CURLcode Curl_http_done(struct connectdata *conn, entire operation is complete */ !conn->bits.retry && !data->set.connect_only && - (http->readbytecount + + (data->req.bytecount + data->req.headerbytecount - data->req.deductheadercount) <= 0) { /* If this connection isn't simply closed to be retried, AND nothing was @@ -1789,9 +1784,16 @@ CURLcode Curl_add_custom_headers(struct connectdata *conn, } else { if(*(--ptr) == ';') { - /* send no-value custom header if terminated by semicolon */ - *ptr = ':'; - semicolonp = ptr; + /* copy the source */ + semicolonp = strdup(headers->data); + if(!semicolonp) { + Curl_add_buffer_free(&req_buffer); + return CURLE_OUT_OF_MEMORY; + } + /* put a colon where the semicolon is */ + semicolonp[ptr - headers->data] = ':'; + /* point at the colon */ + optr = &semicolonp [ptr - headers->data]; } } ptr = optr; @@ -1807,36 +1809,37 @@ CURLcode Curl_add_custom_headers(struct connectdata *conn, if(*ptr || semicolonp) { /* only send this if the contents was non-blank or done special */ CURLcode result = CURLE_OK; + char *compare = semicolonp ? semicolonp : headers->data; if(conn->allocptr.host && /* a Host: header was sent already, don't pass on any custom Host: header as that will produce *two* in the same request! */ - checkprefix("Host:", headers->data)) + checkprefix("Host:", compare)) ; else if(data->set.httpreq == HTTPREQ_POST_FORM && /* this header (extended by formdata.c) is sent later */ - checkprefix("Content-Type:", headers->data)) + checkprefix("Content-Type:", compare)) ; else if(data->set.httpreq == HTTPREQ_POST_MIME && /* this header is sent later */ - checkprefix("Content-Type:", headers->data)) + checkprefix("Content-Type:", compare)) ; else if(conn->bits.authneg && /* while doing auth neg, don't allow the custom length since we will force length zero then */ - checkprefix("Content-Length:", headers->data)) + checkprefix("Content-Length:", compare)) ; else if(conn->allocptr.te && /* when asking for Transfer-Encoding, don't pass on a custom Connection: */ - checkprefix("Connection:", headers->data)) + checkprefix("Connection:", compare)) ; else if((conn->httpversion == 20) && - checkprefix("Transfer-Encoding:", headers->data)) + checkprefix("Transfer-Encoding:", compare)) /* HTTP/2 doesn't support chunked requests */ ; - else if((checkprefix("Authorization:", headers->data) || - checkprefix("Cookie:", headers->data)) && + else if((checkprefix("Authorization:", compare) || + checkprefix("Cookie:", compare)) && /* be careful of sending this potentially sensitive header to other hosts */ (data->state.this_is_a_follow && @@ -1845,10 +1848,10 @@ CURLcode Curl_add_custom_headers(struct connectdata *conn, !strcasecompare(data->state.first_host, conn->host.name))) ; else { - result = Curl_add_bufferf(&req_buffer, "%s\r\n", headers->data); + result = Curl_add_bufferf(&req_buffer, "%s\r\n", compare); } if(semicolonp) - *semicolonp = ';'; /* put back the semicolon */ + free(semicolonp); if(result) return result; } @@ -2000,7 +2003,6 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) data->state.first_remote_port = conn->remote_port; } - http->writebytecount = http->readbytecount = 0; if((conn->handler->protocol&(PROTO_FAMILY_HTTP|CURLPROTO_FTP)) && data->set.upload) { @@ -2061,7 +2063,8 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) return result; } - if((data->state.authhost.multipass || data->state.authproxy.multipass) && + if(((data->state.authhost.multipass && !data->state.authhost.done) + || (data->state.authproxy.multipass && !data->state.authproxy.done)) && (httpreq != HTTPREQ_GET) && (httpreq != HTTPREQ_HEAD)) { /* Auth is required and we are not authenticated yet. Make a PUT or POST @@ -2697,9 +2700,8 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) failf(data, "Failed sending PUT request"); else /* prepare for transfer */ - Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE, - &http->readbytecount, postsize?FIRSTSOCKET:-1, - postsize?&http->writebytecount:NULL); + Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE, + postsize?FIRSTSOCKET:-1); if(result) return result; break; @@ -2719,12 +2721,11 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) failf(data, "Failed sending POST request"); else /* setup variables for the upcoming transfer */ - Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE, &http->readbytecount, - -1, NULL); + Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE, -1); break; } - postsize = http->postsize; + data->state.infilesize = postsize = http->postsize; /* We only set Content-Length and allow a custom Content-Length if we don't upload data chunked, as RFC2616 forbids us to set both @@ -2788,9 +2789,8 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) failf(data, "Failed sending POST request"); else /* prepare for transfer */ - Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE, - &http->readbytecount, postsize?FIRSTSOCKET:-1, - postsize?&http->writebytecount:NULL); + Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE, + postsize?FIRSTSOCKET:-1); if(result) return result; @@ -2944,9 +2944,8 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) if(result) failf(data, "Failed sending HTTP POST request"); else - Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE, - &http->readbytecount, http->postdata?FIRSTSOCKET:-1, - http->postdata?&http->writebytecount:NULL); + Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE, + http->postdata?FIRSTSOCKET:-1); break; default: @@ -2962,33 +2961,30 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) failf(data, "Failed sending HTTP request"); else /* HTTP GET/HEAD download: */ - Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE, &http->readbytecount, - http->postdata?FIRSTSOCKET:-1, - http->postdata?&http->writebytecount:NULL); + Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE, + http->postdata?FIRSTSOCKET:-1); } if(result) return result; - if(http->writebytecount) { + if(data->req.writebytecount) { /* if a request-body has been sent off, we make sure this progress is noted properly */ - Curl_pgrsSetUploadCounter(data, http->writebytecount); + Curl_pgrsSetUploadCounter(data, data->req.writebytecount); if(Curl_pgrsUpdate(conn)) result = CURLE_ABORTED_BY_CALLBACK; - if(http->writebytecount >= postsize) { + if(data->req.writebytecount >= postsize) { /* already sent the entire request body, mark the "upload" as complete */ infof(data, "upload completely sent off: %" CURL_FORMAT_CURL_OFF_T " out of %" CURL_FORMAT_CURL_OFF_T " bytes\n", - http->writebytecount, postsize); + data->req.writebytecount, postsize); data->req.upload_done = TRUE; data->req.keepon &= ~KEEP_SEND; /* we're done writing */ data->req.exp100 = EXP100_SEND_DATA; /* already sent */ Curl_expire_done(data, EXPIRE_100_TIMEOUT); } - else - data->req.writebytecount = http->writebytecount; } if((conn->httpversion == 20) && data->req.upload_chunky) @@ -3383,7 +3379,24 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, data->state.authproblem = TRUE; } #endif - +#if defined(USE_SPNEGO) + if(conn->bits.close && + (((data->req.httpcode == 401) && + (conn->negotiate.state == GSS_AUTHRECV)) || + ((data->req.httpcode == 407) && + (conn->proxyneg.state == GSS_AUTHRECV)))) { + infof(data, "Connection closure while negotiating auth (HTTP 1.0?)\n"); + data->state.authproblem = TRUE; + } + if((conn->negotiate.state == GSS_AUTHDONE) && + (data->req.httpcode != 401)) { + conn->negotiate.state = GSS_AUTHSUCC; + } + if((conn->proxyneg.state == GSS_AUTHDONE) && + (data->req.httpcode != 407)) { + conn->proxyneg.state = GSS_AUTHSUCC; + } +#endif /* * When all the headers have been parsed, see if we should give * up and return an error. @@ -3960,6 +3973,22 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, if(result) return result; } + #ifdef USE_SPNEGO + else if(checkprefix("Persistent-Auth", k->p)) { + struct negotiatedata *negdata = &conn->negotiate; + struct auth *authp = &data->state.authhost; + if(authp->picked == CURLAUTH_NEGOTIATE) { + char *persistentauth = Curl_copy_header_value(k->p); + if(!persistentauth) + return CURLE_OUT_OF_MEMORY; + negdata->noauthpersist = checkprefix("false", persistentauth); + negdata->havenoauthpersist = TRUE; + infof(data, "Negotiate: noauthpersist -> %d, header part: %s", + negdata->noauthpersist, persistentauth); + free(persistentauth); + } + } + #endif else if((k->httpcode >= 300 && k->httpcode < 400) && checkprefix("Location:", k->p) && !data->req.location) { @@ -3987,6 +4016,27 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, } } } +#ifdef USE_ALTSVC + /* If enabled, the header is incoming and this is over HTTPS */ + else if(data->asi && checkprefix("Alt-Svc:", k->p) && + ((conn->handler->flags & PROTOPT_SSL) || +#ifdef CURLDEBUG + /* allow debug builds to circumvent the HTTPS restriction */ + getenv("CURL_ALTSVC_HTTP") +#else + 0 +#endif + )) { + /* the ALPN of the current request */ + enum alpnid id = (conn->httpversion == 20) ? ALPN_h2 : ALPN_h1; + result = Curl_altsvc_parse(data, data->asi, + &k->p[ strlen("Alt-Svc:") ], + id, conn->host.name, + curlx_uitous(conn->remote_port)); + if(result) + return result; + } +#endif else if(conn->handler->protocol & CURLPROTO_RTSP) { result = Curl_rtsp_parseheader(conn, k->p); if(result) |