summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/Makefile.am2
-rw-r--r--lib/altsvc.c4
-rw-r--r--lib/cf-h2-proxy.c6
-rw-r--r--lib/cookie.c68
-rw-r--r--lib/cookie.h10
-rw-r--r--lib/curl_ntlm_core.c2
-rw-r--r--lib/curl_setup.h16
-rw-r--r--lib/curl_setup_once.h4
-rw-r--r--lib/curl_sha512_256.c1
-rw-r--r--lib/curlx/dynbuf.h2
-rw-r--r--lib/curlx/strparse.c2
-rw-r--r--lib/curlx/warnless.c6
-rw-r--r--lib/cw-out.c109
-rw-r--r--lib/ftp.c854
-rw-r--r--lib/hsts.c12
-rw-r--r--lib/http.c613
-rw-r--r--lib/http.h2
-rw-r--r--lib/http2.c5
-rw-r--r--lib/http_aws_sigv4.c2
-rw-r--r--lib/http_chunks.c3
-rw-r--r--lib/http_proxy.c5
-rw-r--r--lib/md4.c2
-rw-r--r--lib/mime.c36
-rw-r--r--lib/parsedate.c22
-rw-r--r--lib/parsedate.h4
-rw-r--r--lib/request.c2
-rw-r--r--lib/sendf.c78
-rw-r--r--lib/sendf.h21
-rw-r--r--lib/setopt.c2
-rw-r--r--lib/smtp.c3
-rw-r--r--lib/socks_sspi.c286
-rw-r--r--lib/strdup.c2
-rw-r--r--lib/url.c1
-rw-r--r--lib/urldata.h6
-rw-r--r--lib/vauth/cleartext.c4
-rw-r--r--lib/vquic/curl_ngtcp2.c4
-rw-r--r--lib/vssh/libssh.c4
-rw-r--r--lib/vssh/libssh2.c6
-rw-r--r--lib/vtls/gtls.c2
-rw-r--r--lib/vtls/openssl.c9
-rw-r--r--lib/vtls/schannel.c234
-rw-r--r--lib/vtls/schannel_int.h5
-rw-r--r--lib/vtls/wolfssl.c313
-rw-r--r--lib/ws.c41
44 files changed, 1541 insertions, 1274 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am
index f6ea5807d..973876f50 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -174,7 +174,7 @@ $(UNITPROTOS): $(CSOURCES)
$(UNIT_V)(cd $(srcdir) && @PERL@ ../scripts/extract-unit-protos $(CSOURCES) > $(top_builddir)/lib/$(UNITPROTOS))
# disable the tests that are mostly causing false positives
-TIDYFLAGS := -checks=-clang-analyzer-security.insecureAPI.bzero,-clang-analyzer-security.insecureAPI.strcpy,-clang-analyzer-optin.performance.Padding,-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling -quiet
+TIDYFLAGS := -checks=-clang-analyzer-security.insecureAPI.bzero,-clang-analyzer-security.insecureAPI.strcpy,-clang-analyzer-optin.performance.Padding,-clang-analyzer-security.ArrayBound,-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling -quiet
if CURL_WERROR
TIDYFLAGS += --warnings-as-errors=*
endif
diff --git a/lib/altsvc.c b/lib/altsvc.c
index a06f40bc2..c7448692f 100644
--- a/lib/altsvc.c
+++ b/lib/altsvc.c
@@ -187,13 +187,13 @@ static CURLcode altsvc_add(struct altsvcinfo *asi, const char *line)
else {
struct altsvc *as;
char dbuf[MAX_ALTSVC_DATELEN + 1];
- time_t expires;
+ time_t expires = 0;
/* The date parser works on a null-terminated string. The maximum length
is upheld by curlx_str_quotedword(). */
memcpy(dbuf, curlx_str(&date), curlx_strlen(&date));
dbuf[curlx_strlen(&date)] = 0;
- expires = Curl_getdate_capped(dbuf);
+ Curl_getdate_capped(dbuf, &expires);
as = altsvc_create(&srchost, &dsthost, &srcalpn, &dstalpn,
(size_t)srcport, (size_t)dstport);
if(as) {
diff --git a/lib/cf-h2-proxy.c b/lib/cf-h2-proxy.c
index 18803ed50..d67bbd55a 100644
--- a/lib/cf-h2-proxy.c
+++ b/lib/cf-h2-proxy.c
@@ -474,7 +474,7 @@ static CURLcode proxy_h2_progress_ingress(struct Curl_cfilter *cf,
Curl_bufq_len(&ctx->inbufq), result, nread);
if(result) {
if(result != CURLE_AGAIN) {
- failf(data, "Failed receiving HTTP2 data");
+ failf(data, "Failed receiving HTTP2 proxy data");
return result;
}
break;
@@ -541,7 +541,7 @@ static ssize_t on_session_send(nghttp2_session *h2,
if(!nwritten)
return NGHTTP2_ERR_WOULDBLOCK;
- return (nwritten > SSIZE_T_MAX) ?
+ return (nwritten > SSIZE_MAX) ?
NGHTTP2_ERR_CALLBACK_FAILURE : (ssize_t)nwritten;
}
@@ -817,7 +817,7 @@ static ssize_t tunnel_send_callback(nghttp2_session *session,
CURL_TRC_CF(data, cf, "[%d] tunnel_send_callback -> %zd",
ts->stream_id, nread);
- return (nread > SSIZE_T_MAX) ?
+ return (nread > SSIZE_MAX) ?
NGHTTP2_ERR_CALLBACK_FAILURE : (ssize_t)nread;
}
diff --git a/lib/cookie.c b/lib/cookie.c
index 99b5e43d6..35d33268f 100644
--- a/lib/cookie.c
+++ b/lib/cookie.c
@@ -107,7 +107,7 @@ static void strstore(char **str, const char *newstr, size_t len);
*/
static void cap_expires(time_t now, struct Cookie *co)
{
- if((TIME_T_MAX - COOKIES_MAXAGE - 30) > now) {
+ if(co->expires && (TIME_T_MAX - COOKIES_MAXAGE - 30) > now) {
timediff_t cap = now + COOKIES_MAXAGE;
if(co->expires > cap) {
cap += 30;
@@ -388,17 +388,17 @@ static void remove_expired(struct CookieInfo *ci)
for(n = Curl_llist_head(&ci->cookielist[i]); n; n = e) {
co = Curl_node_elem(n);
e = Curl_node_next(n);
- if(co->expires && co->expires < now) {
- Curl_node_remove(n);
- freecookie(co);
- ci->numcookies--;
- }
- else {
- /*
- * If this cookie has an expiration timestamp earlier than what we
- * have seen so far then record it for the next round of expirations.
- */
- if(co->expires && co->expires < ci->next_expiration)
+ if(co->expires) {
+ if(co->expires < now) {
+ Curl_node_remove(n);
+ freecookie(co);
+ ci->numcookies--;
+ }
+ else if(co->expires < ci->next_expiration)
+ /*
+ * If this cookie has an expiration timestamp earlier than what we
+ * have seen so far then record it for the next round of expirations.
+ */
ci->next_expiration = co->expires;
}
}
@@ -666,7 +666,6 @@ parse_cookie_header(struct Curl_easy *data,
if(*maxage == '\"')
maxage++;
rc = curlx_str_number(&maxage, &co->expires, CURL_OFF_T_MAX);
-
switch(rc) {
case STRE_OVERFLOW:
/* overflow, used max value */
@@ -678,8 +677,7 @@ parse_cookie_header(struct Curl_easy *data,
break;
case STRE_OK:
if(!co->expires)
- /* already expired */
- co->expires = 1;
+ co->expires = 1; /* expire now */
else if(CURL_OFF_T_MAX - now < co->expires)
/* would overflow */
co->expires = CURL_OFF_T_MAX;
@@ -698,18 +696,15 @@ parse_cookie_header(struct Curl_easy *data,
* will be treated as a session cookie
*/
char dbuf[MAX_DATE_LENGTH + 1];
+ time_t date = 0;
memcpy(dbuf, curlx_str(&val), curlx_strlen(&val));
dbuf[curlx_strlen(&val)] = 0;
- co->expires = Curl_getdate_capped(dbuf);
-
- /*
- * Session cookies have expires set to 0 so if we get that back
- * from the date parser let's add a second to make it a
- * non-session cookie
- */
- if(co->expires == 0)
- co->expires = 1;
- else if(co->expires < 0)
+ if(!Curl_getdate_capped(dbuf, &date)) {
+ if(!date)
+ date++;
+ co->expires = (curl_off_t)date;
+ }
+ else
co->expires = 0;
cap_expires(now, co);
}
@@ -1103,7 +1098,7 @@ Curl_cookie_add(struct Curl_easy *data,
if(!ci->running && /* read from a file */
ci->newsession && /* clean session cookies */
- !co->expires) /* this is a session cookie since it does not expire */
+ !co->expires) /* this is a session cookie */
goto fail;
co->livecookie = ci->running;
@@ -1294,6 +1289,14 @@ static int cookie_sort_ct(const void *p1, const void *p2)
return (c2->creationtime > c1->creationtime) ? 1 : -1;
}
+bool Curl_secure_context(struct connectdata *conn, const char *host)
+{
+ return conn->handler->protocol&(CURLPROTO_HTTPS|CURLPROTO_WSS) ||
+ curl_strequal("localhost", host) ||
+ !strcmp(host, "127.0.0.1") ||
+ !strcmp(host, "::1");
+}
+
/*
* Curl_cookie_getlist
*
@@ -1306,15 +1309,17 @@ static int cookie_sort_ct(const void *p1, const void *p2)
* Returns 0 when there is a list returned. Otherwise non-zero.
*/
int Curl_cookie_getlist(struct Curl_easy *data,
- struct CookieInfo *ci,
- const char *host, const char *path,
- bool secure,
+ struct connectdata *conn,
+ const char *host,
struct Curl_llist *list)
{
size_t matches = 0;
- bool is_ip;
+ const bool is_ip = Curl_host_is_ipnum(host);
const size_t myhash = cookiehash(host);
struct Curl_llist_node *n;
+ const bool secure = Curl_secure_context(conn, host);
+ struct CookieInfo *ci = data->cookies;
+ const char *path = data->state.up.path;
Curl_llist_init(list, NULL);
@@ -1324,9 +1329,6 @@ int Curl_cookie_getlist(struct Curl_easy *data,
/* at first, remove expired cookies */
remove_expired(ci);
- /* check if host is an IP(v4|v6) address */
- is_ip = Curl_host_is_ipnum(host);
-
for(n = Curl_llist_head(&ci->cookielist[myhash]);
n; n = Curl_node_next(n)) {
struct Cookie *co = Curl_node_elem(n);
diff --git a/lib/cookie.h b/lib/cookie.h
index 7af65073c..99aa20af7 100644
--- a/lib/cookie.h
+++ b/lib/cookie.h
@@ -105,21 +105,21 @@ struct CookieInfo {
#define MAX_COOKIE_SEND_AMOUNT 150
struct Curl_easy;
+struct connectdata;
+
/*
* Add a cookie to the internal list of cookies. The domain and path arguments
* are only used if the header boolean is TRUE.
*/
+bool Curl_secure_context(struct connectdata *conn, const char *host);
struct Cookie *Curl_cookie_add(struct Curl_easy *data,
struct CookieInfo *c, bool header,
bool noexpiry, const char *lineptr,
const char *domain, const char *path,
bool secure);
-
-int Curl_cookie_getlist(struct Curl_easy *data,
- struct CookieInfo *c, const char *host,
- const char *path, bool secure,
- struct Curl_llist *list);
+int Curl_cookie_getlist(struct Curl_easy *data, struct connectdata *conn,
+ const char *host, struct Curl_llist *list);
void Curl_cookie_clearall(struct CookieInfo *cookies);
void Curl_cookie_clearsess(struct CookieInfo *cookies);
diff --git a/lib/curl_ntlm_core.c b/lib/curl_ntlm_core.c
index fc0a97b55..e040db2ab 100644
--- a/lib/curl_ntlm_core.c
+++ b/lib/curl_ntlm_core.c
@@ -399,7 +399,7 @@ CURLcode Curl_ntlm_core_mk_nt_hash(const char *password,
size_t len = strlen(password);
unsigned char *pw;
CURLcode result;
- if(len > SIZE_T_MAX/2) /* avoid integer overflow */
+ if(len > SIZE_MAX/2) /* avoid integer overflow */
return CURLE_OUT_OF_MEMORY;
pw = len ? malloc(len * 2) : (unsigned char *)strdup("");
if(!pw)
diff --git a/lib/curl_setup.h b/lib/curl_setup.h
index 12108e6df..bc3fbf28d 100644
--- a/lib/curl_setup.h
+++ b/lib/curl_setup.h
@@ -475,6 +475,10 @@
#include <curl/stdcheaders.h>
#endif
+#if defined(HAVE_STDINT_H) || defined(USE_WOLFSSL)
+#include <stdint.h>
+#endif
+
#ifdef _WIN32
# ifdef HAVE_IO_H
# include <io.h>
@@ -618,21 +622,21 @@
# endif
#endif
-#ifndef SIZE_T_MAX
+#ifndef SIZE_MAX
/* some limits.h headers have this defined, some do not */
#if defined(SIZEOF_SIZE_T) && (SIZEOF_SIZE_T > 4)
-#define SIZE_T_MAX 18446744073709551615U
+#define SIZE_MAX 18446744073709551615U
#else
-#define SIZE_T_MAX 4294967295U
+#define SIZE_MAX 4294967295U
#endif
#endif
-#ifndef SSIZE_T_MAX
+#ifndef SSIZE_MAX
/* some limits.h headers have this defined, some do not */
#if defined(SIZEOF_SIZE_T) && (SIZEOF_SIZE_T > 4)
-#define SSIZE_T_MAX 9223372036854775807
+#define SSIZE_MAX 9223372036854775807
#else
-#define SSIZE_T_MAX 2147483647
+#define SSIZE_MAX 2147483647
#endif
#endif
diff --git a/lib/curl_setup_once.h b/lib/curl_setup_once.h
index 642e1487c..7a54760e1 100644
--- a/lib/curl_setup_once.h
+++ b/lib/curl_setup_once.h
@@ -63,10 +63,6 @@
#include <unistd.h>
#endif
-#if defined(HAVE_STDINT_H) || defined(USE_WOLFSSL)
-#include <stdint.h>
-#endif
-
/* Macro to strip 'const' without triggering a compiler warning.
Use it for APIs that do not or cannot support the const qualifier. */
#ifdef HAVE_STDINT_H
diff --git a/lib/curl_sha512_256.c b/lib/curl_sha512_256.c
index 7258e3f41..e11b2d6a9 100644
--- a/lib/curl_sha512_256.c
+++ b/lib/curl_sha512_256.c
@@ -41,7 +41,6 @@
#ifdef USE_OPENSSL
# include <openssl/opensslv.h>
# if (!defined(LIBRESSL_VERSION_NUMBER) && \
- defined(OPENSSL_VERSION_NUMBER) && \
OPENSSL_VERSION_NUMBER >= 0x10101000L) || \
(defined(LIBRESSL_VERSION_NUMBER) && \
LIBRESSL_VERSION_NUMBER >= 0x3080000fL)
diff --git a/lib/curlx/dynbuf.h b/lib/curlx/dynbuf.h
index 27335a6fb..00ca04789 100644
--- a/lib/curlx/dynbuf.h
+++ b/lib/curlx/dynbuf.h
@@ -62,7 +62,7 @@ int curlx_dyn_vprintf(struct dynbuf *dyn, const char *format, va_list ap_save);
char *curlx_dyn_take(struct dynbuf *s, size_t *plen);
/* Dynamic buffer max sizes */
-#define MAX_DYNBUF_SIZE (SIZE_T_MAX/2)
+#define MAX_DYNBUF_SIZE (SIZE_MAX/2)
#define DYN_DOH_RESPONSE 3000
#define DYN_DOH_CNAME 256
diff --git a/lib/curlx/strparse.c b/lib/curlx/strparse.c
index ecdf5b961..a29d8be2f 100644
--- a/lib/curlx/strparse.c
+++ b/lib/curlx/strparse.c
@@ -165,7 +165,7 @@ static int str_num_base(const char **linep, curl_off_t *nump, curl_off_t max,
(base == 16) ? 'f' : '7';
DEBUGASSERT(linep && *linep && nump);
DEBUGASSERT((base == 8) || (base == 10) || (base == 16));
- DEBUGASSERT(max >= 0); /* mostly to catch SIZE_T_MAX, which is too large */
+ DEBUGASSERT(max >= 0); /* mostly to catch SIZE_MAX, which is too large */
*nump = 0;
p = *linep;
if(!valid_digit(*p, m))
diff --git a/lib/curlx/warnless.c b/lib/curlx/warnless.c
index c6cb7c6e5..bb636a932 100644
--- a/lib/curlx/warnless.c
+++ b/lib/curlx/warnless.c
@@ -98,7 +98,7 @@ unsigned long curlx_uztoul(size_t uznum)
# pragma warning(disable:810) /* conversion may lose significant bits */
#endif
-#if ULONG_MAX < SIZE_T_MAX
+#if ULONG_MAX < SIZE_MAX
DEBUGASSERT(uznum <= (size_t) CURL_MASK_ULONG);
#endif
return (unsigned long)(uznum & (size_t) CURL_MASK_ULONG);
@@ -119,7 +119,7 @@ unsigned int curlx_uztoui(size_t uznum)
# pragma warning(disable:810) /* conversion may lose significant bits */
#endif
-#if UINT_MAX < SIZE_T_MAX
+#if UINT_MAX < SIZE_MAX
DEBUGASSERT(uznum <= (size_t) CURL_MASK_UINT);
#endif
return (unsigned int)(uznum & (size_t) CURL_MASK_UINT);
@@ -243,7 +243,7 @@ int curlx_sztosi(ssize_t sznum)
#endif
DEBUGASSERT(sznum >= 0);
-#if INT_MAX < SSIZE_T_MAX
+#if INT_MAX < SSIZE_MAX
DEBUGASSERT((size_t) sznum <= (size_t) CURL_MASK_SINT);
#endif
return (int)(sznum & (ssize_t) CURL_MASK_SINT);
diff --git a/lib/cw-out.c b/lib/cw-out.c
index ee7dc65df..6d988a00b 100644
--- a/lib/cw-out.c
+++ b/lib/cw-out.c
@@ -74,6 +74,7 @@
typedef enum {
CW_OUT_NONE,
CW_OUT_BODY,
+ CW_OUT_BODY_0LEN,
CW_OUT_HDS
} cw_out_type;
@@ -170,6 +171,7 @@ static void cw_get_writefunc(struct Curl_easy *data, cw_out_type otype,
{
switch(otype) {
case CW_OUT_BODY:
+ case CW_OUT_BODY_0LEN:
*pwcb = data->set.fwrite_func;
*pwcb_data = data->set.out;
*pmax_write = CURL_MAX_WRITE_SIZE;
@@ -193,6 +195,51 @@ static void cw_get_writefunc(struct Curl_easy *data, cw_out_type otype,
}
}
+static CURLcode cw_out_cb_write(struct cw_out_ctx *ctx,
+ struct Curl_easy *data,
+ curl_write_callback wcb,
+ void *wcb_data,
+ cw_out_type otype,
+ const char *buf, size_t blen,
+ size_t *pnwritten)
+{
+ size_t nwritten;
+ CURLcode result;
+
+ DEBUGASSERT(data->conn);
+ *pnwritten = 0;
+ Curl_set_in_callback(data, TRUE);
+ nwritten = wcb((char *)CURL_UNCONST(buf), 1, blen, wcb_data);
+ Curl_set_in_callback(data, FALSE);
+ CURL_TRC_WRITE(data, "[OUT] wrote %zu %s bytes -> %zu",
+ blen, (otype == CW_OUT_HDS) ? "header" : "body",
+ nwritten);
+ if(CURL_WRITEFUNC_PAUSE == nwritten) {
+ if(data->conn->handler->flags & PROTOPT_NONETWORK) {
+ /* Protocols that work without network cannot be paused. This is
+ actually only FILE:// just now, and it cannot pause since the
+ transfer is not done using the "normal" procedure. */
+ failf(data, "Write callback asked for PAUSE when not supported");
+ return CURLE_WRITE_ERROR;
+ }
+ ctx->paused = TRUE;
+ CURL_TRC_WRITE(data, "[OUT] PAUSE requested by client");
+ result = Curl_xfer_pause_recv(data, TRUE);
+ return result ? result : CURLE_AGAIN;
+ }
+ else if(CURL_WRITEFUNC_ERROR == nwritten) {
+ failf(data, "client returned ERROR on write of %zu bytes", blen);
+ return CURLE_WRITE_ERROR;
+ }
+ else if(nwritten != blen) {
+ failf(data, "Failure writing output to destination, "
+ "passed %zu returned %zd", blen, nwritten);
+ return CURLE_WRITE_ERROR;
+ }
+ *pnwritten = nwritten;
+ return CURLE_OK;
+}
+
static CURLcode cw_out_ptr_flush(struct cw_out_ctx *ctx,
struct Curl_easy *data,
cw_out_type otype,
@@ -204,6 +251,7 @@ static CURLcode cw_out_ptr_flush(struct cw_out_ctx *ctx,
void *wcb_data;
size_t max_write, min_write;
size_t wlen, nwritten;
+ CURLcode result;
/* If we errored once, we do not invoke the client callback again */
if(ctx->errored)
@@ -217,40 +265,24 @@ static CURLcode cw_out_ptr_flush(struct cw_out_ctx *ctx,
}
*pconsumed = 0;
- while(blen && !ctx->paused) {
- if(!flush_all && blen < min_write)
- break;
- wlen = max_write ? CURLMIN(blen, max_write) : blen;
- Curl_set_in_callback(data, TRUE);
- nwritten = wcb((char *)CURL_UNCONST(buf), 1, wlen, wcb_data);
- Curl_set_in_callback(data, FALSE);
- CURL_TRC_WRITE(data, "[OUT] wrote %zu %s bytes -> %zu",
- wlen, (otype == CW_OUT_BODY) ? "body" : "header",
- nwritten);
- if(CURL_WRITEFUNC_PAUSE == nwritten) {
- if(data->conn && data->conn->handler->flags & PROTOPT_NONETWORK) {
- /* Protocols that work without network cannot be paused. This is
- actually only FILE:// just now, and it cannot pause since the
- transfer is not done using the "normal" procedure. */
- failf(data, "Write callback asked for PAUSE when not supported");
- return CURLE_WRITE_ERROR;
- }
- ctx->paused = TRUE;
- CURL_TRC_WRITE(data, "[OUT] PAUSE requested by client");
- return Curl_xfer_pause_recv(data, TRUE);
- }
- else if(CURL_WRITEFUNC_ERROR == nwritten) {
- failf(data, "client returned ERROR on write of %zu bytes", wlen);
- return CURLE_WRITE_ERROR;
- }
- else if(nwritten != wlen) {
- failf(data, "Failure writing output to destination, "
- "passed %zu returned %zd", wlen, nwritten);
- return CURLE_WRITE_ERROR;
+ if(otype == CW_OUT_BODY_0LEN) {
+ DEBUGASSERT(!blen);
+ return cw_out_cb_write(ctx, data, wcb, wcb_data, otype,
+ buf, blen, &nwritten);
+ }
+ else {
+ while(blen && !ctx->paused) {
+ if(!flush_all && blen < min_write)
+ break;
+ wlen = max_write ? CURLMIN(blen, max_write) : blen;
+ result = cw_out_cb_write(ctx, data, wcb, wcb_data, otype,
+ buf, wlen, &nwritten);
+ if(result)
+ return result;
+ *pconsumed += nwritten;
+ blen -= nwritten;
+ buf += nwritten;
}
- *pconsumed += nwritten;
- blen -= nwritten;
- buf += nwritten;
}
return CURLE_OK;
}
@@ -262,14 +294,14 @@ static CURLcode cw_out_buf_flush(struct cw_out_ctx *ctx,
{
CURLcode result = CURLE_OK;
- if(curlx_dyn_len(&cwbuf->b)) {
+ if(curlx_dyn_len(&cwbuf->b) || (cwbuf->type == CW_OUT_BODY_0LEN)) {
size_t consumed;
result = cw_out_ptr_flush(ctx, data, cwbuf->type, flush_all,
curlx_dyn_ptr(&cwbuf->b),
curlx_dyn_len(&cwbuf->b),
&consumed);
- if(result)
+ if(result && (result != CURLE_AGAIN))
return result;
if(consumed) {
@@ -382,8 +414,9 @@ static CURLcode cw_out_do_write(struct cw_out_ctx *ctx,
size_t consumed;
result = cw_out_ptr_flush(ctx, data, otype, flush_all,
buf, blen, &consumed);
- if(result)
+ if(result && (result != CURLE_AGAIN))
return result;
+ result = CURLE_OK;
if(consumed < blen) {
/* did not write all, append the rest */
result = cw_out_append(ctx, data, otype,
@@ -413,7 +446,9 @@ static CURLcode cw_out_write(struct Curl_easy *data,
if((type & CLIENTWRITE_BODY) ||
((type & CLIENTWRITE_HEADER) && data->set.include_header)) {
- result = cw_out_do_write(ctx, data, CW_OUT_BODY, flush_all, buf, blen);
+ cw_out_type otype = (!blen && (type & CLIENTWRITE_0LEN)) ?
+ CW_OUT_BODY_0LEN : CW_OUT_BODY;
+ result = cw_out_do_write(ctx, data, otype, flush_all, buf, blen);
if(result)
return result;
}
diff --git a/lib/ftp.c b/lib/ftp.c
index 1da756b9d..6d7c210ce 100644
--- a/lib/ftp.c
+++ b/lib/ftp.c
@@ -571,12 +571,12 @@ static CURLcode ftp_readresp(struct Curl_easy *data,
struct ftp_conn *ftpc,
int sockindex,
struct pingpong *pp,
- int *ftpcode, /* return the ftp-code if done */
+ int *ftpcodep, /* return the ftp-code if done */
size_t *size) /* size of the response */
{
int code;
CURLcode result = Curl_pp_readresp(data, sockindex, pp, &code, size);
-
+ DEBUGASSERT(ftpcodep);
#ifdef HAVE_GSSAPI
{
struct connectdata *conn = data->conn;
@@ -604,8 +604,7 @@ static CURLcode ftp_readresp(struct Curl_easy *data,
if(!ftpc->shutdown)
data->info.httpcode = code;
- if(ftpcode)
- *ftpcode = code;
+ *ftpcodep = code;
if(code == 421) {
/* 421 means "Service not available, closing control connection." and FTP
@@ -633,7 +632,7 @@ static CURLcode ftp_readresp(struct Curl_easy *data,
CURLcode Curl_GetFTPResponse(struct Curl_easy *data,
ssize_t *nreadp, /* return number of bytes read */
- int *ftpcode) /* return the ftp-code */
+ int *ftpcodep) /* return the ftp-code */
{
/*
* We cannot read just one byte per read() and then go back to select() as
@@ -649,20 +648,16 @@ CURLcode Curl_GetFTPResponse(struct Curl_easy *data,
struct pingpong *pp = &ftpc->pp;
size_t nread;
int cache_skip = 0;
- int value_to_be_ignored = 0;
+ DEBUGASSERT(ftpcodep);
CURL_TRC_FTP(data, "getFTPResponse start");
*nreadp = 0;
- if(ftpcode)
- *ftpcode = 0; /* 0 for errors */
- else
- /* make the pointer point to something for the rest of this function */
- ftpcode = &value_to_be_ignored;
+ *ftpcodep = 0; /* 0 for errors */
if(!ftpc)
return CURLE_FAILED_INIT;
- while(!*ftpcode && !result) {
+ while(!*ftpcodep && !result) {
/* check and reset timeout value every lap */
timediff_t timeout = Curl_pp_state_timeout(data, pp, FALSE);
timediff_t interval_ms;
@@ -720,7 +715,7 @@ CURLcode Curl_GetFTPResponse(struct Curl_easy *data,
break;
}
- result = ftp_readresp(data, ftpc, FIRSTSOCKET, pp, ftpcode, &nread);
+ result = ftp_readresp(data, ftpc, FIRSTSOCKET, pp, ftpcodep, &nread);
if(result)
break;
@@ -739,7 +734,7 @@ CURLcode Curl_GetFTPResponse(struct Curl_easy *data,
pp->pending_resp = FALSE;
CURL_TRC_FTP(data, "getFTPResponse -> result=%d, nread=%zd, ftpcode=%d",
- result, *nreadp, *ftpcode);
+ result, *nreadp, *ftpcodep);
return result;
}
@@ -2107,6 +2102,7 @@ static CURLcode ftp_state_mdtm_resp(struct Curl_easy *data,
int year, month, day, hour, minute, second;
struct pingpong *pp = &ftpc->pp;
char *resp = curlx_dyn_ptr(&pp->recvbuf) + 4;
+ bool showtime = FALSE;
if(ftp_213_date(resp, &year, &month, &day, &hour, &minute, &second)) {
/* we have a time, reformat it */
char timebuf[24];
@@ -2114,7 +2110,8 @@ static CURLcode ftp_state_mdtm_resp(struct Curl_easy *data,
"%04d%02d%02d %02d:%02d:%02d GMT",
year, month, day, hour, minute, second);
/* now, convert this into a time() value: */
- data->info.filetime = Curl_getdate_capped(timebuf);
+ if(!Curl_getdate_capped(timebuf, &data->info.filetime))
+ showtime = TRUE;
}
#ifdef CURL_FTP_HTTPSTYLE_HEAD
@@ -2127,10 +2124,8 @@ static CURLcode ftp_state_mdtm_resp(struct Curl_easy *data,
warning: comparison of unsigned expression in '>= 0' is always true */
#pragma GCC diagnostic ignored "-Wtype-limits"
#endif
- if(data->req.no_body &&
- ftpc->file &&
- data->set.get_filetime &&
- (data->info.filetime >= 0) ) {
+ if(data->req.no_body && ftpc->file &&
+ data->set.get_filetime && showtime) {
#if defined(__GNUC__) && (defined(__DJGPP__) || defined(__AMIGA__))
#pragma GCC diagnostic pop
#endif
@@ -2696,6 +2691,175 @@ static CURLcode ftp_state_acct_resp(struct Curl_easy *data,
return result;
}
+static CURLcode ftp_pwd_resp(struct Curl_easy *data,
+ struct ftp_conn *ftpc,
+ int ftpcode)
+{
+ struct pingpong *pp = &ftpc->pp;
+ CURLcode result;
+
+ if(ftpcode == 257) {
+ char *ptr = curlx_dyn_ptr(&pp->recvbuf) + 4; /* start on the first
+ letter */
+ bool entry_extracted = FALSE;
+ struct dynbuf out;
+ curlx_dyn_init(&out, 1000);
+
+ /* Reply format is like
+ 257<space>[rubbish]"<directory-name>"<space><commentary> and the
+ RFC959 says
+
+ The directory name can contain any character; embedded
+ double-quotes should be escaped by double-quotes (the
+ "quote-doubling" convention).
+ */
+
+ /* scan for the first double-quote for non-standard responses */
+ while(*ptr != '\n' && *ptr != '\0' && *ptr != '"')
+ ptr++;
+
+ if('\"' == *ptr) {
+ /* it started good */
+ for(ptr++; *ptr; ptr++) {
+ if('\"' == *ptr) {
+ if('\"' == ptr[1]) {
+ /* "quote-doubling" */
+ result = curlx_dyn_addn(&out, &ptr[1], 1);
+ ptr++;
+ }
+ else {
+ /* end of path */
+ if(curlx_dyn_len(&out))
+ entry_extracted = TRUE;
+ break; /* get out of this loop */
+ }
+ }
+ else
+ result = curlx_dyn_addn(&out, ptr, 1);
+ if(result)
+ return result;
+ }
+ }
+ if(entry_extracted) {
+ /* If the path name does not look like an absolute path (i.e.: it
+ does not start with a '/'), we probably need some server-dependent
+ adjustments. For example, this is the case when connecting to
+ an OS400 FTP server: this server supports two name syntaxes,
+ the default one being incompatible with standard paths. In
+ addition, this server switches automatically to the regular path
+ syntax when one is encountered in a command: this results in
+ having an entrypath in the wrong syntax when later used in CWD.
+ The method used here is to check the server OS: we do it only
+ if the path name looks strange to minimize overhead on other
+ systems. */
+ char *dir = curlx_dyn_ptr(&out);
+
+ if(!ftpc->server_os && dir[0] != '/') {
+ result = Curl_pp_sendf(data, &ftpc->pp, "%s", "SYST");
+ if(result) {
+ free(dir);
+ return result;
+ }
+ free(ftpc->entrypath);
+ ftpc->entrypath = dir; /* remember this */
+ infof(data, "Entry path is '%s'", ftpc->entrypath);
+ /* also save it where getinfo can access it: */
+ free(data->state.most_recent_ftp_entrypath);
+ data->state.most_recent_ftp_entrypath = strdup(ftpc->entrypath);
+ if(!data->state.most_recent_ftp_entrypath)
+ return CURLE_OUT_OF_MEMORY;
+ ftp_state(data, ftpc, FTP_SYST);
+ return result;
+ }
+
+ free(ftpc->entrypath);
+ ftpc->entrypath = dir; /* remember this */
+ infof(data, "Entry path is '%s'", ftpc->entrypath);
+ /* also save it where getinfo can access it: */
+ free(data->state.most_recent_ftp_entrypath);
+ data->state.most_recent_ftp_entrypath = strdup(ftpc->entrypath);
+ if(!data->state.most_recent_ftp_entrypath)
+ return CURLE_OUT_OF_MEMORY;
+ }
+ else {
+ /* could not get the path */
+ curlx_dyn_free(&out);
+ infof(data, "Failed to figure out path");
+ }
+ }
+ ftp_state(data, ftpc, FTP_STOP); /* we are done with CONNECT phase! */
+ CURL_TRC_FTP(data, "[%s] protocol connect phase DONE", FTP_CSTATE(ftpc));
+ return CURLE_OK;
+}
+
+static const char * const ftpauth[] = { "SSL", "TLS" };
+
+static CURLcode ftp_wait_resp(struct Curl_easy *data,
+ struct connectdata *conn,
+ struct ftp_conn *ftpc,
+ int ftpcode)
+{
+ CURLcode result = CURLE_OK;
+ if(ftpcode == 230) {
+ /* 230 User logged in - already! Take as 220 if TLS required. */
+ if(data->set.use_ssl <= CURLUSESSL_TRY ||
+ conn->bits.ftp_use_control_ssl)
+ return ftp_state_user_resp(data, ftpc, ftpcode);
+ }
+ else if(ftpcode != 220) {
+ failf(data, "Got a %03d ftp-server response when 220 was expected",
+ ftpcode);
+ return CURLE_WEIRD_SERVER_REPLY;
+ }
+
+ /* We have received a 220 response fine, now we proceed. */
+#ifdef HAVE_GSSAPI
+ if(data->set.krb) {
+ /* If not anonymous login, try a secure login. Note that this
+ procedure is still BLOCKING. */
+
+ Curl_sec_request_prot(conn, "private");
+ /* We set private first as default, in case the line below fails to
+ set a valid level */
+ Curl_sec_request_prot(conn, data->set.str[STRING_KRB_LEVEL]);
+
+ if(Curl_sec_login(data, conn)) {
+ failf(data, "secure login failed");
+ return CURLE_WEIRD_SERVER_REPLY;
+ }
+ infof(data, "Authentication successful");
+ }
+#endif
+
+ if(data->set.use_ssl && !conn->bits.ftp_use_control_ssl) {
+ /* We do not have an SSL/TLS control connection yet, but FTPS is
+ requested. Try an FTPS connection now */
+
+ ftpc->count3 = 0;
+ switch((long)data->set.ftpsslauth) {
+ case CURLFTPAUTH_DEFAULT:
+ case CURLFTPAUTH_SSL:
+ ftpc->count2 = 1; /* add one to get next */
+ ftpc->count1 = 0;
+ break;
+ case CURLFTPAUTH_TLS:
+ ftpc->count2 = -1; /* subtract one to get next */
+ ftpc->count1 = 1;
+ break;
+ default:
+ failf(data, "unsupported parameter to CURLOPT_FTPSSLAUTH: %d",
+ (int)data->set.ftpsslauth);
+ return CURLE_UNKNOWN_OPTION; /* we do not know what to do */
+ }
+ result = Curl_pp_sendf(data, &ftpc->pp, "AUTH %s",
+ ftpauth[ftpc->count1]);
+ if(!result)
+ ftp_state(data, ftpc, FTP_AUTH);
+ }
+ else
+ result = ftp_state_user(data, ftpc, conn);
+ return result;
+}
static CURLcode ftp_pp_statemachine(struct Curl_easy *data,
struct connectdata *conn)
@@ -2705,7 +2869,6 @@ static CURLcode ftp_pp_statemachine(struct Curl_easy *data,
struct ftp_conn *ftpc = Curl_conn_meta_get(conn, CURL_META_FTP_CONN);
struct FTP *ftp = Curl_meta_get(data, CURL_META_FTP_EASY);
struct pingpong *pp;
- static const char * const ftpauth[] = { "SSL", "TLS" };
size_t nread = 0;
if(!ftpc || !ftp)
@@ -2715,455 +2878,306 @@ static CURLcode ftp_pp_statemachine(struct Curl_easy *data,
return Curl_pp_flushsend(data, pp);
result = ftp_readresp(data, ftpc, FIRSTSOCKET, pp, &ftpcode, &nread);
- if(result)
+ if(result || !ftpcode)
return result;
- if(ftpcode) {
- /* we have now received a full FTP server response */
- switch(ftpc->state) {
- case FTP_WAIT220:
- if(ftpcode == 230) {
- /* 230 User logged in - already! Take as 220 if TLS required. */
- if(data->set.use_ssl <= CURLUSESSL_TRY ||
- conn->bits.ftp_use_control_ssl)
- return ftp_state_user_resp(data, ftpc, ftpcode);
- }
- else if(ftpcode != 220) {
- failf(data, "Got a %03d ftp-server response when 220 was expected",
- ftpcode);
- return CURLE_WEIRD_SERVER_REPLY;
- }
-
- /* We have received a 220 response fine, now we proceed. */
-#ifdef HAVE_GSSAPI
- if(data->set.krb) {
- /* If not anonymous login, try a secure login. Note that this
- procedure is still BLOCKING. */
-
- Curl_sec_request_prot(conn, "private");
- /* We set private first as default, in case the line below fails to
- set a valid level */
- Curl_sec_request_prot(conn, data->set.str[STRING_KRB_LEVEL]);
-
- if(Curl_sec_login(data, conn)) {
- failf(data, "secure login failed");
- return CURLE_WEIRD_SERVER_REPLY;
- }
- infof(data, "Authentication successful");
- }
-#endif
-
- if(data->set.use_ssl && !conn->bits.ftp_use_control_ssl) {
- /* We do not have an SSL/TLS control connection yet, but FTPS is
- requested. Try an FTPS connection now */
-
- ftpc->count3 = 0;
- switch((long)data->set.ftpsslauth) {
- case CURLFTPAUTH_DEFAULT:
- case CURLFTPAUTH_SSL:
- ftpc->count2 = 1; /* add one to get next */
- ftpc->count1 = 0;
- break;
- case CURLFTPAUTH_TLS:
- ftpc->count2 = -1; /* subtract one to get next */
- ftpc->count1 = 1;
- break;
- default:
- failf(data, "unsupported parameter to CURLOPT_FTPSSLAUTH: %d",
- (int)data->set.ftpsslauth);
- return CURLE_UNKNOWN_OPTION; /* we do not know what to do */
- }
- result = Curl_pp_sendf(data, &ftpc->pp, "AUTH %s",
- ftpauth[ftpc->count1]);
- if(!result)
- ftp_state(data, ftpc, FTP_AUTH);
- }
- else
- result = ftp_state_user(data, ftpc, conn);
- break;
+ /* we have now received a full FTP server response */
+ switch(ftpc->state) {
+ case FTP_WAIT220:
+ result = ftp_wait_resp(data, conn, ftpc, ftpcode);
+ break;
- case FTP_AUTH:
- /* we have gotten the response to a previous AUTH command */
+ case FTP_AUTH:
+ /* we have gotten the response to a previous AUTH command */
- if(pp->overflow)
- return CURLE_WEIRD_SERVER_REPLY; /* Forbid pipelining in response. */
+ if(pp->overflow)
+ return CURLE_WEIRD_SERVER_REPLY; /* Forbid pipelining in response. */
- /* RFC2228 (page 5) says:
- *
- * If the server is willing to accept the named security mechanism,
- * and does not require any security data, it must respond with
- * reply code 234/334.
- */
+ /* RFC2228 (page 5) says:
+ *
+ * If the server is willing to accept the named security mechanism,
+ * and does not require any security data, it must respond with
+ * reply code 234/334.
+ */
- if((ftpcode == 234) || (ftpcode == 334)) {
- /* this was BLOCKING, keep it so for now */
- bool done;
- if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
- result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET);
- if(result) {
- /* we failed and bail out */
- return CURLE_USE_SSL_FAILED;
- }
- }
- result = Curl_conn_connect(data, FIRSTSOCKET, TRUE, &done);
- if(!result) {
- conn->bits.ftp_use_data_ssl = FALSE; /* clear-text data */
- conn->bits.ftp_use_control_ssl = TRUE; /* SSL on control */
- result = ftp_state_user(data, ftpc, conn);
+ if((ftpcode == 234) || (ftpcode == 334)) {
+ /* this was BLOCKING, keep it so for now */
+ bool done;
+ if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
+ result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET);
+ if(result) {
+ /* we failed and bail out */
+ return CURLE_USE_SSL_FAILED;
}
}
- else if(ftpc->count3 < 1) {
- ftpc->count3++;
- ftpc->count1 += ftpc->count2; /* get next attempt */
- result = Curl_pp_sendf(data, &ftpc->pp, "AUTH %s",
- ftpauth[ftpc->count1]);
- /* remain in this same state */
- }
- else {
- if(data->set.use_ssl > CURLUSESSL_TRY)
- /* we failed and CURLUSESSL_CONTROL or CURLUSESSL_ALL is set */
- result = CURLE_USE_SSL_FAILED;
- else
- /* ignore the failure and continue */
- result = ftp_state_user(data, ftpc, conn);
- }
- break;
-
- case FTP_USER:
- case FTP_PASS:
- result = ftp_state_user_resp(data, ftpc, ftpcode);
- break;
-
- case FTP_ACCT:
- result = ftp_state_acct_resp(data, ftpc, ftpcode);
- break;
-
- case FTP_PBSZ:
- result =
- Curl_pp_sendf(data, &ftpc->pp, "PROT %c",
- data->set.use_ssl == CURLUSESSL_CONTROL ? 'C' : 'P');
- if(!result)
- ftp_state(data, ftpc, FTP_PROT);
- break;
-
- case FTP_PROT:
- if(ftpcode/100 == 2)
- /* We have enabled SSL for the data connection! */
- conn->bits.ftp_use_data_ssl =
- (data->set.use_ssl != CURLUSESSL_CONTROL);
- /* FTP servers typically responds with 500 if they decide to reject
- our 'P' request */
- else if(data->set.use_ssl > CURLUSESSL_CONTROL)
- /* we failed and bails out */
- return CURLE_USE_SSL_FAILED;
-
- if(data->set.ftp_ccc) {
- /* CCC - Clear Command Channel
- */
- result = Curl_pp_sendf(data, &ftpc->pp, "%s", "CCC");
- if(!result)
- ftp_state(data, ftpc, FTP_CCC);
+ result = Curl_conn_connect(data, FIRSTSOCKET, TRUE, &done);
+ if(!result) {
+ conn->bits.ftp_use_data_ssl = FALSE; /* clear-text data */
+ conn->bits.ftp_use_control_ssl = TRUE; /* SSL on control */
+ result = ftp_state_user(data, ftpc, conn);
}
+ }
+ else if(ftpc->count3 < 1) {
+ ftpc->count3++;
+ ftpc->count1 += ftpc->count2; /* get next attempt */
+ result = Curl_pp_sendf(data, &ftpc->pp, "AUTH %s",
+ ftpauth[ftpc->count1]);
+ /* remain in this same state */
+ }
+ else {
+ if(data->set.use_ssl > CURLUSESSL_TRY)
+ /* we failed and CURLUSESSL_CONTROL or CURLUSESSL_ALL is set */
+ result = CURLE_USE_SSL_FAILED;
else
- result = ftp_state_pwd(data, ftpc);
- break;
-
- case FTP_CCC:
- if(ftpcode < 500) {
- /* First shut down the SSL layer (note: this call will block) */
- /* This has only been tested on the proftpd server, and the mod_tls
- * code sends a close notify alert without waiting for a close notify
- * alert in response. Thus we wait for a close notify alert from the
- * server, but we do not send one. Let's hope other servers do
- * the same... */
- result = Curl_ssl_cfilter_remove(data, FIRSTSOCKET,
- (data->set.ftp_ccc == (unsigned char)CURLFTPSSL_CCC_ACTIVE));
+ /* ignore the failure and continue */
+ result = ftp_state_user(data, ftpc, conn);
+ }
+ break;
- if(result)
- failf(data, "Failed to clear the command channel (CCC)");
- }
- if(!result)
- /* Then continue as normal */
- result = ftp_state_pwd(data, ftpc);
- break;
+ case FTP_USER:
+ case FTP_PASS:
+ result = ftp_state_user_resp(data, ftpc, ftpcode);
+ break;
- case FTP_PWD:
- if(ftpcode == 257) {
- char *ptr = curlx_dyn_ptr(&pp->recvbuf) + 4; /* start on the first
- letter */
- bool entry_extracted = FALSE;
- struct dynbuf out;
- curlx_dyn_init(&out, 1000);
+ case FTP_ACCT:
+ result = ftp_state_acct_resp(data, ftpc, ftpcode);
+ break;
- /* Reply format is like
- 257<space>[rubbish]"<directory-name>"<space><commentary> and the
- RFC959 says
+ case FTP_PBSZ:
+ result =
+ Curl_pp_sendf(data, &ftpc->pp, "PROT %c",
+ data->set.use_ssl == CURLUSESSL_CONTROL ? 'C' : 'P');
+ if(!result)
+ ftp_state(data, ftpc, FTP_PROT);
+ break;
- The directory name can contain any character; embedded
- double-quotes should be escaped by double-quotes (the
- "quote-doubling" convention).
- */
+ case FTP_PROT:
+ if(ftpcode/100 == 2)
+ /* We have enabled SSL for the data connection! */
+ conn->bits.ftp_use_data_ssl =
+ (data->set.use_ssl != CURLUSESSL_CONTROL);
+ /* FTP servers typically responds with 500 if they decide to reject
+ our 'P' request */
+ else if(data->set.use_ssl > CURLUSESSL_CONTROL)
+ /* we failed and bails out */
+ return CURLE_USE_SSL_FAILED;
+
+ if(data->set.ftp_ccc) {
+ /* CCC - Clear Command Channel
+ */
+ result = Curl_pp_sendf(data, &ftpc->pp, "%s", "CCC");
+ if(!result)
+ ftp_state(data, ftpc, FTP_CCC);
+ }
+ else
+ result = ftp_state_pwd(data, ftpc);
+ break;
- /* scan for the first double-quote for non-standard responses */
- while(*ptr != '\n' && *ptr != '\0' && *ptr != '"')
- ptr++;
+ case FTP_CCC:
+ if(ftpcode < 500) {
+ /* First shut down the SSL layer (note: this call will block) */
+ /* This has only been tested on the proftpd server, and the mod_tls
+ * code sends a close notify alert without waiting for a close notify
+ * alert in response. Thus we wait for a close notify alert from the
+ * server, but we do not send one. Let's hope other servers do
+ * the same... */
+ result = Curl_ssl_cfilter_remove(data, FIRSTSOCKET,
+ (data->set.ftp_ccc ==
+ (unsigned char)CURLFTPSSL_CCC_ACTIVE));
+ if(result)
+ failf(data, "Failed to clear the command channel (CCC)");
+ }
+ if(!result)
+ /* Then continue as normal */
+ result = ftp_state_pwd(data, ftpc);
+ break;
- if('\"' == *ptr) {
- /* it started good */
- for(ptr++; *ptr; ptr++) {
- if('\"' == *ptr) {
- if('\"' == ptr[1]) {
- /* "quote-doubling" */
- result = curlx_dyn_addn(&out, &ptr[1], 1);
- ptr++;
- }
- else {
- /* end of path */
- if(curlx_dyn_len(&out))
- entry_extracted = TRUE;
- break; /* get out of this loop */
- }
- }
- else
- result = curlx_dyn_addn(&out, ptr, 1);
- if(result)
- return result;
- }
- }
- if(entry_extracted) {
- /* If the path name does not look like an absolute path (i.e.: it
- does not start with a '/'), we probably need some server-dependent
- adjustments. For example, this is the case when connecting to
- an OS400 FTP server: this server supports two name syntaxes,
- the default one being incompatible with standard paths. In
- addition, this server switches automatically to the regular path
- syntax when one is encountered in a command: this results in
- having an entrypath in the wrong syntax when later used in CWD.
- The method used here is to check the server OS: we do it only
- if the path name looks strange to minimize overhead on other
- systems. */
- char *dir = curlx_dyn_ptr(&out);
-
- if(!ftpc->server_os && dir[0] != '/') {
- result = Curl_pp_sendf(data, &ftpc->pp, "%s", "SYST");
- if(result) {
- free(dir);
- return result;
- }
- free(ftpc->entrypath);
- ftpc->entrypath = dir; /* remember this */
- infof(data, "Entry path is '%s'", ftpc->entrypath);
- /* also save it where getinfo can access it: */
- free(data->state.most_recent_ftp_entrypath);
- data->state.most_recent_ftp_entrypath = strdup(ftpc->entrypath);
- if(!data->state.most_recent_ftp_entrypath)
- return CURLE_OUT_OF_MEMORY;
- ftp_state(data, ftpc, FTP_SYST);
- break;
- }
+ case FTP_PWD:
+ result = ftp_pwd_resp(data, ftpc, ftpcode);
+ break;
- free(ftpc->entrypath);
- ftpc->entrypath = dir; /* remember this */
- infof(data, "Entry path is '%s'", ftpc->entrypath);
- /* also save it where getinfo can access it: */
- free(data->state.most_recent_ftp_entrypath);
- data->state.most_recent_ftp_entrypath = strdup(ftpc->entrypath);
- if(!data->state.most_recent_ftp_entrypath)
- return CURLE_OUT_OF_MEMORY;
- }
- else {
- /* could not get the path */
- curlx_dyn_free(&out);
- infof(data, "Failed to figure out path");
- }
- }
- ftp_state(data, ftpc, FTP_STOP); /* we are done with CONNECT phase! */
- CURL_TRC_FTP(data, "[%s] protocol connect phase DONE", FTP_CSTATE(ftpc));
- break;
+ case FTP_SYST:
+ if(ftpcode == 215) {
+ char *ptr = curlx_dyn_ptr(&pp->recvbuf) + 4; /* start on the first
+ letter */
+ char *os;
+ char *start;
- case FTP_SYST:
- if(ftpcode == 215) {
- char *ptr = curlx_dyn_ptr(&pp->recvbuf) + 4; /* start on the first
- letter */
- char *os;
- char *start;
-
- /* Reply format is like
- 215<space><OS-name><space><commentary>
- */
- while(*ptr == ' ')
- ptr++;
- for(start = ptr; *ptr && *ptr != ' '; ptr++)
- ;
- os = Curl_memdup0(start, ptr - start);
- if(!os)
- return CURLE_OUT_OF_MEMORY;
+ /* Reply format is like
+ 215<space><OS-name><space><commentary>
+ */
+ while(*ptr == ' ')
+ ptr++;
+ for(start = ptr; *ptr && *ptr != ' '; ptr++)
+ ;
+ os = Curl_memdup0(start, ptr - start);
+ if(!os)
+ return CURLE_OUT_OF_MEMORY;
- /* Check for special servers here. */
- if(curl_strequal(os, "OS/400")) {
- /* Force OS400 name format 1. */
- result = Curl_pp_sendf(data, &ftpc->pp, "%s", "SITE NAMEFMT 1");
- if(result) {
- free(os);
- return result;
- }
- /* remember target server OS */
- free(ftpc->server_os);
- ftpc->server_os = os;
- ftp_state(data, ftpc, FTP_NAMEFMT);
- break;
+ /* Check for special servers here. */
+ if(curl_strequal(os, "OS/400")) {
+ /* Force OS400 name format 1. */
+ result = Curl_pp_sendf(data, &ftpc->pp, "%s", "SITE NAMEFMT 1");
+ if(result) {
+ free(os);
+ return result;
}
- /* Nothing special for the target server. */
/* remember target server OS */
free(ftpc->server_os);
ftpc->server_os = os;
+ ftp_state(data, ftpc, FTP_NAMEFMT);
+ break;
}
- else {
- /* Cannot identify server OS. Continue anyway and cross fingers. */
- }
+ /* Nothing special for the target server. */
+ /* remember target server OS */
+ free(ftpc->server_os);
+ ftpc->server_os = os;
+ }
+ else {
+ /* Cannot identify server OS. Continue anyway and cross fingers. */
+ }
+
+ ftp_state(data, ftpc, FTP_STOP); /* we are done with CONNECT phase! */
+ CURL_TRC_FTP(data, "[%s] protocol connect phase DONE", FTP_CSTATE(ftpc));
+ break;
- ftp_state(data, ftpc, FTP_STOP); /* we are done with CONNECT phase! */
- CURL_TRC_FTP(data, "[%s] protocol connect phase DONE", FTP_CSTATE(ftpc));
+ case FTP_NAMEFMT:
+ if(ftpcode == 250) {
+ /* Name format change successful: reload initial path. */
+ ftp_state_pwd(data, ftpc);
break;
+ }
- case FTP_NAMEFMT:
- if(ftpcode == 250) {
- /* Name format change successful: reload initial path. */
- ftp_state_pwd(data, ftpc);
- break;
- }
+ ftp_state(data, ftpc, FTP_STOP); /* we are done with CONNECT phase! */
+ CURL_TRC_FTP(data, "[%s] protocol connect phase DONE", FTP_CSTATE(ftpc));
+ break;
- ftp_state(data, ftpc, FTP_STOP); /* we are done with CONNECT phase! */
- CURL_TRC_FTP(data, "[%s] protocol connect phase DONE", FTP_CSTATE(ftpc));
- break;
+ case FTP_QUOTE:
+ case FTP_POSTQUOTE:
+ case FTP_RETR_PREQUOTE:
+ case FTP_STOR_PREQUOTE:
+ case FTP_LIST_PREQUOTE:
+ if((ftpcode >= 400) && !ftpc->count2) {
+ /* failure response code, and not allowed to fail */
+ failf(data, "QUOT command failed with %03d", ftpcode);
+ result = CURLE_QUOTE_ERROR;
+ }
+ else
+ result = ftp_state_quote(data, ftpc, ftp, FALSE, ftpc->state);
+ break;
- case FTP_QUOTE:
- case FTP_POSTQUOTE:
- case FTP_RETR_PREQUOTE:
- case FTP_STOR_PREQUOTE:
- case FTP_LIST_PREQUOTE:
- if((ftpcode >= 400) && !ftpc->count2) {
- /* failure response code, and not allowed to fail */
- failf(data, "QUOT command failed with %03d", ftpcode);
- result = CURLE_QUOTE_ERROR;
- }
- else
- result = ftp_state_quote(data, ftpc, ftp, FALSE, ftpc->state);
- break;
+ case FTP_CWD:
+ if(ftpcode/100 != 2) {
+ /* failure to CWD there */
+ if(data->set.ftp_create_missing_dirs &&
+ ftpc->cwdcount && !ftpc->count2) {
+ /* try making it */
+ ftpc->count2++; /* counter to prevent CWD-MKD loops */
- case FTP_CWD:
- if(ftpcode/100 != 2) {
- /* failure to CWD there */
- if(data->set.ftp_create_missing_dirs &&
- ftpc->cwdcount && !ftpc->count2) {
- /* try making it */
- ftpc->count2++; /* counter to prevent CWD-MKD loops */
-
- /* count3 is set to allow MKD to fail once per dir. In the case when
- CWD fails and then MKD fails (due to another session raced it to
- create the dir) this then allows for a second try to CWD to it. */
- ftpc->count3 = (data->set.ftp_create_missing_dirs == 2) ? 1 : 0;
-
- result = Curl_pp_sendf(data, &ftpc->pp, "MKD %.*s",
- pathlen(ftpc, ftpc->cwdcount - 1),
- pathpiece(ftpc, ftpc->cwdcount - 1));
- if(!result)
- ftp_state(data, ftpc, FTP_MKD);
- }
- else {
- /* return failure */
- failf(data, "Server denied you to change to the given directory");
- ftpc->cwdfail = TRUE; /* do not remember this path as we failed
- to enter it */
- result = CURLE_REMOTE_ACCESS_DENIED;
- }
+ /* count3 is set to allow MKD to fail once per dir. In the case when
+ CWD fails and then MKD fails (due to another session raced it to
+ create the dir) this then allows for a second try to CWD to it. */
+ ftpc->count3 = (data->set.ftp_create_missing_dirs == 2) ? 1 : 0;
+
+ result = Curl_pp_sendf(data, &ftpc->pp, "MKD %.*s",
+ pathlen(ftpc, ftpc->cwdcount - 1),
+ pathpiece(ftpc, ftpc->cwdcount - 1));
+ if(!result)
+ ftp_state(data, ftpc, FTP_MKD);
}
else {
- /* success */
- ftpc->count2 = 0;
- if(ftpc->cwdcount >= ftpc->dirdepth)
- result = ftp_state_mdtm(data, ftpc, ftp);
- else {
- ftpc->cwdcount++;
- /* send next CWD */
- result = Curl_pp_sendf(data, &ftpc->pp, "CWD %.*s",
- pathlen(ftpc, ftpc->cwdcount - 1),
- pathpiece(ftpc, ftpc->cwdcount - 1));
- }
- }
- break;
-
- case FTP_MKD:
- if((ftpcode/100 != 2) && !ftpc->count3--) {
- /* failure to MKD the dir */
- failf(data, "Failed to MKD dir: %03d", ftpcode);
+ /* return failure */
+ failf(data, "Server denied you to change to the given directory");
+ ftpc->cwdfail = TRUE; /* do not remember this path as we failed
+ to enter it */
result = CURLE_REMOTE_ACCESS_DENIED;
}
+ }
+ else {
+ /* success */
+ ftpc->count2 = 0;
+ if(ftpc->cwdcount >= ftpc->dirdepth)
+ result = ftp_state_mdtm(data, ftpc, ftp);
else {
- ftp_state(data, ftpc, FTP_CWD);
- /* send CWD */
+ ftpc->cwdcount++;
+ /* send next CWD */
result = Curl_pp_sendf(data, &ftpc->pp, "CWD %.*s",
pathlen(ftpc, ftpc->cwdcount - 1),
pathpiece(ftpc, ftpc->cwdcount - 1));
}
- break;
+ }
+ break;
- case FTP_MDTM:
- result = ftp_state_mdtm_resp(data, ftpc, ftp, ftpcode);
- break;
+ case FTP_MKD:
+ if((ftpcode/100 != 2) && !ftpc->count3--) {
+ /* failure to MKD the dir */
+ failf(data, "Failed to MKD dir: %03d", ftpcode);
+ result = CURLE_REMOTE_ACCESS_DENIED;
+ }
+ else {
+ ftp_state(data, ftpc, FTP_CWD);
+ /* send CWD */
+ result = Curl_pp_sendf(data, &ftpc->pp, "CWD %.*s",
+ pathlen(ftpc, ftpc->cwdcount - 1),
+ pathpiece(ftpc, ftpc->cwdcount - 1));
+ }
+ break;
- case FTP_TYPE:
- case FTP_LIST_TYPE:
- case FTP_RETR_TYPE:
- case FTP_STOR_TYPE:
- case FTP_RETR_LIST_TYPE:
- result = ftp_state_type_resp(data, ftpc, ftp, ftpcode, ftpc->state);
- break;
+ case FTP_MDTM:
+ result = ftp_state_mdtm_resp(data, ftpc, ftp, ftpcode);
+ break;
- case FTP_SIZE:
- case FTP_RETR_SIZE:
- case FTP_STOR_SIZE:
- result = ftp_state_size_resp(data, ftpc, ftp, ftpcode, ftpc->state);
- break;
+ case FTP_TYPE:
+ case FTP_LIST_TYPE:
+ case FTP_RETR_TYPE:
+ case FTP_STOR_TYPE:
+ case FTP_RETR_LIST_TYPE:
+ result = ftp_state_type_resp(data, ftpc, ftp, ftpcode, ftpc->state);
+ break;
- case FTP_REST:
- case FTP_RETR_REST:
- result = ftp_state_rest_resp(data, ftpc, ftp, ftpcode, ftpc->state);
- break;
+ case FTP_SIZE:
+ case FTP_RETR_SIZE:
+ case FTP_STOR_SIZE:
+ result = ftp_state_size_resp(data, ftpc, ftp, ftpcode, ftpc->state);
+ break;
- case FTP_PRET:
- if(ftpcode != 200) {
- /* there only is this one standard OK return code. */
- failf(data, "PRET command not accepted: %03d", ftpcode);
- return CURLE_FTP_PRET_FAILED;
- }
- result = ftp_state_use_pasv(data, ftpc, conn);
- break;
+ case FTP_REST:
+ case FTP_RETR_REST:
+ result = ftp_state_rest_resp(data, ftpc, ftp, ftpcode, ftpc->state);
+ break;
- case FTP_PASV:
- result = ftp_state_pasv_resp(data, ftpc, ftpcode);
- break;
+ case FTP_PRET:
+ if(ftpcode != 200) {
+ /* there only is this one standard OK return code. */
+ failf(data, "PRET command not accepted: %03d", ftpcode);
+ return CURLE_FTP_PRET_FAILED;
+ }
+ result = ftp_state_use_pasv(data, ftpc, conn);
+ break;
- case FTP_PORT:
- result = ftp_state_port_resp(data, ftpc, ftp, ftpcode);
- break;
+ case FTP_PASV:
+ result = ftp_state_pasv_resp(data, ftpc, ftpcode);
+ break;
- case FTP_LIST:
- case FTP_RETR:
- result = ftp_state_get_resp(data, ftpc, ftp, ftpcode, ftpc->state);
- break;
+ case FTP_PORT:
+ result = ftp_state_port_resp(data, ftpc, ftp, ftpcode);
+ break;
- case FTP_STOR:
- result = ftp_state_stor_resp(data, ftpc, ftpcode, ftpc->state);
- break;
+ case FTP_LIST:
+ case FTP_RETR:
+ result = ftp_state_get_resp(data, ftpc, ftp, ftpcode, ftpc->state);
+ break;
- case FTP_QUIT:
- default:
- /* internal error */
- ftp_state(data, ftpc, FTP_STOP);
- break;
- }
- } /* if(ftpcode) */
+ case FTP_STOR:
+ result = ftp_state_stor_resp(data, ftpc, ftpcode, ftpc->state);
+ break;
+
+ case FTP_QUIT:
+ default:
+ /* internal error */
+ ftp_state(data, ftpc, FTP_STOP);
+ break;
+ }
return result;
}
diff --git a/lib/hsts.c b/lib/hsts.c
index 91f32276f..9525158bc 100644
--- a/lib/hsts.c
+++ b/lib/hsts.c
@@ -426,7 +426,7 @@ static CURLcode hsts_add(struct hsts *h, const char *line)
bool subdomain = FALSE;
struct stsentry *e;
char dbuf[MAX_HSTS_DATELEN + 1];
- time_t expires;
+ time_t expires = 0;
const char *hp = curlx_str(&host);
/* The date parser works on a null-terminated string. The maximum length
@@ -434,8 +434,10 @@ static CURLcode hsts_add(struct hsts *h, const char *line)
memcpy(dbuf, curlx_str(&date), curlx_strlen(&date));
dbuf[curlx_strlen(&date)] = 0;
- expires = strcmp(dbuf, UNLIMITED) ? Curl_getdate_capped(dbuf) :
- TIME_T_MAX;
+ if(!strcmp(dbuf, UNLIMITED))
+ expires = TIME_T_MAX;
+ else
+ Curl_getdate_capped(dbuf, &expires);
if(hp[0] == '.') {
curlx_str_nudge(&host, 1);
@@ -478,14 +480,14 @@ static CURLcode hsts_pull(struct Curl_easy *data, struct hsts *h)
e.name[0] = 0; /* just to make it clean */
sc = data->set.hsts_read(data, &e, data->set.hsts_read_userp);
if(sc == CURLSTS_OK) {
- time_t expires;
+ time_t expires = 0;
CURLcode result;
DEBUGASSERT(e.name[0]);
if(!e.name[0])
/* bail out if no name was stored */
return CURLE_BAD_FUNCTION_ARGUMENT;
if(e.expire[0])
- expires = Curl_getdate_capped(e.expire);
+ Curl_getdate_capped(e.expire, &expires);
else
expires = TIME_T_MAX; /* the end of time */
result = hsts_create(h, e.name, strlen(e.name),
diff --git a/lib/http.c b/lib/http.c
index 06ab3f164..bcbe6e9e7 100644
--- a/lib/http.c
+++ b/lib/http.c
@@ -105,24 +105,16 @@ static void http_exp100_got100(struct Curl_easy *data);
static CURLcode http_firstwrite(struct Curl_easy *data);
static CURLcode http_header(struct Curl_easy *data,
const char *hd, size_t hdlen);
-static CURLcode http_host(struct Curl_easy *data, struct connectdata *conn);
static CURLcode http_range(struct Curl_easy *data,
Curl_HttpReq httpreq);
-static CURLcode http_req_complete(struct Curl_easy *data,
- struct dynbuf *r, int httpversion,
- Curl_HttpReq httpreq);
-static CURLcode http_req_set_reader(struct Curl_easy *data,
- Curl_HttpReq httpreq, int httpversion,
- const char **tep);
+static CURLcode http_req_set_TE(struct Curl_easy *data,
+ struct dynbuf *req,
+ int httpversion);
static CURLcode http_size(struct Curl_easy *data);
static CURLcode http_statusline(struct Curl_easy *data,
struct connectdata *conn);
-static CURLcode http_target(struct Curl_easy *data, struct connectdata *conn,
- struct dynbuf *req);
+static CURLcode http_target(struct Curl_easy *data, struct dynbuf *req);
static CURLcode http_useragent(struct Curl_easy *data);
-#ifdef HAVE_LIBZ
-static CURLcode http_transferencode(struct Curl_easy *data);
-#endif
/*
@@ -1709,10 +1701,8 @@ CURLcode Curl_add_custom_headers(struct Curl_easy *data,
we will force length zero then */
curlx_str_casecompare(&name, "Content-Length"))
;
- else if(data->state.aptr.te &&
- /* when asking for Transfer-Encoding, do not pass on a custom
- Connection: */
- curlx_str_casecompare(&name, "Connection"))
+ else if(curlx_str_casecompare(&name, "Connection"))
+ /* Normal Connection: header generation takes care of this */
;
else if((httpversion >= 20) &&
curlx_str_casecompare(&name, "Transfer-Encoding"))
@@ -1817,14 +1807,14 @@ CURLcode Curl_add_timecondition(struct Curl_easy *data,
}
#endif
-void Curl_http_method(struct Curl_easy *data, struct connectdata *conn,
+void Curl_http_method(struct Curl_easy *data,
const char **method, Curl_HttpReq *reqp)
{
Curl_HttpReq httpreq = (Curl_HttpReq)data->state.httpreq;
const char *request;
- if(conn->handler->protocol&(CURLPROTO_WS|CURLPROTO_WSS))
+ if(data->conn->handler->protocol&(CURLPROTO_WS|CURLPROTO_WSS))
httpreq = HTTPREQ_GET;
- else if((conn->handler->protocol&(PROTO_FAMILY_HTTP|CURLPROTO_FTP)) &&
+ else if((data->conn->handler->protocol&(PROTO_FAMILY_HTTP|CURLPROTO_FTP)) &&
data->state.upload)
httpreq = HTTPREQ_PUT;
@@ -1875,10 +1865,12 @@ static CURLcode http_useragent(struct Curl_easy *data)
}
-static CURLcode http_host(struct Curl_easy *data, struct connectdata *conn)
+static CURLcode http_set_aptr_host(struct Curl_easy *data)
{
- const char *ptr;
+ struct connectdata *conn = data->conn;
struct dynamically_allocated_data *aptr = &data->state.aptr;
+ const char *ptr;
+
if(!data->state.this_is_a_follow) {
/* Free to avoid leaking memory on multiple requests */
free(data->state.first_host);
@@ -1966,7 +1958,6 @@ static CURLcode http_host(struct Curl_easy *data, struct connectdata *conn)
* Append the request-target to the HTTP request
*/
static CURLcode http_target(struct Curl_easy *data,
- struct connectdata *conn,
struct dynbuf *r)
{
CURLcode result = CURLE_OK;
@@ -1979,7 +1970,7 @@ static CURLcode http_target(struct Curl_easy *data,
}
#ifndef CURL_DISABLE_PROXY
- if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) {
+ if(data->conn->bits.httpproxy && !data->conn->bits.tunnel_proxy) {
/* Using a proxy but does not tunnel through it */
/* The path sent to the proxy is in fact the entire URL. But if the remote
@@ -1993,8 +1984,8 @@ static CURLcode http_target(struct Curl_easy *data,
if(!h)
return CURLE_OUT_OF_MEMORY;
- if(conn->host.dispname != conn->host.name) {
- uc = curl_url_set(h, CURLUPART_HOST, conn->host.name, 0);
+ if(data->conn->host.dispname != data->conn->host.name) {
+ uc = curl_url_set(h, CURLUPART_HOST, data->conn->host.name, 0);
if(uc) {
curl_url_cleanup(h);
return CURLE_OUT_OF_MEMORY;
@@ -2060,8 +2051,6 @@ static CURLcode http_target(struct Curl_easy *data,
}
else
-#else
- (void)conn; /* not used in disabled-proxy builds */
#endif
{
result = curlx_dyn_add(r, path);
@@ -2247,21 +2236,13 @@ static CURLcode http_resume(struct Curl_easy *data, Curl_HttpReq httpreq)
return CURLE_OK;
}
-static CURLcode http_req_set_reader(struct Curl_easy *data,
- Curl_HttpReq httpreq, int httpversion,
- const char **tep)
+static CURLcode http_req_set_TE(struct Curl_easy *data,
+ struct dynbuf *req,
+ int httpversion)
{
CURLcode result = CURLE_OK;
const char *ptr;
- result = set_reader(data, httpreq);
- if(result)
- return result;
-
- result = http_resume(data, httpreq);
- if(result)
- return result;
-
ptr = Curl_checkheaders(data, STRCONST("Transfer-Encoding"));
if(ptr) {
/* Some kind of TE is requested, check if 'chunked' is chosen */
@@ -2295,7 +2276,7 @@ static CURLcode http_req_set_reader(struct Curl_easy *data,
}
if(data->req.upload_chunky)
- *tep = "Transfer-Encoding: chunked\r\n";
+ result = curlx_dyn_add(req, "Transfer-Encoding: chunked\r\n");
}
return result;
}
@@ -2335,9 +2316,10 @@ static CURLcode addexpect(struct Curl_easy *data, struct dynbuf *r,
return CURLE_OK;
}
-static CURLcode http_req_complete(struct Curl_easy *data,
- struct dynbuf *r, int httpversion,
- Curl_HttpReq httpreq)
+static CURLcode http_add_content_hds(struct Curl_easy *data,
+ struct dynbuf *r,
+ int httpversion,
+ Curl_HttpReq httpreq)
{
CURLcode result = CURLE_OK;
curl_off_t req_clen;
@@ -2405,26 +2387,17 @@ static CURLcode http_req_complete(struct Curl_easy *data,
break;
}
- /* end of headers */
- result = curlx_dyn_addn(r, STRCONST("\r\n"));
- if(!result) {
- Curl_pgrsSetUploadSize(data, req_clen);
- if(announced_exp100)
- result = http_exp100_add_reader(data);
- }
+ Curl_pgrsSetUploadSize(data, req_clen);
+ if(announced_exp100)
+ result = http_exp100_add_reader(data);
out:
- if(!result) {
- /* setup variables for the upcoming transfer */
- Curl_xfer_setup_sendrecv(data, FIRSTSOCKET, -1);
- }
return result;
}
#ifndef CURL_DISABLE_COOKIES
static CURLcode http_cookies(struct Curl_easy *data,
- struct connectdata *conn,
struct dynbuf *r)
{
CURLcode result = CURLE_OK;
@@ -2441,15 +2414,9 @@ static CURLcode http_cookies(struct Curl_easy *data,
if(data->cookies && data->state.cookie_engine) {
const char *host = data->state.aptr.cookiehost ?
- data->state.aptr.cookiehost : conn->host.name;
- const bool secure_context =
- conn->handler->protocol&(CURLPROTO_HTTPS|CURLPROTO_WSS) ||
- curl_strequal("localhost", host) ||
- !strcmp(host, "127.0.0.1") ||
- !strcmp(host, "::1");
+ data->state.aptr.cookiehost : data->conn->host.name;
Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
- rc = Curl_cookie_getlist(data, data->cookies, host, data->state.up.path,
- secure_context, &list);
+ rc = Curl_cookie_getlist(data, data->conn, host, &list);
Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
}
if(!rc) {
@@ -2500,7 +2467,7 @@ static CURLcode http_cookies(struct Curl_easy *data,
return result;
}
#else
-#define http_cookies(a,b,c) CURLE_OK
+#define http_cookies(a,b) CURLE_OK
#endif
static CURLcode http_range(struct Curl_easy *data,
@@ -2622,63 +2589,13 @@ static CURLcode http_firstwrite(struct Curl_easy *data)
return CURLE_OK;
}
-#ifdef HAVE_LIBZ
-static CURLcode http_transferencode(struct Curl_easy *data)
-{
- if(!Curl_checkheaders(data, STRCONST("TE")) &&
- data->set.http_transfer_encoding) {
- /* When we are to insert a TE: header in the request, we must also insert
- TE in a Connection: header, so we need to merge the custom provided
- Connection: header and prevent the original to get sent. Note that if
- the user has inserted his/her own TE: header we do not do this magic
- but then assume that the user will handle it all! */
- char *cptr = Curl_checkheaders(data, STRCONST("Connection"));
-#define TE_HEADER "TE: gzip\r\n"
-
- Curl_safefree(data->state.aptr.te);
-
- if(cptr) {
- cptr = Curl_copy_header_value(cptr);
- if(!cptr)
- return CURLE_OUT_OF_MEMORY;
- }
-
- /* Create the (updated) Connection: header */
- data->state.aptr.te = aprintf("Connection: %s%sTE\r\n" TE_HEADER,
- cptr ? cptr : "", (cptr && *cptr) ? ", ":"");
-
- free(cptr);
- if(!data->state.aptr.te)
- return CURLE_OUT_OF_MEMORY;
- }
- return CURLE_OK;
-}
-#endif
-
-/*
- * Curl_http() gets called from the generic multi_do() function when an HTTP
- * request is to be performed. This creates and sends a properly constructed
- * HTTP request.
- */
-CURLcode Curl_http(struct Curl_easy *data, bool *done)
+static CURLcode http_check_new_conn(struct Curl_easy *data)
{
struct connectdata *conn = data->conn;
- CURLcode result = CURLE_OK;
- Curl_HttpReq httpreq;
- const char *te = ""; /* transfer-encoding */
- const char *request;
- const char *httpstring;
- struct dynbuf req;
- char *altused = NULL;
- const char *p_accept; /* Accept: string */
- unsigned char httpversion;
- const char *alpn;
const char *info_version = NULL;
+ const char *alpn;
+ CURLcode result;
- /* Always consider the DO phase done after this function call, even if there
- may be parts of the request that are not yet sent, since we can deal with
- the rest of the request in the PERFORM phase. */
- *done = TRUE;
alpn = Curl_conn_get_alpn_negotiated(data, conn);
if(alpn && !strcmp("h3", alpn)) {
DEBUGASSERT(Curl_conn_http_version(data, conn) == 30);
@@ -2690,7 +2607,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
conn->bits.proxy && !conn->bits.tunnel_proxy) {
result = Curl_http2_switch(data);
if(result)
- goto fail;
+ return result;
}
else
#endif
@@ -2703,7 +2620,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
DEBUGF(infof(data, "HTTP/2 over clean TCP"));
result = Curl_http2_switch(data);
if(result)
- goto fail;
+ return result;
info_version = "HTTP/2";
/* There is no ALPN here, but the connection is now definitely h2 */
conn->httpversion_seen = 20;
@@ -2712,213 +2629,321 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
info_version = "HTTP/1.x";
}
- if(info_version && !data->conn->bits.reuse)
+ if(info_version)
infof(data, "using %s", info_version);
+ return CURLE_OK;
+}
- /* Add collecting of headers written to client. For a new connection,
- * we might have done that already, but reuse
- * or multiplex needs it here as well. */
- result = Curl_headers_init(data);
- if(result)
- goto fail;
+static CURLcode http_add_connection_hd(struct Curl_easy *data,
+ struct dynbuf *req)
+{
+ char *custom = Curl_checkheaders(data, STRCONST("Connection"));
+ char *custom_val = custom ? Curl_copy_header_value(custom) : NULL;
+ const char *sep = (custom_val && *custom_val) ? ", " : "Connection: ";
+ CURLcode result = CURLE_OK;
+ size_t rlen = curlx_dyn_len(req);
- result = http_host(data, conn);
- if(result)
- goto fail;
+ if(custom && !custom_val)
+ return CURLE_OUT_OF_MEMORY;
- result = http_useragent(data);
- if(result)
- goto fail;
+ if(custom_val && *custom_val)
+ result = curlx_dyn_addf(req, "Connection: %s", custom_val);
+ if(!result && data->state.http_hd_te) {
+ result = curlx_dyn_addf(req, "%s%s", sep, "TE");
+ sep = ", ";
+ }
+ if(!result && data->state.http_hd_upgrade) {
+ result = curlx_dyn_addf(req, "%s%s", sep, "Upgrade");
+ sep = ", ";
+ }
+ if(!result && data->state.http_hd_h2_settings) {
+ result = curlx_dyn_addf(req, "%s%s", sep, "HTTP2-Settings");
+ }
+ if(rlen < curlx_dyn_len(req))
+ result = curlx_dyn_addn(req, STRCONST("\r\n"));
- Curl_http_method(data, conn, &request, &httpreq);
+ free(custom_val);
+ return result;
+}
- /* setup the authentication headers */
- {
- char *pq = NULL;
- if(data->state.up.query) {
- pq = aprintf("%s?%s", data->state.up.path, data->state.up.query);
- if(!pq)
- return CURLE_OUT_OF_MEMORY;
- }
- result = Curl_http_output_auth(data, conn, request, httpreq,
- (pq ? pq : data->state.up.path), FALSE);
- free(pq);
- if(result)
- goto fail;
- }
+/* Header identifier in order we send them by default */
+typedef enum {
+ H1_HD_REQUEST,
+ H1_HD_HOST,
+#ifndef CURL_DISABLE_PROXY
+ H1_HD_PROXY_AUTH,
+#endif
+ H1_HD_USER_AUTH,
+ H1_HD_RANGE,
+ H1_HD_USER_AGENT,
+ H1_HD_ACCEPT,
+ H1_HD_TE,
+ H1_HD_ACCEPT_ENCODING,
+ H1_HD_REFERER,
+#ifndef CURL_DISABLE_PROXY
+ H1_HD_PROXY_CONNECTION,
+#endif
+ H1_HD_TRANSFER_ENCODING,
+#ifndef CURL_DISABLE_ALTSVC
+ H1_HD_ALT_USED,
+#endif
+ H1_HD_UPGRADE,
+ H1_HD_COOKIES,
+ H1_HD_CONDITIONALS,
+ H1_HD_CUSTOM,
+ H1_HD_CONTENT,
+ H1_HD_CONNECTION,
+ H1_HD_LAST /* the last, empty header line */
+} http_hd_t;
+
+static CURLcode http_add_hd(struct Curl_easy *data,
+ struct dynbuf *req,
+ http_hd_t id,
+ unsigned char httpversion,
+ const char *method,
+ Curl_HttpReq httpreq)
+{
+ CURLcode result = CURLE_OK;
+ switch(id) {
+ case H1_HD_REQUEST:
+ /* add the main request stuff */
+ /* GET/HEAD/POST/PUT */
+ result = curlx_dyn_addf(req, "%s ", method);
+ if(!result)
+ result = http_target(data, req);
+ if(!result)
+ result = curlx_dyn_addf(req, " HTTP/%s\r\n",
+ get_http_string(httpversion));
+ break;
- Curl_safefree(data->state.aptr.ref);
- if(data->state.referer && !Curl_checkheaders(data, STRCONST("Referer"))) {
- data->state.aptr.ref = aprintf("Referer: %s\r\n", data->state.referer);
- if(!data->state.aptr.ref)
- return CURLE_OUT_OF_MEMORY;
- }
+ case H1_HD_HOST:
+ if(data->state.aptr.host)
+ result = curlx_dyn_add(req, data->state.aptr.host);
+ break;
- if(!Curl_checkheaders(data, STRCONST("Accept-Encoding")) &&
- data->set.str[STRING_ENCODING]) {
- free(data->state.aptr.accept_encoding);
- data->state.aptr.accept_encoding =
- aprintf("Accept-Encoding: %s\r\n", data->set.str[STRING_ENCODING]);
- if(!data->state.aptr.accept_encoding)
- return CURLE_OUT_OF_MEMORY;
- }
- else
- Curl_safefree(data->state.aptr.accept_encoding);
+#ifndef CURL_DISABLE_PROXY
+ case H1_HD_PROXY_AUTH:
+ if(data->state.aptr.proxyuserpwd)
+ result = curlx_dyn_add(req, data->state.aptr.proxyuserpwd);
+ break;
+#endif
+
+ case H1_HD_USER_AUTH:
+ if(data->state.aptr.userpwd)
+ result = curlx_dyn_add(req, data->state.aptr.userpwd);
+ break;
+
+ case H1_HD_RANGE:
+ if(data->state.use_range && data->state.aptr.rangeline)
+ result = curlx_dyn_add(req, data->state.aptr.rangeline);
+ break;
+
+ case H1_HD_USER_AGENT:
+ if(data->set.str[STRING_USERAGENT] && /* User-Agent: */
+ *data->set.str[STRING_USERAGENT] &&
+ data->state.aptr.uagent)
+ result = curlx_dyn_add(req, data->state.aptr.uagent);
+ break;
+
+ case H1_HD_ACCEPT:
+ if(!Curl_checkheaders(data, STRCONST("Accept")))
+ result = curlx_dyn_add(req, "Accept: */*\r\n");
+ break;
+ case H1_HD_TE:
#ifdef HAVE_LIBZ
- /* we only consider transfer-encoding magic if libz support is built-in */
- result = http_transferencode(data);
- if(result)
- goto fail;
+ if(!Curl_checkheaders(data, STRCONST("TE")) &&
+ data->set.http_transfer_encoding) {
+ data->state.http_hd_te = TRUE;
+ result = curlx_dyn_add(req, "TE: gzip\r\n");
+ }
#endif
+ break;
- httpversion = http_request_version(data);
- httpstring = get_http_string(httpversion);
+ case H1_HD_ACCEPT_ENCODING:
+ Curl_safefree(data->state.aptr.accept_encoding);
+ if(!Curl_checkheaders(data, STRCONST("Accept-Encoding")) &&
+ data->set.str[STRING_ENCODING])
+ result = curlx_dyn_addf(req, "Accept-Encoding: %s\r\n",
+ data->set.str[STRING_ENCODING]);
+ break;
- result = http_req_set_reader(data, httpreq, httpversion, &te);
- if(result)
- goto fail;
+ case H1_HD_REFERER:
+ Curl_safefree(data->state.aptr.ref);
+ if(data->state.referer && !Curl_checkheaders(data, STRCONST("Referer")))
+ result = curlx_dyn_addf(req, "Referer: %s\r\n", data->state.referer);
+ break;
- p_accept = Curl_checkheaders(data,
- STRCONST("Accept")) ? NULL : "Accept: */*\r\n";
+#ifndef CURL_DISABLE_PROXY
+ case H1_HD_PROXY_CONNECTION:
+ if(data->conn->bits.httpproxy &&
+ !data->conn->bits.tunnel_proxy &&
+ !Curl_checkheaders(data, STRCONST("Proxy-Connection")) &&
+ !Curl_checkProxyheaders(data, data->conn, STRCONST("Proxy-Connection")))
+ result = curlx_dyn_add(req, "Proxy-Connection: Keep-Alive\r\n");
+ break;
+#endif
- result = http_range(data, httpreq);
- if(result)
- goto fail;
+ case H1_HD_TRANSFER_ENCODING:
+ result = http_req_set_TE(data, req, httpversion);
+ break;
+
+#ifndef CURL_DISABLE_ALTSVC
+ case H1_HD_ALT_USED:
+ if(data->conn->bits.altused &&
+ !Curl_checkheaders(data, STRCONST("Alt-Used")))
+ result = curlx_dyn_addf(req, "Alt-Used: %s:%d\r\n",
+ data->conn->conn_to_host.name,
+ data->conn->conn_to_port);
+ break;
+#endif
+
+ case H1_HD_UPGRADE:
+ if(!Curl_conn_is_ssl(data->conn, FIRSTSOCKET) && (httpversion < 20) &&
+ (data->state.http_neg.wanted & CURL_HTTP_V2x) &&
+ data->state.http_neg.h2_upgrade) {
+ /* append HTTP2 upgrade magic stuff to the HTTP request if it is not done
+ over SSL */
+ result = Curl_http2_request_upgrade(req, data);
+ }
+#ifndef CURL_DISABLE_WEBSOCKETS
+ if(!result && data->conn->handler->protocol&(CURLPROTO_WS|CURLPROTO_WSS))
+ result = Curl_ws_request(data, req);
+#endif
+ break;
+
+ case H1_HD_COOKIES:
+ result = http_cookies(data, req);
+ break;
+
+ case H1_HD_CONDITIONALS:
+ result = Curl_add_timecondition(data, req);
+ break;
+
+ case H1_HD_CUSTOM:
+ result = Curl_add_custom_headers(data, FALSE, httpversion, req);
+ break;
+
+ case H1_HD_CONTENT:
+ result = http_add_content_hds(data, req, httpversion, httpreq);
+ break;
+
+ case H1_HD_CONNECTION: {
+ result = http_add_connection_hd(data, req);
+ break;
+ }
+
+ case H1_HD_LAST:
+ result = curlx_dyn_addn(req, STRCONST("\r\n"));
+ break;
+ }
+ return result;
+}
+/*
+ * Curl_http() gets called from the generic multi_do() function when an HTTP
+ * request is to be performed. This creates and sends a properly constructed
+ * HTTP request.
+ */
+CURLcode Curl_http(struct Curl_easy *data, bool *done)
+{
+ CURLcode result = CURLE_OK;
+ Curl_HttpReq httpreq;
+ const char *method;
+ struct dynbuf req;
+ unsigned char httpversion;
+ size_t hd_id;
+
+ /* Always consider the DO phase done after this function call, even if there
+ may be parts of the request that are not yet sent, since we can deal with
+ the rest of the request in the PERFORM phase. */
+ *done = TRUE;
/* initialize a dynamic send-buffer */
curlx_dyn_init(&req, DYN_HTTP_REQUEST);
-
/* make sure the header buffer is reset - if there are leftovers from a
previous transfer */
curlx_dyn_reset(&data->state.headerb);
- /* add the main request stuff */
- /* GET/HEAD/POST/PUT */
- result = curlx_dyn_addf(&req, "%s ", request);
- if(!result)
- result = http_target(data, conn, &req);
- if(result) {
- curlx_dyn_free(&req);
- goto fail;
+ if(!data->conn->bits.reuse) {
+ result = http_check_new_conn(data);
+ if(result)
+ goto out;
}
-#ifndef CURL_DISABLE_ALTSVC
- if(conn->bits.altused && !Curl_checkheaders(data, STRCONST("Alt-Used"))) {
- altused = aprintf("Alt-Used: %s:%d\r\n",
- conn->conn_to_host.name, conn->conn_to_port);
- if(!altused) {
- curlx_dyn_free(&req);
- return CURLE_OUT_OF_MEMORY;
- }
- }
-#endif
- result =
- curlx_dyn_addf(&req,
- " HTTP/%s\r\n" /* HTTP version */
- "%s" /* host */
- "%s" /* proxyuserpwd */
- "%s" /* userpwd */
- "%s" /* range */
- "%s" /* user agent */
- "%s" /* accept */
- "%s" /* TE: */
- "%s" /* accept-encoding */
- "%s" /* referer */
- "%s" /* Proxy-Connection */
- "%s" /* transfer-encoding */
- "%s",/* Alt-Used */
-
- httpstring,
- (data->state.aptr.host ? data->state.aptr.host : ""),
-#ifndef CURL_DISABLE_PROXY
- data->state.aptr.proxyuserpwd ?
- data->state.aptr.proxyuserpwd : "",
-#else
- "",
-#endif
- data->state.aptr.userpwd ? data->state.aptr.userpwd : "",
- (data->state.use_range && data->state.aptr.rangeline) ?
- data->state.aptr.rangeline : "",
- (data->set.str[STRING_USERAGENT] &&
- *data->set.str[STRING_USERAGENT] &&
- data->state.aptr.uagent) ?
- data->state.aptr.uagent : "",
- p_accept ? p_accept : "",
- data->state.aptr.te ? data->state.aptr.te : "",
- (data->set.str[STRING_ENCODING] &&
- *data->set.str[STRING_ENCODING] &&
- data->state.aptr.accept_encoding) ?
- data->state.aptr.accept_encoding : "",
- (data->state.referer && data->state.aptr.ref) ?
- data->state.aptr.ref : "" /* Referer: <data> */,
-#ifndef CURL_DISABLE_PROXY
- (conn->bits.httpproxy &&
- !conn->bits.tunnel_proxy &&
- !Curl_checkheaders(data, STRCONST("Proxy-Connection")) &&
- !Curl_checkProxyheaders(data, conn,
- STRCONST("Proxy-Connection"))) ?
- "Proxy-Connection: Keep-Alive\r\n":"",
-#else
- "",
-#endif
- te,
- altused ? altused : ""
- );
+ /* Add collecting of headers written to client. For a new connection,
+ * we might have done that already, but reuse
+ * or multiplex needs it here as well. */
+ result = Curl_headers_init(data);
+ if(result)
+ goto out;
- /* clear userpwd and proxyuserpwd to avoid reusing old credentials
- * from reused connections */
- Curl_safefree(data->state.aptr.userpwd);
-#ifndef CURL_DISABLE_PROXY
- Curl_safefree(data->state.aptr.proxyuserpwd);
-#endif
- free(altused);
+ data->state.http_hd_te = FALSE;
+ data->state.http_hd_upgrade = FALSE;
+ data->state.http_hd_h2_settings = FALSE;
- if(result) {
- curlx_dyn_free(&req);
- goto fail;
- }
+ /* what kind of request do we need to send? */
+ Curl_http_method(data, &method, &httpreq);
- if(!Curl_conn_is_ssl(conn, FIRSTSOCKET) && (httpversion < 20) &&
- (data->state.http_neg.wanted & CURL_HTTP_V2x) &&
- data->state.http_neg.h2_upgrade) {
- /* append HTTP2 upgrade magic stuff to the HTTP request if it is not done
- over SSL */
- result = Curl_http2_request_upgrade(&req, data);
- if(result) {
- curlx_dyn_free(&req);
- return result;
+ /* select host to send */
+ result = http_set_aptr_host(data);
+ if(!result) {
+ /* setup the authentication headers, how that method and host are known */
+ char *pq = NULL;
+ if(data->state.up.query) {
+ pq = aprintf("%s?%s", data->state.up.path, data->state.up.query);
+ if(!pq)
+ return CURLE_OUT_OF_MEMORY;
}
+ result = Curl_http_output_auth(data, data->conn, method, httpreq,
+ (pq ? pq : data->state.up.path), FALSE);
+ free(pq);
}
+ if(result)
+ goto out;
- result = http_cookies(data, conn, &req);
-#ifndef CURL_DISABLE_WEBSOCKETS
- if(!result && conn->handler->protocol&(CURLPROTO_WS|CURLPROTO_WSS))
- result = Curl_ws_request(data, &req);
-#endif
+ result = http_useragent(data);
+ if(result)
+ goto out;
+
+ /* Setup input reader, resume information and ranges */
+ result = set_reader(data, httpreq);
if(!result)
- result = Curl_add_timecondition(data, &req);
+ result = http_resume(data, httpreq);
if(!result)
- result = Curl_add_custom_headers(data, FALSE, httpversion, &req);
+ result = http_range(data, httpreq);
+ if(result)
+ goto out;
- if(!result) {
- /* req_send takes ownership of the 'req' memory on success */
- result = http_req_complete(data, &req, httpversion, httpreq);
- if(!result)
- result = Curl_req_send(data, &req, httpversion);
+ httpversion = http_request_version(data);
+ /* Add request line and all headers to `req` */
+ for(hd_id = 0; hd_id <= H1_HD_LAST; ++hd_id) {
+ result = http_add_hd(data, &req, (http_hd_t)hd_id,
+ httpversion, method, httpreq);
+ if(result)
+ goto out;
}
- curlx_dyn_free(&req);
- if(result)
- goto fail;
+
+ /* setup variables for the upcoming transfer and send */
+ Curl_xfer_setup_sendrecv(data, FIRSTSOCKET, -1);
+ result = Curl_req_send(data, &req, httpversion);
if((httpversion >= 20) && data->req.upload_chunky)
/* upload_chunky was set above to set up the request in a chunky fashion,
but is disabled here again to avoid that the chunked encoded version is
actually used when sending the request body over h2 */
data->req.upload_chunky = FALSE;
-fail:
+
+out:
if(CURLE_TOO_LARGE == result)
failf(data, "HTTP request too large");
+
+ /* clear userpwd and proxyuserpwd to avoid reusing old credentials
+ * from reused connections */
+ Curl_safefree(data->state.aptr.userpwd);
+#ifndef CURL_DISABLE_PROXY
+ Curl_safefree(data->state.aptr.proxyuserpwd);
+#endif
+ curlx_dyn_free(&req);
return result;
}
@@ -3171,7 +3196,8 @@ static CURLcode http_header_l(struct Curl_easy *data,
(data->set.timecondition || data->set.get_filetime)) ?
HD_VAL(hd, hdlen, "Last-Modified:") : NULL;
if(v) {
- k->timeofdoc = Curl_getdate_capped(v);
+ if(Curl_getdate_capped(v, &k->timeofdoc))
+ k->timeofdoc = 0;
if(data->set.get_filetime)
data->info.filetime = k->timeofdoc;
return CURLE_OK;
@@ -3286,14 +3312,12 @@ static CURLcode http_header_r(struct Curl_easy *data,
if(v) {
/* Retry-After = HTTP-date / delay-seconds */
curl_off_t retry_after = 0; /* zero for unknown or "now" */
- time_t date;
+ time_t date = 0;
curlx_str_passblanks(&v);
/* try it as a date first, because a date can otherwise start with and
get treated as a number */
- date = Curl_getdate_capped(v);
-
- if((time_t)-1 != date) {
+ if(!Curl_getdate_capped(v, &date)) {
time_t current = time(NULL);
if(date >= current)
/* convert date to number of seconds into the future */
@@ -3334,14 +3358,8 @@ static CURLcode http_header_s(struct Curl_easy *data,
* real peer hostname. */
const char *host = data->state.aptr.cookiehost ?
data->state.aptr.cookiehost : conn->host.name;
- const bool secure_context =
- conn->handler->protocol&(CURLPROTO_HTTPS|CURLPROTO_WSS) ||
- curl_strequal("localhost", host) ||
- !strcmp(host, "127.0.0.1") ||
- !strcmp(host, "::1");
-
- Curl_share_lock(data, CURL_LOCK_DATA_COOKIE,
- CURL_LOCK_ACCESS_SINGLE);
+ const bool secure_context = Curl_secure_context(conn, host);
+ Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
Curl_cookie_add(data, data->cookies, TRUE, FALSE, v, host,
data->state.up.path, secure_context);
Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
@@ -4836,8 +4854,7 @@ static const struct Curl_crtype cr_exp100 = {
Curl_creader_def_needs_rewind,
Curl_creader_def_total_length,
Curl_creader_def_resume_from,
- Curl_creader_def_rewind,
- Curl_creader_def_unpause,
+ Curl_creader_def_cntrl,
Curl_creader_def_is_paused,
cr_exp100_done,
sizeof(struct cr_exp100_ctx)
diff --git a/lib/http.h b/lib/http.h
index 50279aedf..67ef17f5b 100644
--- a/lib/http.h
+++ b/lib/http.h
@@ -106,7 +106,7 @@ CURLcode Curl_add_custom_headers(struct Curl_easy *data, bool is_connect,
CURLcode Curl_dynhds_add_custom(struct Curl_easy *data, bool is_connect,
struct dynhds *hds);
-void Curl_http_method(struct Curl_easy *data, struct connectdata *conn,
+void Curl_http_method(struct Curl_easy *data,
const char **method, Curl_HttpReq *);
/* protocol-specific functions set up to be called by the main engine */
diff --git a/lib/http2.c b/lib/http2.c
index e550abc8f..e2cdc9181 100644
--- a/lib/http2.c
+++ b/lib/http2.c
@@ -799,7 +799,7 @@ static ssize_t send_callback(nghttp2_session *h2,
ctx->nw_out_blocked = 1;
return NGHTTP2_ERR_WOULDBLOCK;
}
- return (nwritten > SSIZE_T_MAX) ?
+ return (nwritten > SSIZE_MAX) ?
NGHTTP2_ERR_CALLBACK_FAILURE : (ssize_t)nwritten;
}
@@ -1820,8 +1820,9 @@ CURLcode Curl_http2_request_upgrade(struct dynbuf *req,
return result;
}
+ data->state.http_hd_upgrade = TRUE;
+ data->state.http_hd_h2_settings = TRUE;
result = curlx_dyn_addf(req,
- "Connection: Upgrade, HTTP2-Settings\r\n"
"Upgrade: %s\r\n"
"HTTP2-Settings: %s\r\n",
NGHTTP2_CLEARTEXT_PROTO_VERSION_ID, base64);
diff --git a/lib/http_aws_sigv4.c b/lib/http_aws_sigv4.c
index 850b7ae11..c62db499c 100644
--- a/lib/http_aws_sigv4.c
+++ b/lib/http_aws_sigv4.c
@@ -775,7 +775,7 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data)
}
}
- Curl_http_method(data, conn, &method, &httpreq);
+ Curl_http_method(data, &method, &httpreq);
payload_hash =
parse_content_sha_hdr(data, curlx_str(&provider1),
diff --git a/lib/http_chunks.c b/lib/http_chunks.c
index f014a256a..f735a820c 100644
--- a/lib/http_chunks.c
+++ b/lib/http_chunks.c
@@ -656,8 +656,7 @@ const struct Curl_crtype Curl_httpchunk_encoder = {
Curl_creader_def_needs_rewind,
cr_chunked_total_length,
Curl_creader_def_resume_from,
- Curl_creader_def_rewind,
- Curl_creader_def_unpause,
+ Curl_creader_def_cntrl,
Curl_creader_def_is_paused,
Curl_creader_def_done,
sizeof(struct chunked_reader)
diff --git a/lib/http_proxy.c b/lib/http_proxy.c
index df51484eb..2d742856c 100644
--- a/lib/http_proxy.c
+++ b/lib/http_proxy.c
@@ -162,11 +162,6 @@ static CURLcode dynhds_add_custom(struct Curl_easy *data,
we will force length zero then */
hd_name_eq(name, namelen, STRCONST("Content-Length:")))
;
- else if(data->state.aptr.te &&
- /* when asking for Transfer-Encoding, do not pass on a custom
- Connection: */
- hd_name_eq(name, namelen, STRCONST("Connection:")))
- ;
else if((httpversion >= 20) &&
hd_name_eq(name, namelen, STRCONST("Transfer-Encoding:")))
/* HTTP/2 and HTTP/3 do not support chunked requests */
diff --git a/lib/md4.c b/lib/md4.c
index ab40c41b2..b9c98d6c4 100644
--- a/lib/md4.c
+++ b/lib/md4.c
@@ -34,7 +34,7 @@
#ifdef USE_OPENSSL
#include <openssl/opensslv.h>
-#if (OPENSSL_VERSION_NUMBER >= 0x30000000L) && !defined(USE_AMISSL)
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined(USE_AMISSL)
/* OpenSSL 3.0.0 marks the MD4 functions as deprecated */
#define OPENSSL_NO_MD4
#else
diff --git a/lib/mime.c b/lib/mime.c
index 3d4eef767..f480bc704 100644
--- a/lib/mime.c
+++ b/lib/mime.c
@@ -2150,22 +2150,27 @@ static CURLcode cr_mime_resume_from(struct Curl_easy *data,
return CURLE_OK;
}
-static CURLcode cr_mime_rewind(struct Curl_easy *data,
- struct Curl_creader *reader)
+static CURLcode cr_mime_cntrl(struct Curl_easy *data,
+ struct Curl_creader *reader,
+ Curl_creader_cntrl opcode)
{
struct cr_mime_ctx *ctx = reader->ctx;
- CURLcode result = mime_rewind(ctx->part);
- if(result)
- failf(data, "Cannot rewind mime/post data");
- return result;
-}
-
-static CURLcode cr_mime_unpause(struct Curl_easy *data,
- struct Curl_creader *reader)
-{
- struct cr_mime_ctx *ctx = reader->ctx;
- (void)data;
- mime_unpause(ctx->part);
+ switch(opcode) {
+ case CURL_CRCNTRL_REWIND: {
+ CURLcode result = mime_rewind(ctx->part);
+ if(result)
+ failf(data, "Cannot rewind mime/post data");
+ return result;
+ }
+ case CURL_CRCNTRL_UNPAUSE:
+ mime_unpause(ctx->part);
+ break;
+ case CURL_CRCNTRL_CLEAR_EOS:
+ ctx->seen_eos = FALSE;
+ break;
+ default:
+ break;
+ }
return CURLE_OK;
}
@@ -2185,8 +2190,7 @@ static const struct Curl_crtype cr_mime = {
cr_mime_needs_rewind,
cr_mime_total_length,
cr_mime_resume_from,
- cr_mime_rewind,
- cr_mime_unpause,
+ cr_mime_cntrl,
cr_mime_is_paused,
Curl_creader_def_done,
sizeof(struct cr_mime_ctx)
diff --git a/lib/parsedate.c b/lib/parsedate.c
index eedab7c41..b9429db7f 100644
--- a/lib/parsedate.c
+++ b/lib/parsedate.c
@@ -589,26 +589,12 @@ time_t curl_getdate(const char *p, const time_t *now)
/* Curl_getdate_capped() differs from curl_getdate() in that this will return
TIME_T_MAX in case the parsed time value was too big, instead of an
- error. */
+ error. Returns non-zero on error. */
-time_t Curl_getdate_capped(const char *p)
+int Curl_getdate_capped(const char *p, time_t *tp)
{
- time_t parsed = -1;
- int rc = parsedate(p, &parsed);
-
- switch(rc) {
- case PARSEDATE_OK:
- if(parsed == (time_t)-1)
- /* avoid returning -1 for a working scenario */
- parsed++;
- return parsed;
- case PARSEDATE_LATER:
- /* this returns the maximum time value */
- return parsed;
- default:
- return -1; /* everything else is fail */
- }
- /* UNREACHABLE */
+ int rc = parsedate(p, tp);
+ return (rc == PARSEDATE_FAIL);
}
/*
diff --git a/lib/parsedate.h b/lib/parsedate.h
index 84c37f167..e5efb53f6 100644
--- a/lib/parsedate.h
+++ b/lib/parsedate.h
@@ -29,10 +29,10 @@ extern const char * const Curl_month[12];
CURLcode Curl_gmtime(time_t intime, struct tm *store);
-/* Curl_getdate_capped() differs from curl_getdate() in that this will return
+/* Curl_getdate_capped() differs from curl_getdate() in that this returns
TIME_T_MAX in case the parsed time value was too big, instead of an
error. */
-time_t Curl_getdate_capped(const char *p);
+int Curl_getdate_capped(const char *p, time_t *store);
#endif /* HEADER_CURL_PARSEDATE_H */
diff --git a/lib/request.c b/lib/request.c
index 733e3e929..8751ada55 100644
--- a/lib/request.c
+++ b/lib/request.c
@@ -308,7 +308,7 @@ static CURLcode req_flush(struct Curl_easy *data)
}
if(data->req.eos_read && !data->req.eos_sent) {
- char tmp;
+ char tmp = 0;
size_t nwritten;
result = xfer_send(data, &tmp, 0, 0, &nwritten);
if(result)
diff --git a/lib/sendf.c b/lib/sendf.c
index 43b30ecc7..551bb5ca8 100644
--- a/lib/sendf.c
+++ b/lib/sendf.c
@@ -151,7 +151,7 @@ CURLcode Curl_client_start(struct Curl_easy *data)
CURL_TRC_READ(data, "client start, rewind readers");
while(r) {
- result = r->crt->rewind(data, r);
+ result = r->crt->cntrl(data, r, CURL_CRCNTRL_REWIND);
if(result) {
failf(data, "rewind of client reader '%s' failed: %d",
r->crt->name, result);
@@ -218,15 +218,15 @@ static size_t get_max_body_write_len(struct Curl_easy *data, curl_off_t limit)
return 0;
}
#if SIZEOF_CURL_OFF_T > SIZEOF_SIZE_T
- else if(remain_diff > SSIZE_T_MAX) {
- return SIZE_T_MAX;
+ else if(remain_diff > SSIZE_MAX) {
+ return SIZE_MAX;
}
#endif
else {
return (size_t)remain_diff;
}
}
- return SIZE_T_MAX;
+ return SIZE_MAX;
}
struct cw_download_ctx {
@@ -543,6 +543,15 @@ CURLcode Curl_creader_read(struct Curl_easy *data,
return reader->crt->do_read(data, reader, buf, blen, nread, eos);
}
+void Curl_creader_clear_eos(struct Curl_easy *data,
+ struct Curl_creader *reader)
+{
+ while(reader) {
+ (void)reader->crt->cntrl(data, reader, CURL_CRCNTRL_CLEAR_EOS);
+ reader = reader->next;
+ }
+}
+
CURLcode Curl_creader_def_init(struct Curl_easy *data,
struct Curl_creader *reader)
{
@@ -598,19 +607,13 @@ CURLcode Curl_creader_def_resume_from(struct Curl_easy *data,
return CURLE_READ_ERROR;
}
-CURLcode Curl_creader_def_rewind(struct Curl_easy *data,
- struct Curl_creader *reader)
-{
- (void)data;
- (void)reader;
- return CURLE_OK;
-}
-
-CURLcode Curl_creader_def_unpause(struct Curl_easy *data,
- struct Curl_creader *reader)
+CURLcode Curl_creader_def_cntrl(struct Curl_easy *data,
+ struct Curl_creader *reader,
+ Curl_creader_cntrl opcode)
{
(void)data;
(void)reader;
+ (void)opcode;
return CURLE_OK;
}
@@ -891,12 +894,24 @@ static CURLcode cr_in_rewind(struct Curl_easy *data,
return CURLE_OK;
}
-static CURLcode cr_in_unpause(struct Curl_easy *data,
- struct Curl_creader *reader)
+static CURLcode cr_in_cntrl(struct Curl_easy *data,
+ struct Curl_creader *reader,
+ Curl_creader_cntrl opcode)
{
struct cr_in_ctx *ctx = reader->ctx;
- (void)data;
- ctx->is_paused = FALSE;
+
+ switch(opcode) {
+ case CURL_CRCNTRL_REWIND:
+ return cr_in_rewind(data, reader);
+ case CURL_CRCNTRL_UNPAUSE:
+ ctx->is_paused = FALSE;
+ break;
+ case CURL_CRCNTRL_CLEAR_EOS:
+ ctx->seen_eos = FALSE;
+ break;
+ default:
+ break;
+ }
return CURLE_OK;
}
@@ -916,8 +931,7 @@ static const struct Curl_crtype cr_in = {
cr_in_needs_rewind,
cr_in_total_length,
cr_in_resume_from,
- cr_in_rewind,
- cr_in_unpause,
+ cr_in_cntrl,
cr_in_is_paused,
Curl_creader_def_done,
sizeof(struct cr_in_ctx)
@@ -1077,8 +1091,7 @@ static const struct Curl_crtype cr_lc = {
Curl_creader_def_needs_rewind,
cr_lc_total_length,
Curl_creader_def_resume_from,
- Curl_creader_def_rewind,
- Curl_creader_def_unpause,
+ Curl_creader_def_cntrl,
Curl_creader_def_is_paused,
Curl_creader_def_done,
sizeof(struct cr_lc_ctx)
@@ -1251,8 +1264,7 @@ static const struct Curl_crtype cr_null = {
Curl_creader_def_needs_rewind,
cr_null_total_length,
Curl_creader_def_resume_from,
- Curl_creader_def_rewind,
- Curl_creader_def_unpause,
+ Curl_creader_def_cntrl,
Curl_creader_def_is_paused,
Curl_creader_def_done,
sizeof(struct Curl_creader)
@@ -1312,12 +1324,19 @@ static bool cr_buf_needs_rewind(struct Curl_easy *data,
return ctx->index > 0;
}
-static CURLcode cr_buf_rewind(struct Curl_easy *data,
- struct Curl_creader *reader)
+static CURLcode cr_buf_cntrl(struct Curl_easy *data,
+ struct Curl_creader *reader,
+ Curl_creader_cntrl opcode)
{
struct cr_buf_ctx *ctx = reader->ctx;
(void)data;
- ctx->index = 0;
+ switch(opcode) {
+ case CURL_CRCNTRL_REWIND:
+ ctx->index = 0;
+ break;
+ default:
+ break;
+ }
return CURLE_OK;
}
@@ -1360,8 +1379,7 @@ static const struct Curl_crtype cr_buf = {
cr_buf_needs_rewind,
cr_buf_total_length,
cr_buf_resume_from,
- cr_buf_rewind,
- Curl_creader_def_unpause,
+ cr_buf_cntrl,
Curl_creader_def_is_paused,
Curl_creader_def_done,
sizeof(struct cr_buf_ctx)
@@ -1417,7 +1435,7 @@ CURLcode Curl_creader_unpause(struct Curl_easy *data)
CURLcode result = CURLE_OK;
while(reader) {
- result = reader->crt->unpause(data, reader);
+ result = reader->crt->cntrl(data, reader, CURL_CRCNTRL_UNPAUSE);
if(result)
break;
reader = reader->next;
diff --git a/lib/sendf.h b/lib/sendf.h
index e5cc600bf..686744390 100644
--- a/lib/sendf.h
+++ b/lib/sendf.h
@@ -50,6 +50,7 @@
#define CLIENTWRITE_1XX (1<<5) /* a 1xx response related HEADER */
#define CLIENTWRITE_TRAILER (1<<6) /* a trailer HEADER */
#define CLIENTWRITE_EOS (1<<7) /* End Of transfer download Stream */
+#define CLIENTWRITE_0LEN (1<<8) /* write even 0-length buffers */
/**
* Write `len` bytes at `prt` to the client. `type` indicates what
@@ -202,6 +203,11 @@ void Curl_cwriter_def_close(struct Curl_easy *data,
struct Curl_cwriter *writer);
+typedef enum {
+ CURL_CRCNTRL_REWIND,
+ CURL_CRCNTRL_UNPAUSE,
+ CURL_CRCNTRL_CLEAR_EOS
+} Curl_creader_cntrl;
/* Client Reader Type, provides the implementation */
struct Curl_crtype {
@@ -215,8 +221,8 @@ struct Curl_crtype {
struct Curl_creader *reader);
CURLcode (*resume_from)(struct Curl_easy *data,
struct Curl_creader *reader, curl_off_t offset);
- CURLcode (*rewind)(struct Curl_easy *data, struct Curl_creader *reader);
- CURLcode (*unpause)(struct Curl_easy *data, struct Curl_creader *reader);
+ CURLcode (*cntrl)(struct Curl_easy *data, struct Curl_creader *reader,
+ Curl_creader_cntrl opcode);
bool (*is_paused)(struct Curl_easy *data, struct Curl_creader *reader);
void (*done)(struct Curl_easy *data,
struct Curl_creader *reader, int premature);
@@ -264,10 +270,9 @@ curl_off_t Curl_creader_def_total_length(struct Curl_easy *data,
CURLcode Curl_creader_def_resume_from(struct Curl_easy *data,
struct Curl_creader *reader,
curl_off_t offset);
-CURLcode Curl_creader_def_rewind(struct Curl_easy *data,
- struct Curl_creader *reader);
-CURLcode Curl_creader_def_unpause(struct Curl_easy *data,
- struct Curl_creader *reader);
+CURLcode Curl_creader_def_cntrl(struct Curl_easy *data,
+ struct Curl_creader *reader,
+ Curl_creader_cntrl opcode);
bool Curl_creader_def_is_paused(struct Curl_easy *data,
struct Curl_creader *reader);
void Curl_creader_def_done(struct Curl_easy *data,
@@ -281,6 +286,10 @@ CURLcode Curl_creader_read(struct Curl_easy *data,
struct Curl_creader *reader,
char *buf, size_t blen, size_t *nread, bool *eos);
+/* Tell the reader and all below that any EOS state is to be cleared */
+void Curl_creader_clear_eos(struct Curl_easy *data,
+ struct Curl_creader *reader);
+
/**
* Create a new creader instance with given type and phase. Is not
* inserted into the writer chain by this call.
diff --git a/lib/setopt.c b/lib/setopt.c
index 168417738..5adfe4dbe 100644
--- a/lib/setopt.c
+++ b/lib/setopt.c
@@ -1683,7 +1683,7 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option,
/*
* Check that requested length does not overflow the size_t type.
*/
- else if(s->postfieldsize > SIZE_T_MAX)
+ else if(s->postfieldsize > SIZE_MAX)
return CURLE_OUT_OF_MEMORY;
#endif
else {
diff --git a/lib/smtp.c b/lib/smtp.c
index 97083e41f..c52cf0dc5 100644
--- a/lib/smtp.c
+++ b/lib/smtp.c
@@ -2069,8 +2069,7 @@ static const struct Curl_crtype cr_eob = {
Curl_creader_def_needs_rewind,
cr_eob_total_length,
Curl_creader_def_resume_from,
- Curl_creader_def_rewind,
- Curl_creader_def_unpause,
+ Curl_creader_def_cntrl,
Curl_creader_def_is_paused,
Curl_creader_def_done,
sizeof(struct cr_eob_ctx)
diff --git a/lib/socks_sspi.c b/lib/socks_sspi.c
index 1e80ee18d..49210585b 100644
--- a/lib/socks_sspi.c
+++ b/lib/socks_sspi.c
@@ -90,7 +90,6 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
unsigned char socksreq[4]; /* room for GSS-API exchange header only */
const char *service = data->set.str[STRING_PROXY_SERVICE_NAME] ?
data->set.str[STRING_PROXY_SERVICE_NAME] : "rcmd";
- const size_t service_length = strlen(service);
/* GSS-API request looks like
* +----+------+-----+----------------+
@@ -101,20 +100,12 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
*/
/* prepare service name */
- if(strchr(service, '/')) {
+ if(strchr(service, '/'))
service_name = strdup(service);
- if(!service_name)
- return CURLE_OUT_OF_MEMORY;
- }
- else {
- service_name = malloc(service_length +
- strlen(conn->socks_proxy.host.name) + 2);
- if(!service_name)
- return CURLE_OUT_OF_MEMORY;
- msnprintf(service_name, service_length +
- strlen(conn->socks_proxy.host.name) + 2, "%s/%s",
- service, conn->socks_proxy.host.name);
- }
+ else
+ service_name = aprintf("%s/%s", service, conn->socks_proxy.host.name);
+ if(!service_name)
+ return CURLE_OUT_OF_MEMORY;
input_desc.cBuffers = 1;
input_desc.pBuffers = &sspi_recv_token;
@@ -132,6 +123,10 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
sspi_send_token.cbBuffer = 0;
sspi_send_token.pvBuffer = NULL;
+ sspi_w_token[0].pvBuffer =
+ sspi_w_token[1].pvBuffer =
+ sspi_w_token[2].pvBuffer = NULL;
+
wrap_desc.cBuffers = 3;
wrap_desc.pBuffers = sspi_w_token;
wrap_desc.ulVersion = SECBUFFER_VERSION;
@@ -139,21 +134,19 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
cred_handle.dwLower = 0;
cred_handle.dwUpper = 0;
- status = Curl_pSecFn->AcquireCredentialsHandle(NULL,
+ names.sUserName = NULL;
+
+ status =
+ Curl_pSecFn->AcquireCredentialsHandle(NULL,
(TCHAR *)CURL_UNCONST(TEXT("Kerberos")),
- SECPKG_CRED_OUTBOUND,
- NULL,
- NULL,
- NULL,
- NULL,
- &cred_handle,
- &expiry);
+ SECPKG_CRED_OUTBOUND,
+ NULL, NULL, NULL, NULL,
+ &cred_handle, &expiry);
if(check_sspi_err(data, status, "AcquireCredentialsHandle")) {
failf(data, "Failed to acquire credentials.");
- free(service_name);
- Curl_pSecFn->FreeCredentialsHandle(&cred_handle);
- return CURLE_COULDNT_CONNECT;
+ result = CURLE_COULDNT_CONNECT;
+ goto error;
}
(void)curlx_nonblock(sock, FALSE);
@@ -164,24 +157,24 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
TCHAR *sname;
sname = curlx_convert_UTF8_to_tchar(service_name);
- if(!sname)
- return CURLE_OUT_OF_MEMORY;
-
- status = Curl_pSecFn->InitializeSecurityContext(&cred_handle,
- context_handle,
- sname,
- ISC_REQ_MUTUAL_AUTH |
- ISC_REQ_ALLOCATE_MEMORY |
- ISC_REQ_CONFIDENTIALITY |
- ISC_REQ_REPLAY_DETECT,
- 0,
- SECURITY_NATIVE_DREP,
- &input_desc,
- 0,
- &sspi_context,
- &output_desc,
- &sspi_ret_flags,
- &expiry);
+ if(!sname) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto error;
+ }
+
+ status =
+ Curl_pSecFn->InitializeSecurityContext(&cred_handle, context_handle,
+ sname,
+ ISC_REQ_MUTUAL_AUTH |
+ ISC_REQ_ALLOCATE_MEMORY |
+ ISC_REQ_CONFIDENTIALITY |
+ ISC_REQ_REPLAY_DETECT,
+ 0, SECURITY_NATIVE_DREP,
+ &input_desc, 0,
+ &sspi_context,
+ &output_desc,
+ &sspi_ret_flags,
+ &expiry);
curlx_unicodefree(sname);
@@ -192,13 +185,9 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
}
if(check_sspi_err(data, status, "InitializeSecurityContext")) {
- free(service_name);
- Curl_pSecFn->FreeCredentialsHandle(&cred_handle);
- Curl_pSecFn->DeleteSecurityContext(&sspi_context);
- if(sspi_recv_token.pvBuffer)
- Curl_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
failf(data, "Failed to initialise security context.");
- return CURLE_COULDNT_CONNECT;
+ result = CURLE_COULDNT_CONNECT;
+ goto error;
}
if(sspi_send_token.cbBuffer) {
@@ -211,14 +200,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
&written);
if(code || (written != 4)) {
failf(data, "Failed to send SSPI authentication request.");
- free(service_name);
- if(sspi_send_token.pvBuffer)
- Curl_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
- if(sspi_recv_token.pvBuffer)
- Curl_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
- Curl_pSecFn->FreeCredentialsHandle(&cred_handle);
- Curl_pSecFn->DeleteSecurityContext(&sspi_context);
- return CURLE_COULDNT_CONNECT;
+ result = CURLE_COULDNT_CONNECT;
+ goto error;
}
code = Curl_conn_cf_send(cf->next, data,
@@ -226,16 +209,9 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
sspi_send_token.cbBuffer, FALSE, &written);
if(code || (sspi_send_token.cbBuffer != written)) {
failf(data, "Failed to send SSPI authentication token.");
- free(service_name);
- if(sspi_send_token.pvBuffer)
- Curl_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
- if(sspi_recv_token.pvBuffer)
- Curl_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
- Curl_pSecFn->FreeCredentialsHandle(&cred_handle);
- Curl_pSecFn->DeleteSecurityContext(&sspi_context);
- return CURLE_COULDNT_CONNECT;
+ result = CURLE_COULDNT_CONNECT;
+ goto error;
}
-
}
if(sspi_send_token.pvBuffer) {
@@ -266,29 +242,23 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
result = Curl_blockread_all(cf, data, (char *)socksreq, 4, &actualread);
if(result || (actualread != 4)) {
failf(data, "Failed to receive SSPI authentication response.");
- free(service_name);
- Curl_pSecFn->FreeCredentialsHandle(&cred_handle);
- Curl_pSecFn->DeleteSecurityContext(&sspi_context);
- return CURLE_COULDNT_CONNECT;
+ result = CURLE_COULDNT_CONNECT;
+ goto error;
}
/* ignore the first (VER) byte */
if(socksreq[1] == 255) { /* status / message type */
failf(data, "User was rejected by the SOCKS5 server (%u %u).",
(unsigned int)socksreq[0], (unsigned int)socksreq[1]);
- free(service_name);
- Curl_pSecFn->FreeCredentialsHandle(&cred_handle);
- Curl_pSecFn->DeleteSecurityContext(&sspi_context);
- return CURLE_COULDNT_CONNECT;
+ result = CURLE_COULDNT_CONNECT;
+ goto error;
}
if(socksreq[1] != 1) { /* status / message type */
failf(data, "Invalid SSPI authentication response type (%u %u).",
(unsigned int)socksreq[0], (unsigned int)socksreq[1]);
- free(service_name);
- Curl_pSecFn->FreeCredentialsHandle(&cred_handle);
- Curl_pSecFn->DeleteSecurityContext(&sspi_context);
- return CURLE_COULDNT_CONNECT;
+ result = CURLE_COULDNT_CONNECT;
+ goto error;
}
memcpy(&us_length, socksreq + 2, sizeof(short));
@@ -298,39 +268,32 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
sspi_recv_token.pvBuffer = malloc(us_length);
if(!sspi_recv_token.pvBuffer) {
- free(service_name);
- Curl_pSecFn->FreeCredentialsHandle(&cred_handle);
- Curl_pSecFn->DeleteSecurityContext(&sspi_context);
- return CURLE_OUT_OF_MEMORY;
+ result = CURLE_OUT_OF_MEMORY;
+ goto error;
}
result = Curl_blockread_all(cf, data, (char *)sspi_recv_token.pvBuffer,
sspi_recv_token.cbBuffer, &actualread);
if(result || (actualread != us_length)) {
failf(data, "Failed to receive SSPI authentication token.");
- free(service_name);
- if(sspi_recv_token.pvBuffer)
- Curl_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
- Curl_pSecFn->FreeCredentialsHandle(&cred_handle);
- Curl_pSecFn->DeleteSecurityContext(&sspi_context);
- return CURLE_COULDNT_CONNECT;
+ result = CURLE_COULDNT_CONNECT;
+ goto error;
}
context_handle = &sspi_context;
}
- free(service_name);
+ Curl_safefree(service_name);
/* Everything is good so far, user was authenticated! */
status = Curl_pSecFn->QueryCredentialsAttributes(&cred_handle,
- SECPKG_CRED_ATTR_NAMES,
- &names);
+ SECPKG_CRED_ATTR_NAMES,
+ &names);
Curl_pSecFn->FreeCredentialsHandle(&cred_handle);
if(check_sspi_err(data, status, "QueryCredentialAttributes")) {
- Curl_pSecFn->DeleteSecurityContext(&sspi_context);
- Curl_pSecFn->FreeContextBuffer(names.sUserName);
failf(data, "Failed to determine username.");
- return CURLE_COULDNT_CONNECT;
+ result = CURLE_COULDNT_CONNECT;
+ goto error;
}
else {
#ifndef CURL_DISABLE_VERBOSE_STRINGS
@@ -396,12 +359,12 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
}
else {
status = Curl_pSecFn->QueryContextAttributes(&sspi_context,
- SECPKG_ATTR_SIZES,
- &sspi_sizes);
+ SECPKG_ATTR_SIZES,
+ &sspi_sizes);
if(check_sspi_err(data, status, "QueryContextAttributes")) {
- Curl_pSecFn->DeleteSecurityContext(&sspi_context);
failf(data, "Failed to query security context attributes.");
- return CURLE_COULDNT_CONNECT;
+ result = CURLE_COULDNT_CONNECT;
+ goto error;
}
sspi_w_token[0].cbBuffer = sspi_sizes.cbSecurityTrailer;
@@ -409,16 +372,15 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
sspi_w_token[0].pvBuffer = malloc(sspi_sizes.cbSecurityTrailer);
if(!sspi_w_token[0].pvBuffer) {
- Curl_pSecFn->DeleteSecurityContext(&sspi_context);
- return CURLE_OUT_OF_MEMORY;
+ result = CURLE_OUT_OF_MEMORY;
+ goto error;
}
sspi_w_token[1].cbBuffer = 1;
sspi_w_token[1].pvBuffer = malloc(1);
if(!sspi_w_token[1].pvBuffer) {
- Curl_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
- Curl_pSecFn->DeleteSecurityContext(&sspi_context);
- return CURLE_OUT_OF_MEMORY;
+ result = CURLE_OUT_OF_MEMORY;
+ goto error;
}
memcpy(sspi_w_token[1].pvBuffer, &gss_enc, 1);
@@ -426,42 +388,33 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
sspi_w_token[2].cbBuffer = sspi_sizes.cbBlockSize;
sspi_w_token[2].pvBuffer = malloc(sspi_sizes.cbBlockSize);
if(!sspi_w_token[2].pvBuffer) {
- Curl_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
- Curl_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
- Curl_pSecFn->DeleteSecurityContext(&sspi_context);
- return CURLE_OUT_OF_MEMORY;
+ result = CURLE_OUT_OF_MEMORY;
+ goto error;
}
status = Curl_pSecFn->EncryptMessage(&sspi_context,
- KERB_WRAP_NO_ENCRYPT,
- &wrap_desc,
- 0);
+ KERB_WRAP_NO_ENCRYPT,
+ &wrap_desc, 0);
if(check_sspi_err(data, status, "EncryptMessage")) {
- Curl_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
- Curl_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
- Curl_pSecFn->FreeContextBuffer(sspi_w_token[2].pvBuffer);
- Curl_pSecFn->DeleteSecurityContext(&sspi_context);
failf(data, "Failed to query security context attributes.");
- return CURLE_COULDNT_CONNECT;
+ result = CURLE_COULDNT_CONNECT;
+ goto error;
}
- sspi_send_token.cbBuffer = sspi_w_token[0].cbBuffer
- + sspi_w_token[1].cbBuffer
- + sspi_w_token[2].cbBuffer;
+ sspi_send_token.cbBuffer = sspi_w_token[0].cbBuffer +
+ sspi_w_token[1].cbBuffer +
+ sspi_w_token[2].cbBuffer;
sspi_send_token.pvBuffer = malloc(sspi_send_token.cbBuffer);
if(!sspi_send_token.pvBuffer) {
- Curl_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
- Curl_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
- Curl_pSecFn->FreeContextBuffer(sspi_w_token[2].pvBuffer);
- Curl_pSecFn->DeleteSecurityContext(&sspi_context);
- return CURLE_OUT_OF_MEMORY;
+ result = CURLE_OUT_OF_MEMORY;
+ goto error;
}
memcpy(sspi_send_token.pvBuffer, sspi_w_token[0].pvBuffer,
sspi_w_token[0].cbBuffer);
memcpy((PUCHAR) sspi_send_token.pvBuffer +(int)sspi_w_token[0].cbBuffer,
sspi_w_token[1].pvBuffer, sspi_w_token[1].cbBuffer);
- memcpy((PUCHAR) sspi_send_token.pvBuffer
- + sspi_w_token[0].cbBuffer
- + sspi_w_token[1].cbBuffer,
+ memcpy((PUCHAR) sspi_send_token.pvBuffer +
+ sspi_w_token[0].cbBuffer +
+ sspi_w_token[1].cbBuffer,
sspi_w_token[2].pvBuffer, sspi_w_token[2].cbBuffer);
Curl_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
@@ -482,10 +435,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
&written);
if(code || (written != 4)) {
failf(data, "Failed to send SSPI encryption request.");
- if(sspi_send_token.pvBuffer)
- Curl_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
- Curl_pSecFn->DeleteSecurityContext(&sspi_context);
- return CURLE_COULDNT_CONNECT;
+ result = CURLE_COULDNT_CONNECT;
+ goto error;
}
if(data->set.socks5_gssapi_nec) {
@@ -494,8 +445,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
&written);
if(code || (written != 1)) {
failf(data, "Failed to send SSPI encryption type.");
- Curl_pSecFn->DeleteSecurityContext(&sspi_context);
- return CURLE_COULDNT_CONNECT;
+ result = CURLE_COULDNT_CONNECT;
+ goto error;
}
}
else {
@@ -504,10 +455,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
sspi_send_token.cbBuffer, FALSE, &written);
if(code || (sspi_send_token.cbBuffer != (size_t)written)) {
failf(data, "Failed to send SSPI encryption type.");
- if(sspi_send_token.pvBuffer)
- Curl_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
- Curl_pSecFn->DeleteSecurityContext(&sspi_context);
- return CURLE_COULDNT_CONNECT;
+ result = CURLE_COULDNT_CONNECT;
+ goto error;
}
if(sspi_send_token.pvBuffer)
Curl_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
@@ -516,23 +465,23 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
result = Curl_blockread_all(cf, data, (char *)socksreq, 4, &actualread);
if(result || (actualread != 4)) {
failf(data, "Failed to receive SSPI encryption response.");
- Curl_pSecFn->DeleteSecurityContext(&sspi_context);
- return CURLE_COULDNT_CONNECT;
+ result = CURLE_COULDNT_CONNECT;
+ goto error;
}
/* ignore the first (VER) byte */
if(socksreq[1] == 255) { /* status / message type */
failf(data, "User was rejected by the SOCKS5 server (%u %u).",
(unsigned int)socksreq[0], (unsigned int)socksreq[1]);
- Curl_pSecFn->DeleteSecurityContext(&sspi_context);
- return CURLE_COULDNT_CONNECT;
+ result = CURLE_COULDNT_CONNECT;
+ goto error;
}
if(socksreq[1] != 2) { /* status / message type */
failf(data, "Invalid SSPI encryption response type (%u %u).",
(unsigned int)socksreq[0], (unsigned int)socksreq[1]);
- Curl_pSecFn->DeleteSecurityContext(&sspi_context);
- return CURLE_COULDNT_CONNECT;
+ result = CURLE_COULDNT_CONNECT;
+ goto error;
}
memcpy(&us_length, socksreq + 2, sizeof(short));
@@ -541,8 +490,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
sspi_w_token[0].cbBuffer = us_length;
sspi_w_token[0].pvBuffer = malloc(us_length);
if(!sspi_w_token[0].pvBuffer) {
- Curl_pSecFn->DeleteSecurityContext(&sspi_context);
- return CURLE_OUT_OF_MEMORY;
+ result = CURLE_OUT_OF_MEMORY;
+ goto error;
}
result = Curl_blockread_all(cf, data, (char *)sspi_w_token[0].pvBuffer,
@@ -550,9 +499,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
if(result || (actualread != us_length)) {
failf(data, "Failed to receive SSPI encryption type.");
- Curl_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
- Curl_pSecFn->DeleteSecurityContext(&sspi_context);
- return CURLE_COULDNT_CONNECT;
+ result = CURLE_COULDNT_CONNECT;
+ goto error;
}
@@ -563,30 +511,20 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
sspi_w_token[1].cbBuffer = 0;
sspi_w_token[1].pvBuffer = NULL;
- status = Curl_pSecFn->DecryptMessage(&sspi_context,
- &wrap_desc,
- 0,
- &qop);
+ status = Curl_pSecFn->DecryptMessage(&sspi_context, &wrap_desc,
+ 0, &qop);
if(check_sspi_err(data, status, "DecryptMessage")) {
- if(sspi_w_token[0].pvBuffer)
- Curl_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
- if(sspi_w_token[1].pvBuffer)
- Curl_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
- Curl_pSecFn->DeleteSecurityContext(&sspi_context);
failf(data, "Failed to query security context attributes.");
- return CURLE_COULDNT_CONNECT;
+ result = CURLE_COULDNT_CONNECT;
+ goto error;
}
if(sspi_w_token[1].cbBuffer != 1) {
failf(data, "Invalid SSPI encryption response length (%lu).",
(unsigned long)sspi_w_token[1].cbBuffer);
- if(sspi_w_token[0].pvBuffer)
- Curl_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
- if(sspi_w_token[1].pvBuffer)
- Curl_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
- Curl_pSecFn->DeleteSecurityContext(&sspi_context);
- return CURLE_COULDNT_CONNECT;
+ result = CURLE_COULDNT_CONNECT;
+ goto error;
}
memcpy(socksreq, sspi_w_token[1].pvBuffer, sspi_w_token[1].cbBuffer);
@@ -597,9 +535,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
if(sspi_w_token[0].cbBuffer != 1) {
failf(data, "Invalid SSPI encryption response length (%lu).",
(unsigned long)sspi_w_token[0].cbBuffer);
- Curl_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
- Curl_pSecFn->DeleteSecurityContext(&sspi_context);
- return CURLE_COULDNT_CONNECT;
+ result = CURLE_COULDNT_CONNECT;
+ goto error;
}
memcpy(socksreq, sspi_w_token[0].pvBuffer, sspi_w_token[0].cbBuffer);
Curl_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
@@ -621,5 +558,22 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
}
*/
return CURLE_OK;
+error:
+ free(service_name);
+ Curl_pSecFn->FreeCredentialsHandle(&cred_handle);
+ Curl_pSecFn->DeleteSecurityContext(&sspi_context);
+ if(sspi_recv_token.pvBuffer)
+ Curl_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
+ if(sspi_send_token.pvBuffer)
+ Curl_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
+ if(names.sUserName)
+ Curl_pSecFn->FreeContextBuffer(names.sUserName);
+ if(sspi_w_token[0].pvBuffer)
+ Curl_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
+ if(sspi_w_token[1].pvBuffer)
+ Curl_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
+ if(sspi_w_token[2].pvBuffer)
+ Curl_pSecFn->FreeContextBuffer(sspi_w_token[2].pvBuffer);
+ return result;
}
#endif
diff --git a/lib/strdup.c b/lib/strdup.c
index 69c41a5e2..66fd7c60e 100644
--- a/lib/strdup.c
+++ b/lib/strdup.c
@@ -71,7 +71,7 @@ wchar_t *Curl_wcsdup(const wchar_t *src)
{
size_t length = wcslen(src);
- if(length > (SIZE_T_MAX / sizeof(wchar_t)) - 1)
+ if(length > (SIZE_MAX / sizeof(wchar_t)) - 1)
return (wchar_t *)NULL; /* integer overflow */
return (wchar_t *)Curl_memdup(src, (length + 1) * sizeof(wchar_t));
diff --git a/lib/url.c b/lib/url.c
index 20161cac4..b52267cb6 100644
--- a/lib/url.c
+++ b/lib/url.c
@@ -320,7 +320,6 @@ CURLcode Curl_close(struct Curl_easy **datap)
Curl_safefree(data->state.aptr.uagent);
Curl_safefree(data->state.aptr.userpwd);
Curl_safefree(data->state.aptr.accept_encoding);
- Curl_safefree(data->state.aptr.te);
Curl_safefree(data->state.aptr.rangeline);
Curl_safefree(data->state.aptr.ref);
Curl_safefree(data->state.aptr.host);
diff --git a/lib/urldata.h b/lib/urldata.h
index a35512cf7..cf181af64 100644
--- a/lib/urldata.h
+++ b/lib/urldata.h
@@ -1122,7 +1122,6 @@ struct UrlState {
#ifndef CURL_DISABLE_RTSP
char *rtsp_transport;
#endif
- char *te; /* TE: request header */
/* transfer credentials */
char *user;
@@ -1178,6 +1177,11 @@ struct UrlState {
internal use and the user does not have ownership of the
handle. */
BIT(http_ignorecustom); /* ignore custom method from now */
+#ifndef CURL_DISABLE_HTTP
+ BIT(http_hd_te); /* Added HTTP header TE: */
+ BIT(http_hd_upgrade); /* Added HTTP header Upgrade: */
+ BIT(http_hd_h2_settings); /* Added HTTP header H2Settings: */
+#endif
};
/*
diff --git a/lib/vauth/cleartext.c b/lib/vauth/cleartext.c
index 8fc5d41b9..ebf026fcf 100644
--- a/lib/vauth/cleartext.c
+++ b/lib/vauth/cleartext.c
@@ -74,8 +74,8 @@ CURLcode Curl_auth_create_plain_message(const char *authzid,
plen = strlen(passwd);
/* Compute binary message length. Check for overflows. */
- if((zlen > SIZE_T_MAX/4) || (clen > SIZE_T_MAX/4) ||
- (plen > (SIZE_T_MAX/2 - 2)))
+ if((zlen > SIZE_MAX/4) || (clen > SIZE_MAX/4) ||
+ (plen > (SIZE_MAX/2 - 2)))
return CURLE_OUT_OF_MEMORY;
plainlen = zlen + clen + plen + 2;
diff --git a/lib/vquic/curl_ngtcp2.c b/lib/vquic/curl_ngtcp2.c
index 575ebb902..6470f1506 100644
--- a/lib/vquic/curl_ngtcp2.c
+++ b/lib/vquic/curl_ngtcp2.c
@@ -431,9 +431,9 @@ static void quic_settings(struct cf_ngtcp2_ctx *ctx,
s->log_printf = NULL;
#endif
- (void)data;
s->initial_ts = pktx->ts;
- s->handshake_timeout = QUIC_HANDSHAKE_TIMEOUT;
+ s->handshake_timeout = (data->set.connecttimeout > 0) ?
+ data->set.connecttimeout * NGTCP2_MILLISECONDS : QUIC_HANDSHAKE_TIMEOUT;
s->max_window = 100 * ctx->max_stream_window;
s->max_stream_window = 10 * ctx->max_stream_window;
diff --git a/lib/vssh/libssh.c b/lib/vssh/libssh.c
index 8c366aafa..2898a70f0 100644
--- a/lib/vssh/libssh.c
+++ b/lib/vssh/libssh.c
@@ -1855,9 +1855,9 @@ static int myssh_in_SFTP_QUOTE_STAT(struct Curl_easy *data,
}
else if(!strncmp(cmd, "atime", 5) ||
!strncmp(cmd, "mtime", 5)) {
- time_t date = Curl_getdate_capped(sshc->quote_path1);
+ time_t date;
bool fail = FALSE;
- if(date == -1) {
+ if(Curl_getdate_capped(sshc->quote_path1, &date)) {
failf(data, "incorrect date format for %.*s", 5, cmd);
fail = TRUE;
}
diff --git a/lib/vssh/libssh2.c b/lib/vssh/libssh2.c
index ea7648239..13f374564 100644
--- a/lib/vssh/libssh2.c
+++ b/lib/vssh/libssh2.c
@@ -1390,16 +1390,16 @@ sftp_quote_stat(struct Curl_easy *data,
}
else if(!strncmp(cmd, "atime", 5) ||
!strncmp(cmd, "mtime", 5)) {
- time_t date = Curl_getdate_capped(sshc->quote_path1);
+ time_t date;
bool fail = FALSE;
- if(date == -1) {
+ if(Curl_getdate_capped(sshc->quote_path1, &date)) {
failf(data, "incorrect date format for %.*s", 5, cmd);
fail = TRUE;
}
#if SIZEOF_TIME_T > SIZEOF_LONG
if(date > 0xffffffff) {
- /* if 'long' cannot old >32-bit, this date cannot be sent */
+ /* if 'long' cannot hold >32-bit, this date cannot be sent */
failf(data, "date overflow");
fail = TRUE;
}
diff --git a/lib/vtls/gtls.c b/lib/vtls/gtls.c
index 3c572806e..20eae443d 100644
--- a/lib/vtls/gtls.c
+++ b/lib/vtls/gtls.c
@@ -426,7 +426,7 @@ CURLcode Curl_gtls_shared_creds_create(struct Curl_easy *data,
CURLcode Curl_gtls_shared_creds_up_ref(struct gtls_shared_creds *creds)
{
DEBUGASSERT(creds);
- if(creds->refcount < SIZE_T_MAX) {
+ if(creds->refcount < SIZE_MAX) {
++creds->refcount;
return CURLE_OK;
}
diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c
index 45469205c..5971d9a35 100644
--- a/lib/vtls/openssl.c
+++ b/lib/vtls/openssl.c
@@ -121,8 +121,14 @@
static void ossl_provider_cleanup(struct Curl_easy *data);
#endif
+/*
+ * AWS-LC has `SSL_CTX_set_default_read_buffer_len()?` but runs into
+ * decryption failures with large buffers. Sporadic failures in
+ * test_10_08 with h2 proxy uploads, increased frequency
+ * with CURL_DBG_SOCK_RBLOCK=50. Looks like a bug on their part.
+ */
#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
- !defined(LIBRESSL_VERSION_NUMBER) && !defined(OPENSSL_IS_BORINGSSL)
+ !defined(LIBRESSL_VERSION_NUMBER) && !defined(HAVE_BORINGSSL_LIKE)
#define HAVE_SSL_CTX_SET_DEFAULT_READ_BUFFER_LEN 1
#endif
@@ -4129,7 +4135,6 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
However using a large buffer (8 packets) actually decreases performance.
4 packets is better.
*/
-
#ifdef HAVE_SSL_CTX_SET_DEFAULT_READ_BUFFER_LEN
SSL_CTX_set_default_read_buffer_len(octx->ssl_ctx, 0x401e * 4);
#endif
diff --git a/lib/vtls/schannel.c b/lib/vtls/schannel.c
index 7fb4cb1fd..ea06d31ab 100644
--- a/lib/vtls/schannel.c
+++ b/lib/vtls/schannel.c
@@ -57,6 +57,7 @@
#include "../curlx/version_win32.h"
#include "../rand.h"
#include "../curlx/strparse.h"
+#include "../progress.h"
/* The last #include file should be: */
#include "../curl_memory.h"
@@ -65,12 +66,15 @@
/* Some verbose debug messages are wrapped by SCH_DEV() instead of DEBUGF()
* and only shown if CURL_SCHANNEL_DEV_DEBUG was defined at build time. These
* messages are extra verbose and intended for curl developers debugging
- * Schannel recv decryption.
+ * Schannel recv decryption and renegotiation.
*/
#ifdef CURL_SCHANNEL_DEV_DEBUG
#define SCH_DEV(x) x
+#define SCH_DEV_SHOWBOOL(x) \
+ infof(data, "schannel: " #x " %s", (x) ? "TRUE" : "FALSE");
#else
#define SCH_DEV(x) do { } while(0)
+#define SCH_DEV_SHOWBOOL(x) do { } while(0)
#endif
/* Offered by mingw-w64 v8+. MS SDK 7.0A+. */
@@ -1099,6 +1103,7 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
backend->recv_sspi_close_notify = FALSE;
backend->recv_connection_closed = FALSE;
backend->recv_renegotiating = FALSE;
+ backend->renegotiate_state.started = FALSE;
backend->encdata_is_incomplete = FALSE;
/* continue to second handshake step */
@@ -1246,6 +1251,7 @@ schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
if(!SOCKET_WRITABLE(Curl_conn_cf_get_socket(cf, data), 0)) {
SCH_DEV(infof(data, "schannel: handshake waiting for writeable socket"));
connssl->io_need = CURL_SSL_IO_NEED_SEND;
+ free(inbuf[0].pvBuffer);
return CURLE_OK;
}
@@ -1679,6 +1685,205 @@ static CURLcode schannel_connect(struct Curl_cfilter *cf,
return CURLE_OK;
}
+enum schannel_renegotiate_caller_t {
+ SCH_RENEG_CALLER_IS_RECV,
+ SCH_RENEG_CALLER_IS_SEND
+};
+
+/* This function renegotiates the connection due to a server request received
+ by schannel_recv. This function returns CURLE_AGAIN if the renegotiation is
+ incomplete. In that case, we remain in the renegotiation (connecting) stage
+ and future calls to schannel_recv and schannel_send must call this function
+ first to complete the renegotiation. */
+static CURLcode
+schannel_recv_renegotiate(struct Curl_cfilter *cf, struct Curl_easy *data,
+ enum schannel_renegotiate_caller_t caller)
+{
+ CURLcode result;
+ curl_socket_t sockfd;
+ const timediff_t max_renegotiate_ms = 5 * 60 * 1000; /* 5 minutes */
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct schannel_ssl_backend_data *backend =
+ (struct schannel_ssl_backend_data *)connssl->backend;
+ struct schannel_renegotiate_state *rs = &backend->renegotiate_state;
+
+ if(!backend || !backend->recv_renegotiating) {
+ failf(data, "schannel: unexpected call to schannel_recv_renegotiate");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ if(caller == SCH_RENEG_CALLER_IS_RECV)
+ SCH_DEV(infof(data, "schannel: renegotiation caller is schannel_recv"));
+ else if(caller == SCH_RENEG_CALLER_IS_SEND)
+ SCH_DEV(infof(data, "schannel: renegotiation caller is schannel_send"));
+ else {
+ failf(data, "schannel: unknown caller for schannel_recv_renegotiate");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ sockfd = Curl_conn_cf_get_socket(cf, data);
+
+ if(sockfd == CURL_SOCKET_BAD) {
+ failf(data, "schannel: renegotiation missing socket");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ if(!rs->started) { /* new renegotiation */
+ infof(data, "schannel: renegotiating SSL/TLS connection");
+ DEBUGASSERT(connssl->state == ssl_connection_complete);
+ DEBUGASSERT(connssl->connecting_state == ssl_connect_done);
+ connssl->state = ssl_connection_negotiating;
+ connssl->connecting_state = ssl_connect_2;
+ memset(rs, 0, sizeof(*rs));
+ rs->io_need = CURL_SSL_IO_NEED_SEND;
+ rs->start_time = curlx_now();
+ rs->started = TRUE;
+ }
+
+ for(;;) {
+ bool block_read, block_write, blocking, done;
+ curl_socket_t readfd, writefd;
+ timediff_t elapsed;
+
+ elapsed = curlx_timediff(curlx_now(), rs->start_time);
+ if(elapsed >= max_renegotiate_ms) {
+ failf(data, "schannel: renegotiation timeout");
+ result = CURLE_SSL_CONNECT_ERROR;
+ break;
+ }
+
+ /* the current io_need state may have been overwritten since the last time
+ this function was called. restore the io_need state needed to continue
+ the renegotiation. */
+
+ connssl->io_need = rs->io_need;
+
+ result = schannel_connect(cf, data, &done);
+
+ rs->io_need = connssl->io_need;
+
+ if(!result && !done)
+ result = CURLE_AGAIN;
+
+ if(result != CURLE_AGAIN)
+ break;
+
+ readfd = (rs->io_need & CURL_SSL_IO_NEED_RECV) ? sockfd : CURL_SOCKET_BAD;
+ writefd = (rs->io_need & CURL_SSL_IO_NEED_SEND) ? sockfd : CURL_SOCKET_BAD;
+
+ if(readfd == CURL_SOCKET_BAD && writefd == CURL_SOCKET_BAD)
+ continue;
+
+ /* connect should not have requested io read and write together */
+ DEBUGASSERT(readfd == CURL_SOCKET_BAD || writefd == CURL_SOCKET_BAD);
+
+ /* This function is partially blocking to avoid a stoppage that would
+ * occur if the user is waiting on the socket only in one direction.
+ *
+ * For example, if the user has called recv then they may not be waiting
+ * for a writeable socket and vice versa, so we block to avoid that.
+ *
+ * In practice a wait is unlikely to occur. For caller recv if handshake
+ * data needs to be sent then we block for a writeable socket that should
+ * be writeable immediately except for OS resource constraints. For caller
+ * send if handshake data needs to be received then we block for a readable
+ * socket, which could take some time, but it's more likely the user has
+ * called recv since they had called it prior (only recv can start
+ * renegotiation and probably the user is going to call it again to get
+ * more of their data before calling send).
+ */
+
+ block_read = (caller == SCH_RENEG_CALLER_IS_SEND) ? TRUE : FALSE;
+ block_write = (caller == SCH_RENEG_CALLER_IS_RECV) ? TRUE : FALSE;
+
+ blocking = (block_read && (readfd != CURL_SOCKET_BAD)) ||
+ (block_write && (writefd != CURL_SOCKET_BAD));
+
+ SCH_DEV_SHOWBOOL(block_read);
+ SCH_DEV_SHOWBOOL(block_write);
+ SCH_DEV_SHOWBOOL(blocking);
+
+ for(;;) {
+ int what;
+ timediff_t timeout, remaining;
+
+ if(Curl_pgrsUpdate(data)) {
+ result = CURLE_ABORTED_BY_CALLBACK;
+ break;
+ }
+
+ elapsed = curlx_timediff(curlx_now(), rs->start_time);
+ if(elapsed >= max_renegotiate_ms) {
+ failf(data, "schannel: renegotiation timeout");
+ result = CURLE_SSL_CONNECT_ERROR;
+ break;
+ }
+ remaining = max_renegotiate_ms - elapsed;
+
+ if(blocking) {
+ timeout = Curl_timeleft(data, NULL, FALSE);
+
+ if(timeout < 0) {
+ result = CURLE_OPERATION_TIMEDOUT;
+ break;
+ }
+
+ /* the blocking is in intervals so that the progress function can be
+ called every second */
+ if(!timeout || timeout > 1000)
+ timeout = 1000;
+
+ if(timeout > remaining)
+ timeout = remaining;
+ }
+ else
+ timeout = 0;
+
+ SCH_DEV(infof(data, "schannel: renegotiation wait until socket is"
+ "%s%s for up to %" FMT_TIMEDIFF_T " ms",
+ ((readfd != CURL_SOCKET_BAD) ? " readable" : ""),
+ ((writefd != CURL_SOCKET_BAD) ? " writeable" : ""),
+ timeout));
+
+ what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd, timeout);
+
+ if(what > 0 && (what & (CURL_CSELECT_IN | CURL_CSELECT_OUT))) {
+ SCH_DEV(infof(data, "schannel: renegotiation socket %s%s",
+ ((what & CURL_CSELECT_IN) ? "CURL_CSELECT_IN " : ""),
+ ((what & CURL_CSELECT_OUT) ? "CURL_CSELECT_OUT " : "")));
+ result = CURLE_AGAIN;
+ break;
+ }
+ else if(!what) {
+ SCH_DEV(infof(data, "schannel: renegotiation socket timeout"));
+ if(blocking)
+ continue;
+ else
+ return CURLE_AGAIN;
+ }
+
+ failf(data, "schannel: socket error during renegotiation");
+ result = CURLE_SSL_CONNECT_ERROR;
+ break;
+ }
+ if(result != CURLE_AGAIN)
+ break;
+ }
+
+ DEBUGASSERT(result != CURLE_AGAIN);
+
+ rs->started = FALSE;
+ backend->recv_renegotiating = FALSE;
+ connssl->io_need = CURL_SSL_IO_NEED_NONE;
+
+ if(result)
+ failf(data, "schannel: renegotiation failed");
+ else
+ infof(data, "schannel: SSL/TLS connection renegotiated");
+
+ return result;
+}
+
static CURLcode
schannel_send(struct Curl_cfilter *cf, struct Curl_easy *data,
const void *buf, size_t len, size_t *pnwritten)
@@ -1696,6 +1901,12 @@ schannel_send(struct Curl_cfilter *cf, struct Curl_easy *data,
DEBUGASSERT(backend);
*pnwritten = 0;
+ if(backend->recv_renegotiating) {
+ result = schannel_recv_renegotiate(cf, data, SCH_RENEG_CALLER_IS_SEND);
+ if(result)
+ return result;
+ }
+
/* check if the maximum stream sizes were queried */
if(backend->stream_sizes.cbMaximumMessage == 0) {
sspi_status = Curl_pSecFn->QueryContextAttributes(
@@ -1827,7 +2038,6 @@ schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
struct ssl_connect_data *connssl = cf->ctx;
unsigned char *reallocated_buffer;
size_t reallocated_length;
- bool done = FALSE;
SecBuffer inbuf[4];
SecBufferDesc inbuf_desc;
SECURITY_STATUS sspi_status = SEC_E_OK;
@@ -1841,6 +2051,12 @@ schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
DEBUGASSERT(backend);
*pnread = 0;
+ if(backend->recv_renegotiating) {
+ result = schannel_recv_renegotiate(cf, data, SCH_RENEG_CALLER_IS_RECV);
+ if(result)
+ return result;
+ }
+
/****************************************************************************
* Do not return or set backend->recv_unrecoverable_err unless in the
* cleanup. The pattern for return error is set *err, optional infof, goto
@@ -2033,21 +2249,13 @@ schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
goto cleanup;
}
- /* begin renegotiation */
- infof(data, "schannel: renegotiating SSL/TLS connection");
- connssl->state = ssl_connection_negotiating;
- connssl->connecting_state = ssl_connect_2;
- connssl->io_need = CURL_SSL_IO_NEED_SEND;
backend->recv_renegotiating = TRUE;
- result = schannel_connect(cf, data, &done);
- backend->recv_renegotiating = FALSE;
- if(result) {
- infof(data, "schannel: renegotiation failed");
+ result = schannel_recv_renegotiate(cf, data, SCH_RENEG_CALLER_IS_RECV);
+ if(result)
goto cleanup;
- }
+
/* now retry receiving data */
sspi_status = SEC_E_OK;
- infof(data, "schannel: SSL/TLS connection renegotiated");
continue;
}
/* check if the server closed the connection */
diff --git a/lib/vtls/schannel_int.h b/lib/vtls/schannel_int.h
index 83be77e0f..5483d12b5 100644
--- a/lib/vtls/schannel_int.h
+++ b/lib/vtls/schannel_int.h
@@ -123,6 +123,11 @@ struct schannel_ssl_backend_data {
more bytes into encdata then set this back to false. */
unsigned long req_flags, ret_flags;
CURLcode recv_unrecoverable_err; /* schannel_recv had an unrecoverable err */
+ struct schannel_renegotiate_state {
+ bool started;
+ struct curltime start_time;
+ int io_need;
+ } renegotiate_state;
BIT(recv_sspi_close_notify); /* true if connection closed by close_notify */
BIT(recv_connection_closed); /* true if connection closed, regardless how */
BIT(recv_renegotiating); /* true if recv is doing renegotiation */
diff --git a/lib/vtls/wolfssl.c b/lib/vtls/wolfssl.c
index d04396209..afbb9b821 100644
--- a/lib/vtls/wolfssl.c
+++ b/lib/vtls/wolfssl.c
@@ -938,6 +938,163 @@ wssl_legacy_CTX_set_max_proto_version(WOLFSSL_CTX* ctx, int version)
#define wolfSSL_CTX_set_max_proto_version wssl_legacy_CTX_set_max_proto_version
#endif
+static CURLcode client_certificate(struct Curl_easy *data,
+ struct ssl_config_data *ssl_config,
+ struct wssl_ctx *wctx)
+{
+ /* Load the client certificate, and private key */
+#ifndef NO_FILESYSTEM
+ if(ssl_config->primary.cert_blob || ssl_config->primary.clientcert) {
+ const char *cert_file = ssl_config->primary.clientcert;
+ const char *key_file = ssl_config->key;
+ const struct curl_blob *cert_blob = ssl_config->primary.cert_blob;
+ const struct curl_blob *key_blob = ssl_config->key_blob;
+ int file_type = wssl_do_file_type(ssl_config->cert_type);
+ int rc;
+
+ switch(file_type) {
+ case WOLFSSL_FILETYPE_PEM:
+ rc = cert_blob ?
+ wolfSSL_CTX_use_certificate_chain_buffer(wctx->ssl_ctx,
+ cert_blob->data,
+ (long)cert_blob->len) :
+ wolfSSL_CTX_use_certificate_chain_file(wctx->ssl_ctx, cert_file);
+ break;
+ case WOLFSSL_FILETYPE_ASN1:
+ rc = cert_blob ?
+ wolfSSL_CTX_use_certificate_buffer(wctx->ssl_ctx, cert_blob->data,
+ (long)cert_blob->len, file_type) :
+ wolfSSL_CTX_use_certificate_file(wctx->ssl_ctx, cert_file, file_type);
+ break;
+ default:
+ failf(data, "unknown cert type");
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ }
+ if(rc != 1) {
+ failf(data, "unable to use client certificate");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ if(!key_blob && !key_file) {
+ key_blob = cert_blob;
+ key_file = cert_file;
+ }
+ else
+ file_type = wssl_do_file_type(ssl_config->key_type);
+
+ rc = key_blob ?
+ wolfSSL_CTX_use_PrivateKey_buffer(wctx->ssl_ctx, key_blob->data,
+ (long)key_blob->len, file_type) :
+ wolfSSL_CTX_use_PrivateKey_file(wctx->ssl_ctx, key_file, file_type);
+ if(rc != 1) {
+ failf(data, "unable to set private key");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
+#else /* NO_FILESYSTEM */
+ if(ssl_config->primary.cert_blob) {
+ const struct curl_blob *cert_blob = ssl_config->primary.cert_blob;
+ const struct curl_blob *key_blob = ssl_config->key_blob;
+ int file_type = wssl_do_file_type(ssl_config->cert_type);
+ int rc;
+
+ switch(file_type) {
+ case WOLFSSL_FILETYPE_PEM:
+ rc = wolfSSL_CTX_use_certificate_chain_buffer(wctx->ssl_ctx,
+ cert_blob->data,
+ (long)cert_blob->len);
+ break;
+ case WOLFSSL_FILETYPE_ASN1:
+ rc = wolfSSL_CTX_use_certificate_buffer(wctx->ssl_ctx, cert_blob->data,
+ (long)cert_blob->len, file_type);
+ break;
+ default:
+ failf(data, "unknown cert type");
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ }
+ if(rc != 1) {
+ failf(data, "unable to use client certificate");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ if(!key_blob)
+ key_blob = cert_blob;
+ else
+ file_type = wssl_do_file_type(ssl_config->key_type);
+
+ if(wolfSSL_CTX_use_PrivateKey_buffer(wctx->ssl_ctx, key_blob->data,
+ (long)key_blob->len,
+ file_type) != 1) {
+ failf(data, "unable to set private key");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
+#endif /* !NO_FILESYSTEM */
+ return CURLE_OK;
+}
+
+static CURLcode ssl_version(struct Curl_easy *data,
+ struct ssl_primary_config *conn_config,
+ struct wssl_ctx *wctx)
+{
+ int res;
+ switch(conn_config->version) {
+ case CURL_SSLVERSION_DEFAULT:
+ case CURL_SSLVERSION_TLSv1:
+ case CURL_SSLVERSION_TLSv1_0:
+ res = wolfSSL_CTX_set_min_proto_version(wctx->ssl_ctx, TLS1_VERSION);
+ break;
+ case CURL_SSLVERSION_TLSv1_1:
+ res = wolfSSL_CTX_set_min_proto_version(wctx->ssl_ctx, TLS1_1_VERSION);
+ break;
+ case CURL_SSLVERSION_TLSv1_2:
+ res = wolfSSL_CTX_set_min_proto_version(wctx->ssl_ctx, TLS1_2_VERSION);
+ break;
+#ifdef WOLFSSL_TLS13
+ case CURL_SSLVERSION_TLSv1_3:
+ res = wolfSSL_CTX_set_min_proto_version(wctx->ssl_ctx, TLS1_3_VERSION);
+ break;
+#endif
+ default:
+ failf(data, "wolfSSL: unsupported minimum TLS version value");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ if(res != WOLFSSL_SUCCESS) {
+ failf(data, "wolfSSL: failed set the minimum TLS version");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ switch(conn_config->version_max) {
+#ifdef WOLFSSL_TLS13
+ case CURL_SSLVERSION_MAX_TLSv1_3:
+ res = wolfSSL_CTX_set_max_proto_version(wctx->ssl_ctx, TLS1_3_VERSION);
+ break;
+#endif
+ case CURL_SSLVERSION_MAX_TLSv1_2:
+ res = wolfSSL_CTX_set_max_proto_version(wctx->ssl_ctx, TLS1_2_VERSION);
+ break;
+ case CURL_SSLVERSION_MAX_TLSv1_1:
+ res = wolfSSL_CTX_set_max_proto_version(wctx->ssl_ctx, TLS1_1_VERSION);
+ break;
+ case CURL_SSLVERSION_MAX_TLSv1_0:
+ res = wolfSSL_CTX_set_max_proto_version(wctx->ssl_ctx, TLS1_VERSION);
+ break;
+ case CURL_SSLVERSION_MAX_DEFAULT:
+ case CURL_SSLVERSION_MAX_NONE:
+ res = WOLFSSL_SUCCESS;
+ break;
+ default:
+ failf(data, "wolfSSL: unsupported maximum TLS version value");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ if(res != WOLFSSL_SUCCESS) {
+ failf(data, "wolfSSL: failed set the maximum TLS version");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ return CURLE_OK;
+}
+
+
#define QUIC_CIPHERS \
"TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_" \
"POLY1305_SHA256:TLS_AES_128_CCM_SHA256"
@@ -957,7 +1114,6 @@ CURLcode Curl_wssl_ctx_init(struct wssl_ctx *wctx,
struct ssl_primary_config *conn_config;
WOLFSSL_METHOD* req_method = NULL;
struct alpn_spec alpns;
- int res;
char *curves;
#ifdef WOLFSSL_HAVE_KYBER
word16 pqkem = 0;
@@ -998,63 +1154,9 @@ CURLcode Curl_wssl_ctx_init(struct wssl_ctx *wctx,
goto out;
}
- switch(conn_config->version) {
- case CURL_SSLVERSION_DEFAULT:
- case CURL_SSLVERSION_TLSv1:
- case CURL_SSLVERSION_TLSv1_0:
- res = wolfSSL_CTX_set_min_proto_version(wctx->ssl_ctx, TLS1_VERSION);
- break;
- case CURL_SSLVERSION_TLSv1_1:
- res = wolfSSL_CTX_set_min_proto_version(wctx->ssl_ctx, TLS1_1_VERSION);
- break;
- case CURL_SSLVERSION_TLSv1_2:
- res = wolfSSL_CTX_set_min_proto_version(wctx->ssl_ctx, TLS1_2_VERSION);
- break;
-#ifdef WOLFSSL_TLS13
- case CURL_SSLVERSION_TLSv1_3:
- res = wolfSSL_CTX_set_min_proto_version(wctx->ssl_ctx, TLS1_3_VERSION);
- break;
-#endif
- default:
- failf(data, "wolfSSL: unsupported minimum TLS version value");
- result = CURLE_SSL_CONNECT_ERROR;
- goto out;
- }
- if(res != WOLFSSL_SUCCESS) {
- failf(data, "wolfSSL: failed set the minimum TLS version");
- result = CURLE_SSL_CONNECT_ERROR;
- goto out;
- }
-
- switch(conn_config->version_max) {
-#ifdef WOLFSSL_TLS13
- case CURL_SSLVERSION_MAX_TLSv1_3:
- res = wolfSSL_CTX_set_max_proto_version(wctx->ssl_ctx, TLS1_3_VERSION);
- break;
-#endif
- case CURL_SSLVERSION_MAX_TLSv1_2:
- res = wolfSSL_CTX_set_max_proto_version(wctx->ssl_ctx, TLS1_2_VERSION);
- break;
- case CURL_SSLVERSION_MAX_TLSv1_1:
- res = wolfSSL_CTX_set_max_proto_version(wctx->ssl_ctx, TLS1_1_VERSION);
- break;
- case CURL_SSLVERSION_MAX_TLSv1_0:
- res = wolfSSL_CTX_set_max_proto_version(wctx->ssl_ctx, TLS1_VERSION);
- break;
- case CURL_SSLVERSION_MAX_DEFAULT:
- case CURL_SSLVERSION_MAX_NONE:
- res = WOLFSSL_SUCCESS;
- break;
- default:
- failf(data, "wolfSSL: unsupported maximum TLS version value");
- result = CURLE_SSL_CONNECT_ERROR;
- goto out;
- }
- if(res != WOLFSSL_SUCCESS) {
- failf(data, "wolfSSL: failed set the maximum TLS version");
- result = CURLE_SSL_CONNECT_ERROR;
+ result = ssl_version(data, conn_config, wctx);
+ if(result)
goto out;
- }
#ifndef WOLFSSL_TLS13
{
@@ -1129,100 +1231,9 @@ CURLcode Curl_wssl_ctx_init(struct wssl_ctx *wctx,
}
}
- /* Load the client certificate, and private key */
-#ifndef NO_FILESYSTEM
- if(ssl_config->primary.cert_blob || ssl_config->primary.clientcert) {
- const char *cert_file = ssl_config->primary.clientcert;
- const char *key_file = ssl_config->key;
- const struct curl_blob *cert_blob = ssl_config->primary.cert_blob;
- const struct curl_blob *key_blob = ssl_config->key_blob;
- int file_type = wssl_do_file_type(ssl_config->cert_type);
- int rc;
-
- switch(file_type) {
- case WOLFSSL_FILETYPE_PEM:
- rc = cert_blob ?
- wolfSSL_CTX_use_certificate_chain_buffer(wctx->ssl_ctx,
- cert_blob->data,
- (long)cert_blob->len) :
- wolfSSL_CTX_use_certificate_chain_file(wctx->ssl_ctx, cert_file);
- break;
- case WOLFSSL_FILETYPE_ASN1:
- rc = cert_blob ?
- wolfSSL_CTX_use_certificate_buffer(wctx->ssl_ctx, cert_blob->data,
- (long)cert_blob->len, file_type) :
- wolfSSL_CTX_use_certificate_file(wctx->ssl_ctx, cert_file, file_type);
- break;
- default:
- failf(data, "unknown cert type");
- result = CURLE_BAD_FUNCTION_ARGUMENT;
- goto out;
- }
- if(rc != 1) {
- failf(data, "unable to use client certificate");
- result = CURLE_SSL_CONNECT_ERROR;
- goto out;
- }
-
- if(!key_blob && !key_file) {
- key_blob = cert_blob;
- key_file = cert_file;
- }
- else
- file_type = wssl_do_file_type(ssl_config->key_type);
-
- rc = key_blob ?
- wolfSSL_CTX_use_PrivateKey_buffer(wctx->ssl_ctx, key_blob->data,
- (long)key_blob->len, file_type) :
- wolfSSL_CTX_use_PrivateKey_file(wctx->ssl_ctx, key_file, file_type);
- if(rc != 1) {
- failf(data, "unable to set private key");
- result = CURLE_SSL_CONNECT_ERROR;
- goto out;
- }
- }
-#else /* NO_FILESYSTEM */
- if(ssl_config->primary.cert_blob) {
- const struct curl_blob *cert_blob = ssl_config->primary.cert_blob;
- const struct curl_blob *key_blob = ssl_config->key_blob;
- int file_type = wssl_do_file_type(ssl_config->cert_type);
- int rc;
-
- switch(file_type) {
- case WOLFSSL_FILETYPE_PEM:
- rc = wolfSSL_CTX_use_certificate_chain_buffer(wctx->ssl_ctx,
- cert_blob->data,
- (long)cert_blob->len);
- break;
- case WOLFSSL_FILETYPE_ASN1:
- rc = wolfSSL_CTX_use_certificate_buffer(wctx->ssl_ctx, cert_blob->data,
- (long)cert_blob->len, file_type);
- break;
- default:
- failf(data, "unknown cert type");
- result = CURLE_BAD_FUNCTION_ARGUMENT;
- goto out;
- }
- if(rc != 1) {
- failf(data, "unable to use client certificate");
- result = CURLE_SSL_CONNECT_ERROR;
- goto out;
- }
-
- if(!key_blob)
- key_blob = cert_blob;
- else
- file_type = wssl_do_file_type(ssl_config->key_type);
-
- if(wolfSSL_CTX_use_PrivateKey_buffer(wctx->ssl_ctx, key_blob->data,
- (long)key_blob->len,
- file_type) != 1) {
- failf(data, "unable to set private key");
- result = CURLE_SSL_CONNECT_ERROR;
- goto out;
- }
- }
-#endif /* !NO_FILESYSTEM */
+ result = client_certificate(data, ssl_config, wctx);
+ if(result)
+ goto out;
/* SSL always tries to verify the peer, this only says whether it should
* fail to connect if the verification fails, or if it should continue
diff --git a/lib/ws.c b/lib/ws.c
index 82b8a2c22..6070cd6d0 100644
--- a/lib/ws.c
+++ b/lib/ws.c
@@ -474,7 +474,7 @@ static CURLcode ws_dec_read_head(struct ws_decoder *dec,
static CURLcode ws_dec_pass_payload(struct ws_decoder *dec,
struct Curl_easy *data,
struct bufq *inraw,
- ws_write_payload *write_payload,
+ ws_write_payload *write_cb,
void *write_ctx)
{
const unsigned char *inbuf;
@@ -487,9 +487,9 @@ static CURLcode ws_dec_pass_payload(struct ws_decoder *dec,
while(remain && Curl_bufq_peek(inraw, &inbuf, &inlen)) {
if((curl_off_t)inlen > remain)
inlen = (size_t)remain;
- nwritten = write_payload(inbuf, inlen, dec->frame_age, dec->frame_flags,
- dec->payload_offset, dec->payload_len,
- write_ctx, &result);
+ nwritten = write_cb(inbuf, inlen, dec->frame_age, dec->frame_flags,
+ dec->payload_offset, dec->payload_len,
+ write_ctx, &result);
if(nwritten < 0)
return result;
Curl_bufq_skip(inraw, (size_t)nwritten);
@@ -505,7 +505,7 @@ static CURLcode ws_dec_pass_payload(struct ws_decoder *dec,
static CURLcode ws_dec_pass(struct ws_decoder *dec,
struct Curl_easy *data,
struct bufq *inraw,
- ws_write_payload *write_payload,
+ ws_write_payload *write_cb,
void *write_ctx)
{
CURLcode result;
@@ -535,8 +535,8 @@ static CURLcode ws_dec_pass(struct ws_decoder *dec,
ssize_t nwritten;
const unsigned char tmp = '\0';
/* special case of a 0 length frame, need to write once */
- nwritten = write_payload(&tmp, 0, dec->frame_age, dec->frame_flags,
- 0, 0, write_ctx, &result);
+ nwritten = write_cb(&tmp, 0, dec->frame_age, dec->frame_flags,
+ 0, 0, write_ctx, &result);
if(nwritten < 0)
return result;
dec->state = WS_DEC_INIT;
@@ -544,7 +544,7 @@ static CURLcode ws_dec_pass(struct ws_decoder *dec,
}
FALLTHROUGH();
case WS_DEC_PAYLOAD:
- result = ws_dec_pass_payload(dec, data, inraw, write_payload, write_ctx);
+ result = ws_dec_pass_payload(dec, data, inraw, write_cb, write_ctx);
ws_dec_info(dec, data, "passing");
if(result)
return result;
@@ -631,7 +631,8 @@ static ssize_t ws_cw_dec_next(const unsigned char *buf, size_t buflen,
update_meta(ws, frame_age, frame_flags, payload_offset,
payload_len, buflen);
- *err = Curl_cwriter_write(data, ctx->next_writer, ctx->cw_type,
+ *err = Curl_cwriter_write(data, ctx->next_writer,
+ (ctx->cw_type | CLIENTWRITE_0LEN),
(const char *)buf, buflen);
if(*err)
return -1;
@@ -943,7 +944,12 @@ static CURLcode cr_ws_read(struct Curl_easy *data,
return result;
ctx->read_eos = eos;
- if(!nread) {
+ if(!Curl_bufq_is_empty(&ws->sendbuf)) {
+ /* client_read started a new frame, we disregard any eos reported */
+ ctx->read_eos = FALSE;
+ Curl_creader_clear_eos(data, reader->next);
+ }
+ else if(!nread) {
/* nothing to convert, return this right away */
if(ctx->read_eos)
ctx->eos = TRUE;
@@ -952,7 +958,7 @@ static CURLcode cr_ws_read(struct Curl_easy *data,
goto out;
}
- if(!ws->enc.payload_remain) {
+ if(!ws->enc.payload_remain && Curl_bufq_is_empty(&ws->sendbuf)) {
/* encode the data as a new BINARY frame */
result = ws_enc_write_head(data, &ws->enc, CURLWS_BINARY, nread,
&ws->sendbuf);
@@ -990,8 +996,7 @@ static const struct Curl_crtype ws_cr_encode = {
Curl_creader_def_needs_rewind,
Curl_creader_def_total_length,
Curl_creader_def_resume_from,
- Curl_creader_def_rewind,
- Curl_creader_def_unpause,
+ Curl_creader_def_cntrl,
Curl_creader_def_is_paused,
Curl_creader_def_done,
sizeof(struct cr_ws_ctx)
@@ -1019,11 +1024,6 @@ CURLcode Curl_ws_request(struct Curl_easy *data, struct dynbuf *req)
"Upgrade", "websocket"
},
{
- /* The request MUST contain a |Connection| header field whose value
- MUST include the "Upgrade" token. */
- "Connection", "Upgrade",
- },
- {
/* The request MUST include a header field with the name
|Sec-WebSocket-Version|. The value of this header field MUST be
13. */
@@ -1038,7 +1038,7 @@ CURLcode Curl_ws_request(struct Curl_easy *data, struct dynbuf *req)
"Sec-WebSocket-Key", NULL,
}
};
- heads[3].val = &keyval[0];
+ heads[2].val = &keyval[0];
/* 16 bytes random */
result = Curl_rand(data, (unsigned char *)rand, sizeof(rand));
@@ -1060,6 +1060,7 @@ CURLcode Curl_ws_request(struct Curl_easy *data, struct dynbuf *req)
heads[i].val);
}
}
+ data->state.http_hd_upgrade = TRUE;
k->upgr101 = UPGR101_WS;
return result;
}
@@ -1732,7 +1733,7 @@ CURL_EXTERN CURLcode curl_ws_start_frame(CURL *d,
return CURLE_FAILED_INIT;
}
- CURL_TRC_WS(data, "curl_start_frame(flags=%x, frame_len=%" FMT_OFF_T,
+ CURL_TRC_WS(data, "curl_ws_start_frame(flags=%x, frame_len=%" FMT_OFF_T,
flags, frame_len);
if(!data->conn) {