From 4446fda8e019a0138bec1aa2d83a720d63019ff9 Mon Sep 17 00:00:00 2001
From: Curl Upstream <curl-library@cool.haxx.se>
Date: Tue, 30 Jun 2020 15:30:38 +0200
Subject: curl 2020-06-30 (5a1fc8d3)

Code extracted from:

    https://github.com/curl/curl.git

at commit 5a1fc8d33808d7b22f57bdf9403cda7ff07b0670 (curl-7_71_1).
---
 include/curl/curl.h    |   6 +--
 include/curl/curlver.h |   6 +--
 include/curl/multi.h   |   2 +-
 lib/cookie.c           |   2 +-
 lib/curl_path.c        |   4 +-
 lib/dict.c             |   3 +-
 lib/dotdot.c           |  10 ++--
 lib/dynbuf.c           |   2 +-
 lib/dynbuf.h           |   2 +-
 lib/escape.c           |  24 +++++++---
 lib/escape.h           |  11 ++++-
 lib/file.c             |   2 +-
 lib/formdata.c         |   4 +-
 lib/ftp.c              |  18 +++++---
 lib/gopher.c           |   2 +-
 lib/http.c             |  10 ++--
 lib/http2.c            |  44 ++++++++++++++++++
 lib/if2ip.c            |  13 ++++--
 lib/imap.c             |  10 ++--
 lib/ldap.c             |   7 +--
 lib/mime.c             |   2 +-
 lib/mqtt.c             |   3 +-
 lib/multi.c            | 122 ++-----------------------------------------------
 lib/multihandle.h      |   4 --
 lib/pingpong.c         |   4 +-
 lib/pop3.c             |   4 +-
 lib/sendf.c            |   2 +-
 lib/smb.c              |   4 +-
 lib/smtp.c             |   5 +-
 lib/strerror.c         |   2 +-
 lib/strtok.c           |   4 +-
 lib/tftp.c             |   2 +-
 lib/url.c              |  37 +++++++++------
 lib/urlapi.c           |  13 ++++--
 lib/urldata.h          |   5 +-
 lib/vquic/ngtcp2.c     |   4 +-
 lib/vtls/mbedtls.c     |  10 ++++
 lib/vtls/openssl.c     |  11 +++--
 lib/vtls/vtls.c        |  48 +++++++++++++++++--
 39 files changed, 250 insertions(+), 218 deletions(-)

diff --git a/include/curl/curl.h b/include/curl/curl.h
index d5f8817..e3531f5 100644
--- a/include/curl/curl.h
+++ b/include/curl/curl.h
@@ -774,7 +774,7 @@ enum curl_khtype {
 };
 
 struct curl_khkey {
-  const char *key; /* points to a zero-terminated string encoded with base64
+  const char *key; /* points to a null-terminated string encoded with base64
                       if len is zero, otherwise to the "raw" data */
   size_t len;
   enum curl_khtype keytype;
@@ -1446,7 +1446,7 @@ typedef enum {
   /* 132 OBSOLETE. Gone in 7.16.0 */
   /* 133 OBSOLETE. Gone in 7.16.0 */
 
-  /* zero terminated string for pass on to the FTP server when asked for
+  /* null-terminated string for pass on to the FTP server when asked for
      "account" info */
   CURLOPT(CURLOPT_FTP_ACCOUNT, CURLOPTTYPE_STRINGPOINT, 134),
 
@@ -2118,7 +2118,7 @@ typedef enum {
   CURL_TIMECOND_LAST
 } curl_TimeCond;
 
-/* Special size_t value signaling a zero-terminated string. */
+/* Special size_t value signaling a null-terminated string. */
 #define CURL_ZERO_TERMINATED ((size_t) -1)
 
 /* curl_strequal() and curl_strnequal() are subject for removal in a future
diff --git a/include/curl/curlver.h b/include/curl/curlver.h
index a398b0b..fe29f0f 100644
--- a/include/curl/curlver.h
+++ b/include/curl/curlver.h
@@ -30,13 +30,13 @@
 
 /* This is the version number of the libcurl package from which this header
    file origins: */
-#define LIBCURL_VERSION "7.71.0-DEV"
+#define LIBCURL_VERSION "7.71.1-DEV"
 
 /* The numeric version number is also available "in parts" by using these
    defines: */
 #define LIBCURL_VERSION_MAJOR 7
 #define LIBCURL_VERSION_MINOR 71
-#define LIBCURL_VERSION_PATCH 0
+#define LIBCURL_VERSION_PATCH 1
 
 /* This is the numeric version of the libcurl version number, meant for easier
    parsing and comparisons by programs. The LIBCURL_VERSION_NUM define will
@@ -57,7 +57,7 @@
    CURL_VERSION_BITS() macro since curl's own configure script greps for it
    and needs it to contain the full number.
 */
-#define LIBCURL_VERSION_NUM 0x074700
+#define LIBCURL_VERSION_NUM 0x074701
 
 /*
  * This is the date and time when the full source package was created. The
diff --git a/include/curl/multi.h b/include/curl/multi.h
index 3039e78..2e6bb72 100644
--- a/include/curl/multi.h
+++ b/include/curl/multi.h
@@ -267,7 +267,7 @@ CURL_EXTERN CURLMsg *curl_multi_info_read(CURLM *multi_handle,
  *          value into the equivalent human readable error string.  This is
  *          useful for printing meaningful error messages.
  *
- * Returns: A pointer to a zero-terminated error message.
+ * Returns: A pointer to a null-terminated error message.
  */
 CURL_EXTERN const char *curl_multi_strerror(CURLMcode);
 
diff --git a/lib/cookie.c b/lib/cookie.c
index 68054e1..cb7d94b 100644
--- a/lib/cookie.c
+++ b/lib/cookie.c
@@ -755,7 +755,7 @@ Curl_cookie_add(struct Curl_easy *data,
         co->path = malloc(pathlen + 1); /* one extra for the zero byte */
         if(co->path) {
           memcpy(co->path, path, pathlen);
-          co->path[pathlen] = 0; /* zero terminate */
+          co->path[pathlen] = 0; /* null-terminate */
           co->spath = sanitize_cookie_path(co->path);
           if(!co->spath)
             badcookie = TRUE; /* out of memory bad */
diff --git a/lib/curl_path.c b/lib/curl_path.c
index f429634..fbd98cb 100644
--- a/lib/curl_path.c
+++ b/lib/curl_path.c
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
@@ -42,7 +42,7 @@ CURLcode Curl_getworkingpath(struct connectdata *conn,
   size_t working_path_len;
   CURLcode result =
     Curl_urldecode(data, data->state.up.path, 0, &working_path,
-                   &working_path_len, FALSE);
+                   &working_path_len, REJECT_ZERO);
   if(result)
     return result;
 
diff --git a/lib/dict.c b/lib/dict.c
index c802dee..f529b48 100644
--- a/lib/dict.c
+++ b/lib/dict.c
@@ -99,7 +99,8 @@ static char *unescape_word(struct Curl_easy *data, const char *inputbuff)
   char *dictp;
   size_t len;
 
-  CURLcode result = Curl_urldecode(data, inputbuff, 0, &newp, &len, FALSE);
+  CURLcode result = Curl_urldecode(data, inputbuff, 0, &newp, &len,
+                                   REJECT_NADA);
   if(!newp || result)
     return NULL;
 
diff --git a/lib/dotdot.c b/lib/dotdot.c
index fe4f497..ce9a052 100644
--- a/lib/dotdot.c
+++ b/lib/dotdot.c
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
@@ -39,7 +39,7 @@
  * Curl_dedotdotify()
  * @unittest: 1395
  *
- * This function gets a zero-terminated path with dot and dotdot sequences
+ * This function gets a null-terminated path with dot and dotdot sequences
  * passed in and strips them off according to the rules in RFC 3986 section
  * 5.2.4.
  *
@@ -62,7 +62,7 @@ char *Curl_dedotdotify(const char *input)
   if(!out)
     return NULL; /* out of memory */
 
-  *out = 0; /* zero terminates, for inputs like "./" */
+  *out = 0; /* null-terminates, for inputs like "./" */
 
   /* get a cloned copy of the input */
   clone = strdup(input);
@@ -129,7 +129,7 @@ char *Curl_dedotdotify(const char *input)
         if(*outptr == '/')
           break;
       }
-      *outptr = 0; /* zero-terminate where it stops */
+      *outptr = 0; /* null-terminate where it stops */
     }
     else if(!strcmp("/..", clone)) {
       clone[2]='/';
@@ -141,7 +141,7 @@ char *Curl_dedotdotify(const char *input)
         if(*outptr == '/')
           break;
       }
-      *outptr = 0; /* zero-terminate where it stops */
+      *outptr = 0; /* null-terminate where it stops */
     }
 
     /*  D.  if the input buffer consists only of "." or "..", then remove
diff --git a/lib/dynbuf.c b/lib/dynbuf.c
index dfc1d05..38d370b 100644
--- a/lib/dynbuf.c
+++ b/lib/dynbuf.c
@@ -161,7 +161,7 @@ CURLcode Curl_dyn_addn(struct dynbuf *s, const void *mem, size_t len)
 }
 
 /*
- * Append a zero terminated string at the end.
+ * Append a null-terminated string at the end.
  */
 CURLcode Curl_dyn_add(struct dynbuf *s, const char *str)
 {
diff --git a/lib/dynbuf.h b/lib/dynbuf.h
index 7809bec..c80239e 100644
--- a/lib/dynbuf.h
+++ b/lib/dynbuf.h
@@ -23,7 +23,7 @@
  ***************************************************************************/
 
 struct dynbuf {
-  char *bufr;    /* point to a zero terminated allocated buffer */
+  char *bufr;    /* point to a null-terminated allocated buffer */
   size_t leng;   /* number of bytes *EXCLUDING* the zero terminator */
   size_t allc;   /* size of the current allocation */
   size_t toobig; /* size limit for the buffer */
diff --git a/lib/escape.c b/lib/escape.c
index f3c558e..2bea145 100644
--- a/lib/escape.c
+++ b/lib/escape.c
@@ -89,6 +89,9 @@ char *curl_easy_escape(struct Curl_easy *data, const char *string,
   Curl_dyn_init(&d, CURL_MAX_INPUT_LENGTH);
 
   length = (inlength?(size_t)inlength:strlen(string));
+  if(!length)
+    return strdup("");
+
   while(length--) {
     unsigned char in = *string; /* we need to treat the characters unsigned */
 
@@ -120,19 +123,26 @@ char *curl_easy_escape(struct Curl_easy *data, const char *string,
 /*
  * Curl_urldecode() URL decodes the given string.
  *
- * Optionally detects control characters (byte codes lower than 32) in the
- * data and rejects such data.
- *
  * Returns a pointer to a malloced string in *ostring with length given in
  * *olen. If length == 0, the length is assumed to be strlen(string).
  *
  * 'data' can be set to NULL but then this function can't convert network
  * data to host for non-ascii.
+ *
+ * ctrl options:
+ * - REJECT_NADA: accept everything
+ * - REJECT_CTRL: rejects control characters (byte codes lower than 32) in
+ *                the data
+ * - REJECT_ZERO: rejects decoded zero bytes
+ *
+ * The values for the enum starts at 2, to make the assert detect legacy
+ * invokes that used TRUE/FALSE (0 and 1).
  */
+
 CURLcode Curl_urldecode(struct Curl_easy *data,
                         const char *string, size_t length,
                         char **ostring, size_t *olen,
-                        bool reject_ctrl)
+                        enum urlreject ctrl)
 {
   size_t alloc;
   char *ns;
@@ -141,6 +151,7 @@ CURLcode Curl_urldecode(struct Curl_easy *data,
   CURLcode result = CURLE_OK;
 
   DEBUGASSERT(string);
+  DEBUGASSERT(ctrl >= REJECT_NADA); /* crash on TRUE/FALSE */
 
   alloc = (length?length:strlen(string)) + 1;
   ns = malloc(alloc);
@@ -176,7 +187,8 @@ CURLcode Curl_urldecode(struct Curl_easy *data,
       alloc -= 2;
     }
 
-    if(reject_ctrl && (in < 0x20)) {
+    if(((ctrl == REJECT_CTRL) && (in < 0x20)) ||
+       ((ctrl == REJECT_ZERO) && (in == 0))) {
       free(ns);
       return CURLE_URL_MALFORMAT;
     }
@@ -210,7 +222,7 @@ char *curl_easy_unescape(struct Curl_easy *data, const char *string,
     size_t inputlen = length;
     size_t outputlen;
     CURLcode res = Curl_urldecode(data, string, inputlen, &str, &outputlen,
-                                  FALSE);
+                                  REJECT_NADA);
     if(res)
       return NULL;
 
diff --git a/lib/escape.h b/lib/escape.h
index d8bbe5c..586db7e 100644
--- a/lib/escape.h
+++ b/lib/escape.h
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
@@ -25,9 +25,16 @@
  * allocated string or NULL if an error occurred.  */
 
 bool Curl_isunreserved(unsigned char in);
+
+enum urlreject {
+  REJECT_NADA = 2,
+  REJECT_CTRL,
+  REJECT_ZERO
+};
+
 CURLcode Curl_urldecode(struct Curl_easy *data,
                         const char *string, size_t length,
                         char **ostring, size_t *olen,
-                        bool reject_crlf);
+                        enum urlreject ctrl);
 
 #endif /* HEADER_CURL_ESCAPE_H */
diff --git a/lib/file.c b/lib/file.c
index 576a794..cd3e49c 100644
--- a/lib/file.c
+++ b/lib/file.c
@@ -144,7 +144,7 @@ static CURLcode file_connect(struct connectdata *conn, bool *done)
   size_t real_path_len;
 
   CURLcode result = Curl_urldecode(data, data->state.up.path, 0, &real_path,
-                                   &real_path_len, FALSE);
+                                   &real_path_len, REJECT_ZERO);
   if(result)
     return result;
 
diff --git a/lib/formdata.c b/lib/formdata.c
index dfa44bc..1cab2c5 100644
--- a/lib/formdata.c
+++ b/lib/formdata.c
@@ -602,7 +602,7 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost,
         /* Note that there's small risk that form->name is NULL here if the
            app passed in a bad combo, so we better check for that first. */
         if(form->name) {
-          /* copy name (without strdup; possibly not nul-terminated) */
+          /* copy name (without strdup; possibly not null-terminated) */
           form->name = Curl_memdup(form->name, form->namelength?
                                    form->namelength:
                                    strlen(form->name) + 1);
@@ -771,7 +771,7 @@ void curl_formfree(struct curl_httppost *form)
 }
 
 
-/* Set mime part name, taking care of non nul-terminated name string. */
+/* Set mime part name, taking care of non null-terminated name string. */
 static CURLcode setname(curl_mimepart *part, const char *name, size_t len)
 {
   char *zname;
diff --git a/lib/ftp.c b/lib/ftp.c
index 175d2ee..20351ff 100644
--- a/lib/ftp.c
+++ b/lib/ftp.c
@@ -1043,6 +1043,7 @@ static CURLcode ftp_state_use_port(struct connectdata *conn,
   } /* data->set.ftpport */
 
   if(!host) {
+    const char *r;
     /* not an interface and not a host name, get default by extracting
        the IP from the control connection */
     sslen = sizeof(ss);
@@ -1055,13 +1056,15 @@ static CURLcode ftp_state_use_port(struct connectdata *conn,
     switch(sa->sa_family) {
 #ifdef ENABLE_IPV6
     case AF_INET6:
-      Curl_inet_ntop(sa->sa_family, &sa6->sin6_addr, hbuf, sizeof(hbuf));
+      r = Curl_inet_ntop(sa->sa_family, &sa6->sin6_addr, hbuf, sizeof(hbuf));
       break;
 #endif
     default:
-      Curl_inet_ntop(sa->sa_family, &sa4->sin_addr, hbuf, sizeof(hbuf));
+      r = Curl_inet_ntop(sa->sa_family, &sa4->sin_addr, hbuf, sizeof(hbuf));
       break;
     }
+    if(!r)
+      return CURLE_FTP_PORT_FAILED;
     host = hbuf; /* use this host name */
     possibly_non_local = FALSE; /* we know it is local now */
   }
@@ -1449,7 +1452,7 @@ static CURLcode ftp_state_list(struct connectdata *conn)
     /* url-decode before evaluation: e.g. paths starting/ending with %2f */
     const char *slashPos = NULL;
     char *rawPath = NULL;
-    result = Curl_urldecode(data, ftp->path, 0, &rawPath, NULL, TRUE);
+    result = Curl_urldecode(data, ftp->path, 0, &rawPath, NULL, REJECT_CTRL);
     if(result)
       return result;
 
@@ -2824,7 +2827,7 @@ static CURLcode ftp_statemach_act(struct connectdata *conn)
             store++;
             ptr++;
           }
-          *store = '\0'; /* zero terminate */
+          *store = '\0'; /* null-terminate */
         }
         if(entry_extracted) {
           /* If the path name does not look like an absolute path (i.e.: it
@@ -2888,7 +2891,7 @@ static CURLcode ftp_statemach_act(struct connectdata *conn)
           ptr++;
         for(store = os; *ptr && *ptr != ' ';)
           *store++ = *ptr++;
-        *store = '\0'; /* zero terminate */
+        *store = '\0'; /* null-terminate */
 
         /* Check for special servers here. */
 
@@ -3191,7 +3194,8 @@ static CURLcode ftp_done(struct connectdata *conn, CURLcode status,
 
   if(!result)
     /* get the url-decoded "raw" path */
-    result = Curl_urldecode(data, ftp->path, 0, &rawPath, &pathLen, TRUE);
+    result = Curl_urldecode(data, ftp->path, 0, &rawPath, &pathLen,
+                            REJECT_CTRL);
   if(result) {
     /* We can limp along anyway (and should try to since we may already be in
      * the error path) */
@@ -4107,7 +4111,7 @@ CURLcode ftp_parse_url_path(struct connectdata *conn)
   ftpc->cwdfail = FALSE;
 
   /* url-decode ftp path before further evaluation */
-  result = Curl_urldecode(data, ftp->path, 0, &rawPath, &pathLen, TRUE);
+  result = Curl_urldecode(data, ftp->path, 0, &rawPath, &pathLen, REJECT_CTRL);
   if(result)
     return result;
 
diff --git a/lib/gopher.c b/lib/gopher.c
index c48098f..b4811b2 100644
--- a/lib/gopher.c
+++ b/lib/gopher.c
@@ -116,7 +116,7 @@ static CURLcode gopher_do(struct connectdata *conn, bool *done)
     newp += 2;
 
     /* ... and finally unescape */
-    result = Curl_urldecode(data, newp, 0, &sel, &len, FALSE);
+    result = Curl_urldecode(data, newp, 0, &sel, &len, REJECT_ZERO);
     free(gopherpath);
     if(result)
       return result;
diff --git a/lib/http.c b/lib/http.c
index 482f9ad..28d66c2 100644
--- a/lib/http.c
+++ b/lib/http.c
@@ -125,7 +125,8 @@ const struct Curl_handler Curl_handler_http = {
   ZERO_NULL,                            /* connection_check */
   PORT_HTTP,                            /* defport */
   CURLPROTO_HTTP,                       /* protocol */
-  PROTOPT_CREDSPERREQUEST               /* flags */
+  PROTOPT_CREDSPERREQUEST |             /* flags */
+  PROTOPT_USERPWDCTRL
 };
 
 #ifdef USE_SSL
@@ -150,7 +151,8 @@ const struct Curl_handler Curl_handler_https = {
   ZERO_NULL,                            /* connection_check */
   PORT_HTTPS,                           /* defport */
   CURLPROTO_HTTPS,                      /* protocol */
-  PROTOPT_SSL | PROTOPT_CREDSPERREQUEST | PROTOPT_ALPN_NPN /* flags */
+  PROTOPT_SSL | PROTOPT_CREDSPERREQUEST | PROTOPT_ALPN_NPN | /* flags */
+  PROTOPT_USERPWDCTRL
 };
 #endif
 
@@ -268,7 +270,7 @@ char *Curl_copy_header_value(const char *header)
     return NULL;
 
   memcpy(value, start, len);
-  value[len] = 0; /* zero terminate */
+  value[len] = 0; /* null-terminate */
 
   return value;
 }
@@ -306,7 +308,7 @@ static CURLcode http_output_basic(struct connectdata *conn, bool proxy)
     pwd = conn->passwd;
   }
 
-  out = aprintf("%s:%s", user, pwd);
+  out = aprintf("%s:%s", user, pwd ? pwd : "");
   if(!out)
     return CURLE_OUT_OF_MEMORY;
 
diff --git a/lib/http2.c b/lib/http2.c
index 6199001..6cf651f 100644
--- a/lib/http2.c
+++ b/lib/http2.c
@@ -469,6 +469,46 @@ static struct Curl_easy *duphandle(struct Curl_easy *data)
   return second;
 }
 
+static int set_transfer_url(struct Curl_easy *data,
+                            struct curl_pushheaders *hp)
+{
+  const char *v;
+  CURLU *u = curl_url();
+  CURLUcode uc;
+  char *url;
+
+  v = curl_pushheader_byname(hp, ":scheme");
+  if(v) {
+    uc = curl_url_set(u, CURLUPART_SCHEME, v, 0);
+    if(uc)
+      return 1;
+  }
+
+  v = curl_pushheader_byname(hp, ":authority");
+  if(v) {
+    uc = curl_url_set(u, CURLUPART_HOST, v, 0);
+    if(uc)
+      return 2;
+  }
+
+  v = curl_pushheader_byname(hp, ":path");
+  if(v) {
+    uc = curl_url_set(u, CURLUPART_PATH, v, 0);
+    if(uc)
+      return 3;
+  }
+
+  uc = curl_url_get(u, CURLUPART_URL, &url, 0);
+  if(uc)
+    return 4;
+  curl_url_cleanup(u);
+
+  if(data->change.url_alloc)
+    free(data->change.url);
+  data->change.url_alloc = TRUE;
+  data->change.url = url;
+  return 0;
+}
 
 static int push_promise(struct Curl_easy *data,
                         struct connectdata *conn,
@@ -505,6 +545,10 @@ static int push_promise(struct Curl_easy *data,
       goto fail;
     }
 
+    rv = set_transfer_url(newhandle, &heads);
+    if(rv)
+      goto fail;
+
     Curl_set_in_callback(data, true);
     rv = data->multi->push_cb(data, newhandle,
                               stream->push_headers_used, &heads,
diff --git a/lib/if2ip.c b/lib/if2ip.c
index b283f67..3938869 100644
--- a/lib/if2ip.c
+++ b/lib/if2ip.c
@@ -118,7 +118,7 @@ if2ip_result_t Curl_if2ip(int af, unsigned int remote_scope,
         if(iface->ifa_addr->sa_family == af) {
           if(strcasecompare(iface->ifa_name, interf)) {
             void *addr;
-            char *ip;
+            const char *ip;
             char scope[12] = "";
             char ipstr[64];
 #ifdef ENABLE_IPV6
@@ -153,15 +153,15 @@ if2ip_result_t Curl_if2ip(int af, unsigned int remote_scope,
               }
 
               if(scopeid)
-                  msnprintf(scope, sizeof(scope), "%%%u", scopeid);
+                msnprintf(scope, sizeof(scope), "%%%u", scopeid);
 #endif
             }
             else
 #endif
               addr =
-                  &((struct sockaddr_in *)(void *)iface->ifa_addr)->sin_addr;
+                &((struct sockaddr_in *)(void *)iface->ifa_addr)->sin_addr;
             res = IF2IP_FOUND;
-            ip = (char *) Curl_inet_ntop(af, addr, ipstr, sizeof(ipstr));
+            ip = Curl_inet_ntop(af, addr, ipstr, sizeof(ipstr));
             msnprintf(buf, buf_size, "%s%s", ip, scope);
             break;
           }
@@ -190,6 +190,7 @@ if2ip_result_t Curl_if2ip(int af, unsigned int remote_scope,
   struct sockaddr_in *s;
   curl_socket_t dummy;
   size_t len;
+  const char *r;
 
   (void)remote_scope;
   (void)local_scope_id;
@@ -219,9 +220,11 @@ if2ip_result_t Curl_if2ip(int af, unsigned int remote_scope,
 
   s = (struct sockaddr_in *)(void *)&req.ifr_addr;
   memcpy(&in, &s->sin_addr, sizeof(in));
-  Curl_inet_ntop(s->sin_family, &in, buf, buf_size);
+  r = Curl_inet_ntop(s->sin_family, &in, buf, buf_size);
 
   sclose(dummy);
+  if(!r)
+    return IF2IP_NOT_FOUND;
   return IF2IP_FOUND;
 }
 
diff --git a/lib/imap.c b/lib/imap.c
index 14c76f0..cad0e59 100644
--- a/lib/imap.c
+++ b/lib/imap.c
@@ -1957,7 +1957,7 @@ static CURLcode imap_parse_url_path(struct connectdata *conn)
       end--;
 
     result = Curl_urldecode(data, begin, end - begin, &imap->mailbox, NULL,
-                            TRUE);
+                            REJECT_CTRL);
     if(result)
       return result;
   }
@@ -1979,7 +1979,8 @@ static CURLcode imap_parse_url_path(struct connectdata *conn)
       return CURLE_URL_MALFORMAT;
 
     /* Decode the name parameter */
-    result = Curl_urldecode(data, begin, ptr - begin, &name, NULL, TRUE);
+    result = Curl_urldecode(data, begin, ptr - begin, &name, NULL,
+                            REJECT_CTRL);
     if(result)
       return result;
 
@@ -1989,7 +1990,8 @@ static CURLcode imap_parse_url_path(struct connectdata *conn)
       ptr++;
 
     /* Decode the value parameter */
-    result = Curl_urldecode(data, begin, ptr - begin, &value, &valuelen, TRUE);
+    result = Curl_urldecode(data, begin, ptr - begin, &value, &valuelen,
+                            REJECT_CTRL);
     if(result) {
       free(name);
       return result;
@@ -2077,7 +2079,7 @@ static CURLcode imap_parse_custom_request(struct connectdata *conn)
 
   if(custom) {
     /* URL decode the custom request */
-    result = Curl_urldecode(data, custom, 0, &imap->custom, NULL, TRUE);
+    result = Curl_urldecode(data, custom, 0, &imap->custom, NULL, REJECT_CTRL);
 
     /* Extract the parameters if specified */
     if(!result) {
diff --git a/lib/ldap.c b/lib/ldap.c
index 2730658..512def6 100644
--- a/lib/ldap.c
+++ b/lib/ldap.c
@@ -883,7 +883,7 @@ static int _ldap_url_parse2(const struct connectdata *conn, LDAPURLDesc *ludp)
     LDAP_TRACE(("DN '%s'\n", dn));
 
     /* Unescape the DN */
-    result = Curl_urldecode(conn->data, dn, 0, &unescaped, NULL, FALSE);
+    result = Curl_urldecode(conn->data, dn, 0, &unescaped, NULL, REJECT_ZERO);
     if(result) {
       rc = LDAP_NO_MEMORY;
 
@@ -949,7 +949,7 @@ static int _ldap_url_parse2(const struct connectdata *conn, LDAPURLDesc *ludp)
 
       /* Unescape the attribute */
       result = Curl_urldecode(conn->data, attributes[i], 0, &unescaped, NULL,
-                              FALSE);
+                              REJECT_ZERO);
       if(result) {
         free(attributes);
 
@@ -1018,7 +1018,8 @@ static int _ldap_url_parse2(const struct connectdata *conn, LDAPURLDesc *ludp)
     LDAP_TRACE(("filter '%s'\n", filter));
 
     /* Unescape the filter */
-    result = Curl_urldecode(conn->data, filter, 0, &unescaped, NULL, FALSE);
+    result = Curl_urldecode(conn->data, filter, 0, &unescaped, NULL,
+                            REJECT_ZERO);
     if(result) {
       rc = LDAP_NO_MEMORY;
 
diff --git a/lib/mime.c b/lib/mime.c
index 4a87c4a..6a9b64a 100644
--- a/lib/mime.c
+++ b/lib/mime.c
@@ -1419,7 +1419,7 @@ CURLcode curl_mime_data(curl_mimepart *part,
 
     if(datasize)
       memcpy(part->data, data, datasize);
-    part->data[datasize] = '\0';    /* Set a nul terminator as sentinel. */
+    part->data[datasize] = '\0';    /* Set a null terminator as sentinel. */
 
     part->readfunc = mime_mem_read;
     part->seekfunc = mime_mem_seek;
diff --git a/lib/mqtt.c b/lib/mqtt.c
index d09aab4..f6f4416 100644
--- a/lib/mqtt.c
+++ b/lib/mqtt.c
@@ -211,7 +211,8 @@ static CURLcode mqtt_get_topic(struct connectdata *conn,
   char *path = conn->data->state.up.path;
 
   if(strlen(path) > 1) {
-    result = Curl_urldecode(conn->data, path + 1, 0, topic, topiclen, FALSE);
+    result = Curl_urldecode(conn->data, path + 1, 0, topic, topiclen,
+                            REJECT_NADA);
   }
   else {
     failf(conn->data, "Error: No topic specified.");
diff --git a/lib/multi.c b/lib/multi.c
index b106c94..249e360 100644
--- a/lib/multi.c
+++ b/lib/multi.c
@@ -374,11 +374,6 @@ struct Curl_multi *Curl_multi_handle(int hashsize, /* socket hash */
   multi->max_concurrent_streams = 100;
   multi->ipv6_works = Curl_ipv6works(NULL);
 
-#ifdef USE_WINSOCK
-  multi->wsa_event = WSACreateEvent();
-  if(multi->wsa_event == WSA_INVALID_EVENT)
-    goto error;
-#else
 #ifdef ENABLE_WAKEUP
   if(Curl_socketpair(AF_UNIX, SOCK_STREAM, 0, multi->wakeup_pair) < 0) {
     multi->wakeup_pair[0] = CURL_SOCKET_BAD;
@@ -392,7 +387,6 @@ struct Curl_multi *Curl_multi_handle(int hashsize, /* socket hash */
     multi->wakeup_pair[1] = CURL_SOCKET_BAD;
   }
 #endif
-#endif
 
   return multi;
 
@@ -1073,15 +1067,11 @@ static CURLMcode Curl_multi_wait(struct Curl_multi *multi,
   unsigned int i;
   unsigned int nfds = 0;
   unsigned int curlfds;
+  bool ufds_malloc = FALSE;
   long timeout_internal;
   int retcode = 0;
-#ifndef USE_WINSOCK
   struct pollfd a_few_on_stack[NUM_POLLS_ON_STACK];
   struct pollfd *ufds = &a_few_on_stack[0];
-  bool ufds_malloc = FALSE;
-#else
-  DEBUGASSERT(multi->wsa_event != WSA_INVALID_EVENT);
-#endif
 
   if(!GOOD_MULTI_HANDLE(multi))
     return CURLM_BAD_HANDLE;
@@ -1127,16 +1117,11 @@ static CURLMcode Curl_multi_wait(struct Curl_multi *multi,
   nfds += extra_nfds; /* add the externally provided ones */
 
 #ifdef ENABLE_WAKEUP
-#ifdef USE_WINSOCK
-  if(use_wakeup) {
-#else
   if(use_wakeup && multi->wakeup_pair[0] != CURL_SOCKET_BAD) {
-#endif
     ++nfds;
   }
 #endif
 
-#ifndef USE_WINSOCK
   if(nfds > NUM_POLLS_ON_STACK) {
     /* 'nfds' is a 32 bit value and 'struct pollfd' is typically 8 bytes
        big, so at 2^29 sockets this value might wrap. When a process gets
@@ -1147,9 +1132,7 @@ static CURLMcode Curl_multi_wait(struct Curl_multi *multi,
       return CURLM_OUT_OF_MEMORY;
     ufds_malloc = TRUE;
   }
-
   nfds = 0;
-#endif
 
   /* only do the second loop if we found descriptors in the first stage run
      above */
@@ -1162,36 +1145,22 @@ static CURLMcode Curl_multi_wait(struct Curl_multi *multi,
 
       for(i = 0; i< MAX_SOCKSPEREASYHANDLE; i++) {
         curl_socket_t s = CURL_SOCKET_BAD;
-#ifdef USE_WINSOCK
-        long mask = 0;
-#endif
+
         if(bitmap & GETSOCK_READSOCK(i)) {
-#ifdef USE_WINSOCK
-          mask |= FD_READ;
-#else
           ufds[nfds].fd = sockbunch[i];
           ufds[nfds].events = POLLIN;
           ++nfds;
-#endif
           s = sockbunch[i];
         }
         if(bitmap & GETSOCK_WRITESOCK(i)) {
-#ifdef USE_WINSOCK
-          mask |= FD_WRITE;
-#else
           ufds[nfds].fd = sockbunch[i];
           ufds[nfds].events = POLLOUT;
           ++nfds;
-#endif
           s = sockbunch[i];
         }
         if(s == CURL_SOCKET_BAD) {
           break;
         }
-#ifdef USE_WINSOCK
-        if(WSAEventSelect(s, multi->wsa_event, mask) != 0)
-          return CURLM_INTERNAL_ERROR;
-#endif
       }
 
       data = data->next; /* check next handle */
@@ -1200,17 +1169,6 @@ static CURLMcode Curl_multi_wait(struct Curl_multi *multi,
 
   /* Add external file descriptions from poll-like struct curl_waitfd */
   for(i = 0; i < extra_nfds; i++) {
-#ifdef USE_WINSOCK
-    long events = 0;
-    if(extra_fds[i].events & CURL_WAIT_POLLIN)
-      events |= FD_READ;
-    if(extra_fds[i].events & CURL_WAIT_POLLPRI)
-      events |= FD_OOB;
-    if(extra_fds[i].events & CURL_WAIT_POLLOUT)
-      events |= FD_WRITE;
-    if(WSAEventSelect(extra_fds[i].fd, multi->wsa_event, events) != 0)
-      return CURLM_INTERNAL_ERROR;
-#else
     ufds[nfds].fd = extra_fds[i].fd;
     ufds[nfds].events = 0;
     if(extra_fds[i].events & CURL_WAIT_POLLIN)
@@ -1220,61 +1178,28 @@ static CURLMcode Curl_multi_wait(struct Curl_multi *multi,
     if(extra_fds[i].events & CURL_WAIT_POLLOUT)
       ufds[nfds].events |= POLLOUT;
     ++nfds;
-#endif
   }
 
 #ifdef ENABLE_WAKEUP
-#ifndef USE_WINSOCK
   if(use_wakeup && multi->wakeup_pair[0] != CURL_SOCKET_BAD) {
     ufds[nfds].fd = multi->wakeup_pair[0];
     ufds[nfds].events = POLLIN;
     ++nfds;
   }
 #endif
-#endif
 
   if(nfds) {
     int pollrc;
     /* wait... */
-#ifdef USE_WINSOCK
-    DWORD waitrc = WSAWaitForMultipleEvents(1, &multi->wsa_event, FALSE,
-                                            timeout_ms, FALSE);
-    /* WSA_WAIT_EVENT_0 is 0, so waitrc >= WSA_WAIT_EVENT_0 warns */
-    if(waitrc == WSA_WAIT_EVENT_0)
-      pollrc = 1;
-    else
-      pollrc = -1;
-#else
     pollrc = Curl_poll(ufds, nfds, timeout_ms);
-#endif
 
     if(pollrc > 0) {
-#ifdef USE_WINSOCK
-      retcode = 0;
-#else
       retcode = pollrc;
-#endif
       /* copy revents results from the poll to the curl_multi_wait poll
          struct, the bit values of the actual underlying poll() implementation
          may not be the same as the ones in the public libcurl API! */
       for(i = 0; i < extra_nfds; i++) {
         unsigned short mask = 0;
-#ifdef USE_WINSOCK
-        WSANETWORKEVENTS events = {0};
-        if(WSAEnumNetworkEvents(extra_fds[i].fd, multi->wsa_event,
-                                &events) == 0) {
-          if(events.lNetworkEvents & FD_READ)
-            mask |= CURL_WAIT_POLLIN;
-          if(events.lNetworkEvents & FD_WRITE)
-            mask |= CURL_WAIT_POLLOUT;
-          if(events.lNetworkEvents & FD_OOB)
-            mask |= CURL_WAIT_POLLPRI;
-
-          if(events.lNetworkEvents != 0)
-            retcode++;
-        }
-        WSAEventSelect(extra_fds[i].fd, multi->wsa_event, 0);
-#else
         unsigned r = ufds[curlfds + i].revents;
 
         if(r & POLLIN)
@@ -1283,39 +1208,10 @@ static CURLMcode Curl_multi_wait(struct Curl_multi *multi,
           mask |= CURL_WAIT_POLLOUT;
         if(r & POLLPRI)
           mask |= CURL_WAIT_POLLPRI;
-#endif
 
         extra_fds[i].revents = mask;
       }
 
-#ifdef USE_WINSOCK
-      /* Count up all our own sockets that had activity,
-         and remove them from the event. */
-      if(curlfds) {
-        data = multi->easyp;
-        while(data) {
-          bitmap = multi_getsock(data, sockbunch);
-
-          for(i = 0; i < MAX_SOCKSPEREASYHANDLE; i++) {
-            if(bitmap & (GETSOCK_READSOCK(i) | GETSOCK_WRITESOCK(i))) {
-              WSANETWORKEVENTS events = {0};
-              if(WSAEnumNetworkEvents(sockbunch[i], multi->wsa_event,
-                                      &events) == 0) {
-                if(events.lNetworkEvents != 0)
-                  retcode++;
-              }
-              WSAEventSelect(sockbunch[i], multi->wsa_event, 0);
-            }
-            else
-              break;
-          }
-
-          data = data->next;
-        }
-      }
-
-      WSAResetEvent(multi->wsa_event);
-#else
 #ifdef ENABLE_WAKEUP
       if(use_wakeup && multi->wakeup_pair[0] != CURL_SOCKET_BAD) {
         if(ufds[curlfds + extra_nfds].revents & POLLIN) {
@@ -1328,8 +1224,10 @@ static CURLMcode Curl_multi_wait(struct Curl_multi *multi,
                when there is no more data, breaking the loop. */
             nread = sread(multi->wakeup_pair[0], buf, sizeof(buf));
             if(nread <= 0) {
+#ifndef USE_WINSOCK
               if(nread < 0 && EINTR == SOCKERRNO)
                 continue;
+#endif
               break;
             }
           }
@@ -1338,14 +1236,11 @@ static CURLMcode Curl_multi_wait(struct Curl_multi *multi,
         }
       }
 #endif
-#endif
     }
   }
 
-#ifndef USE_WINSOCK
   if(ufds_malloc)
     free(ufds);
-#endif
   if(ret)
     *ret = retcode;
   if(!extrawait || nfds)
@@ -1400,10 +1295,6 @@ CURLMcode curl_multi_wakeup(struct Curl_multi *multi)
     return CURLM_BAD_HANDLE;
 
 #ifdef ENABLE_WAKEUP
-#ifdef USE_WINSOCK
-  if(WSASetEvent(multi->wsa_event))
-    return CURLM_OK;
-#else
   /* the wakeup_pair variable is only written during init and cleanup,
      making it safe to access from another thread after the init part
      and before cleanup */
@@ -1437,7 +1328,6 @@ CURLMcode curl_multi_wakeup(struct Curl_multi *multi)
     }
   }
 #endif
-#endif
   return CURLM_WAKEUP_FAILURE;
 }
 
@@ -2596,14 +2486,10 @@ CURLMcode curl_multi_cleanup(struct Curl_multi *multi)
     Curl_hash_destroy(&multi->hostcache);
     Curl_psl_destroy(&multi->psl);
 
-#ifdef USE_WINSOCK
-    WSACloseEvent(multi->wsa_event);
-#else
 #ifdef ENABLE_WAKEUP
     sclose(multi->wakeup_pair[0]);
     sclose(multi->wakeup_pair[1]);
 #endif
-#endif
     free(multi);
 
     return CURLM_OK;
diff --git a/lib/multihandle.h b/lib/multihandle.h
index 94bbad7..91eca16 100644
--- a/lib/multihandle.h
+++ b/lib/multihandle.h
@@ -138,14 +138,10 @@ struct Curl_multi {
                                     previous callback */
   unsigned int max_concurrent_streams;
 
-#ifdef USE_WINSOCK
-  WSAEVENT wsa_event; /* winsock event used for waits */
-#else
 #ifdef ENABLE_WAKEUP
   curl_socket_t wakeup_pair[2]; /* socketpair() used for wakeup
                                    0 is used for read, 1 is used for write */
 #endif
-#endif
   /* multiplexing wanted */
   bool multiplexing;
   bool recheckstate; /* see Curl_multi_connchanged */
diff --git a/lib/pingpong.c b/lib/pingpong.c
index ced832e..3143315 100644
--- a/lib/pingpong.c
+++ b/lib/pingpong.c
@@ -384,10 +384,10 @@ CURLcode Curl_pp_readresp(curl_socket_t sockfd,
 
           if(pp->endofresp(conn, pp->linestart_resp, perline, code)) {
             /* This is the end of the last line, copy the last line to the
-               start of the buffer and zero terminate, for old times sake */
+               start of the buffer and null-terminate, for old times sake */
             size_t n = ptr - pp->linestart_resp;
             memmove(buf, pp->linestart_resp, n);
-            buf[n] = 0; /* zero terminate */
+            buf[n] = 0; /* null-terminate */
             keepon = FALSE;
             pp->linestart_resp = ptr + 1; /* advance pointer */
             i++; /* skip this before getting out */
diff --git a/lib/pop3.c b/lib/pop3.c
index 2f490b2..9ff5c78 100644
--- a/lib/pop3.c
+++ b/lib/pop3.c
@@ -1390,7 +1390,7 @@ static CURLcode pop3_parse_url_path(struct connectdata *conn)
   const char *path = &data->state.up.path[1]; /* skip leading path */
 
   /* URL decode the path for the message ID */
-  return Curl_urldecode(data, path, 0, &pop3->id, NULL, TRUE);
+  return Curl_urldecode(data, path, 0, &pop3->id, NULL, REJECT_CTRL);
 }
 
 /***********************************************************************
@@ -1408,7 +1408,7 @@ static CURLcode pop3_parse_custom_request(struct connectdata *conn)
 
   /* URL decode the custom request */
   if(custom)
-    result = Curl_urldecode(data, custom, 0, &pop3->custom, NULL, TRUE);
+    result = Curl_urldecode(data, custom, 0, &pop3->custom, NULL, REJECT_CTRL);
 
   return result;
 }
diff --git a/lib/sendf.c b/lib/sendf.c
index 147ecbf..6943fa8 100644
--- a/lib/sendf.c
+++ b/lib/sendf.c
@@ -593,7 +593,7 @@ static CURLcode chop_write(struct connectdata *conn,
         return pausewrite(data, type, ptr, len);
       }
       if(wrote != chunklen) {
-        failf(data, "Failed writing body (%zu != %zu)", wrote, chunklen);
+        failf(data, "Failure writing output to destination");
         return CURLE_WRITE_ERROR;
       }
     }
diff --git a/lib/smb.c b/lib/smb.c
index 12f9925..d493adc 100644
--- a/lib/smb.c
+++ b/lib/smb.c
@@ -6,7 +6,7 @@
  *                             \___|\___/|_| \_\_____|
  *
  * Copyright (C) 2014, Bill Nagel <wnagel@tycoint.com>, Exacq Technologies
- * Copyright (C) 2016-2019, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2016-2020, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
@@ -964,7 +964,7 @@ static CURLcode smb_parse_url_path(struct connectdata *conn)
 
   /* URL decode the path */
   CURLcode result = Curl_urldecode(data, data->state.up.path, 0, &path, NULL,
-                                   TRUE);
+                                   REJECT_CTRL);
   if(result)
     return result;
 
diff --git a/lib/smtp.c b/lib/smtp.c
index ec93648..685513b 100644
--- a/lib/smtp.c
+++ b/lib/smtp.c
@@ -1689,7 +1689,8 @@ static CURLcode smtp_parse_url_path(struct connectdata *conn)
   }
 
   /* URL decode the path and use it as the domain in our EHLO */
-  return Curl_urldecode(conn->data, path, 0, &smtpc->domain, NULL, TRUE);
+  return Curl_urldecode(conn->data, path, 0, &smtpc->domain, NULL,
+                        REJECT_CTRL);
 }
 
 /***********************************************************************
@@ -1707,7 +1708,7 @@ static CURLcode smtp_parse_custom_request(struct connectdata *conn)
 
   /* URL decode the custom request */
   if(custom)
-    result = Curl_urldecode(data, custom, 0, &smtp->custom, NULL, TRUE);
+    result = Curl_urldecode(data, custom, 0, &smtp->custom, NULL, REJECT_CTRL);
 
   return result;
 }
diff --git a/lib/strerror.c b/lib/strerror.c
index 1a166bf..015e588 100644
--- a/lib/strerror.c
+++ b/lib/strerror.c
@@ -795,7 +795,7 @@ const char *Curl_strerror(int err, char *buf, size_t buflen)
 
 #endif /* end of not Windows */
 
-  buf[max] = '\0'; /* make sure the string is zero terminated */
+  buf[max] = '\0'; /* make sure the string is null-terminated */
 
   /* strip trailing '\r\n' or '\n'. */
   p = strrchr(buf, '\n');
diff --git a/lib/strtok.c b/lib/strtok.c
index be8f481..ba6e025 100644
--- a/lib/strtok.c
+++ b/lib/strtok.c
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
@@ -52,7 +52,7 @@ Curl_strtok_r(char *ptr, const char *sep, char **end)
 
     if(**end) {
       /* the end is not a null byte */
-      **end = '\0';  /* zero terminate it! */
+      **end = '\0';  /* null-terminate it! */
       ++*end;        /* advance the last pointer to beyond the null byte */
     }
 
diff --git a/lib/tftp.c b/lib/tftp.c
index 4f2f973..378d956 100644
--- a/lib/tftp.c
+++ b/lib/tftp.c
@@ -487,7 +487,7 @@ static CURLcode tftp_send_first(struct tftp_state_data *state,
        file name so we skip the always-present first letter of the path
        string. */
     result = Curl_urldecode(data, &state->conn->data->state.up.path[1], 0,
-                            &filename, NULL, FALSE);
+                            &filename, NULL, REJECT_ZERO);
     if(result)
       return result;
 
diff --git a/lib/url.c b/lib/url.c
index 8225e61..a1a6b69 100644
--- a/lib/url.c
+++ b/lib/url.c
@@ -1894,23 +1894,32 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data,
   if(result)
     return result;
 
-  uc = curl_url_get(uh, CURLUPART_USER, &data->state.up.user,
-                    CURLU_URLDECODE);
+  /* we don't use the URL API's URL decoder option here since it rejects
+     control codes and we want to allow them for some schemes in the user and
+     password fields */
+  uc = curl_url_get(uh, CURLUPART_USER, &data->state.up.user, 0);
   if(!uc) {
-    conn->user = strdup(data->state.up.user);
-    if(!conn->user)
-      return CURLE_OUT_OF_MEMORY;
+    char *decoded;
+    result = Curl_urldecode(NULL, data->state.up.user, 0, &decoded, NULL,
+                            conn->handler->flags&PROTOPT_USERPWDCTRL ?
+                            REJECT_ZERO : REJECT_CTRL);
+    if(result)
+      return result;
+    conn->user = decoded;
     conn->bits.user_passwd = TRUE;
   }
   else if(uc != CURLUE_NO_USER)
     return Curl_uc_to_curlcode(uc);
 
-  uc = curl_url_get(uh, CURLUPART_PASSWORD, &data->state.up.password,
-                    CURLU_URLDECODE);
+  uc = curl_url_get(uh, CURLUPART_PASSWORD, &data->state.up.password, 0);
   if(!uc) {
-    conn->passwd = strdup(data->state.up.password);
-    if(!conn->passwd)
-      return CURLE_OUT_OF_MEMORY;
+    char *decoded;
+    result = Curl_urldecode(NULL, data->state.up.password, 0, &decoded, NULL,
+                            conn->handler->flags&PROTOPT_USERPWDCTRL ?
+                            REJECT_ZERO : REJECT_CTRL);
+    if(result)
+      return result;
+    conn->passwd = decoded;
     conn->bits.user_passwd = TRUE;
   }
   else if(uc != CURLUE_NO_PASSWORD)
@@ -2373,10 +2382,10 @@ static CURLcode parse_proxy_auth(struct Curl_easy *data,
 
   if(proxyuser)
     result = Curl_urldecode(data, proxyuser, 0, &conn->http_proxy.user, NULL,
-                            FALSE);
+                            REJECT_ZERO);
   if(!result && proxypasswd)
     result = Curl_urldecode(data, proxypasswd, 0, &conn->http_proxy.passwd,
-                            NULL, FALSE);
+                            NULL, REJECT_ZERO);
   return result;
 }
 
@@ -3600,6 +3609,7 @@ static CURLcode create_conn(struct Curl_easy *data,
     data->set.str[STRING_SSL_CIPHER13_LIST_ORIG];
   data->set.ssl.primary.pinned_key =
     data->set.str[STRING_SSL_PINNEDPUBLICKEY_ORIG];
+  data->set.ssl.primary.cert_blob = data->set.blobs[BLOB_CERT_ORIG];
 
 #ifndef CURL_DISABLE_PROXY
   data->set.proxy_ssl.primary.CApath = data->set.str[STRING_SSL_CAPATH_PROXY];
@@ -3613,6 +3623,7 @@ static CURLcode create_conn(struct Curl_easy *data,
     data->set.str[STRING_SSL_CIPHER13_LIST_PROXY];
   data->set.proxy_ssl.primary.pinned_key =
     data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY];
+  data->set.proxy_ssl.primary.cert_blob = data->set.blobs[BLOB_CERT_PROXY];
   data->set.proxy_ssl.CRLfile = data->set.str[STRING_SSL_CRLFILE_PROXY];
   data->set.proxy_ssl.issuercert = data->set.str[STRING_SSL_ISSUERCERT_PROXY];
   data->set.proxy_ssl.cert = data->set.str[STRING_CERT_PROXY];
@@ -3646,7 +3657,7 @@ static CURLcode create_conn(struct Curl_easy *data,
   data->set.ssl.issuercert_blob = data->set.blobs[BLOB_SSL_ISSUERCERT_ORIG];
 
   if(!Curl_clone_primary_ssl_config(&data->set.ssl.primary,
-     &conn->ssl_config)) {
+                                    &conn->ssl_config)) {
     result = CURLE_OUT_OF_MEMORY;
     goto out;
   }
diff --git a/lib/urlapi.c b/lib/urlapi.c
index 37937fb..acbfb82 100644
--- a/lib/urlapi.c
+++ b/lib/urlapi.c
@@ -225,7 +225,7 @@ static void strcpy_url(char *output, const char *url, bool relative)
       break;
     }
   }
-  *optr = 0; /* zero terminate output buffer */
+  *optr = 0; /* null-terminate output buffer */
 
 }
 
@@ -584,7 +584,7 @@ static CURLUcode junkscan(const char *part)
       0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
       0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
       0x7f,
-      0x00 /* zero terminate */
+      0x00 /* null-terminate */
     };
     size_t n = strlen(part);
     size_t nfine = strcspn(part, badbytes);
@@ -1185,7 +1185,10 @@ CURLUcode curl_url_get(CURLU *u, CURLUPart what,
     if(urldecode) {
       char *decoded;
       size_t dlen;
-      CURLcode res = Curl_urldecode(NULL, *part, 0, &decoded, &dlen, TRUE);
+      /* this unconditional rejection of control bytes is documented
+         API behavior */
+      CURLcode res = Curl_urldecode(NULL, *part, 0, &decoded, &dlen,
+                                    REJECT_CTRL);
       free(*part);
       if(res) {
         *part = NULL;
@@ -1395,7 +1398,7 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
         i = (const unsigned char *)part;
         for(o = enc; *i; ++o, ++i)
           *o = (*i == ' ') ? '+' : *i;
-        *o = 0; /* zero terminate */
+        *o = 0; /* null-terminate */
         part = strdup(enc);
         if(!part) {
           free(enc);
@@ -1419,7 +1422,7 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
           o += 3;
         }
       }
-      *o = 0; /* zero terminate */
+      *o = 0; /* null-terminate */
       newp = enc;
       if(free_part)
         free((char *)part);
diff --git a/lib/urldata.h b/lib/urldata.h
index 74b43ab..f80a02d 100644
--- a/lib/urldata.h
+++ b/lib/urldata.h
@@ -229,6 +229,7 @@ struct ssl_primary_config {
   char *cipher_list;     /* list of ciphers to use */
   char *cipher_list13;   /* list of TLS 1.3 cipher suites to use */
   char *pinned_key;
+  struct curl_blob *cert_blob;
   BIT(verifypeer);       /* set TRUE if this is desired */
   BIT(verifyhost);       /* set TRUE if CN/SAN must match hostname */
   BIT(verifystatus);     /* set TRUE if certificate status must be checked */
@@ -766,6 +767,8 @@ struct Curl_handler {
                                          HTTP proxy as HTTP proxies may know
                                          this protocol and act as a gateway */
 #define PROTOPT_WILDCARD (1<<12) /* protocol supports wildcard matching */
+#define PROTOPT_USERPWDCTRL (1<<13) /* Allow "control bytes" (< 32 ascii) in
+                                       user name and password */
 
 #define CONNCHECK_NONE 0                 /* No checks */
 #define CONNCHECK_ISDEAD (1<<0)          /* Check if the connection is dead. */
@@ -1562,7 +1565,7 @@ enum dupstring {
   STRING_DNS_LOCAL_IP4,
   STRING_DNS_LOCAL_IP6,
 
-  /* -- end of zero-terminated strings -- */
+  /* -- end of null-terminated strings -- */
 
   STRING_LASTZEROTERMINATED,
 
diff --git a/lib/vquic/ngtcp2.c b/lib/vquic/ngtcp2.c
index e552823..d29cb37 100644
--- a/lib/vquic/ngtcp2.c
+++ b/lib/vquic/ngtcp2.c
@@ -757,7 +757,9 @@ static ngtcp2_conn_callbacks ng_callbacks = {
   cb_extend_max_stream_data,
   NULL, /* dcid_status */
   NULL, /* handshake_confirmed */
-  NULL  /* recv_new_token */
+  NULL, /* recv_new_token */
+  ngtcp2_crypto_delete_crypto_aead_ctx_cb,
+  ngtcp2_crypto_delete_crypto_cipher_ctx_cb
 };
 
 /*
diff --git a/lib/vtls/mbedtls.c b/lib/vtls/mbedtls.c
index ba31b8e..545f824 100644
--- a/lib/vtls/mbedtls.c
+++ b/lib/vtls/mbedtls.c
@@ -246,9 +246,14 @@ mbed_connect_step1(struct connectdata *conn,
   const char * const ssl_capath = SSL_CONN_CONFIG(CApath);
   char * const ssl_cert = SSL_SET_OPTION(cert);
   const char * const ssl_crlfile = SSL_SET_OPTION(CRLfile);
+#ifndef CURL_DISABLE_PROXY
   const char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name :
     conn->host.name;
   const long int port = SSL_IS_PROXY() ? conn->port : conn->remote_port;
+#else
+  const char * const hostname = conn->host.name;
+  const long int port = conn->remote_port;
+#endif
   int ret = -1;
   char errorbuf[128];
   errorbuf[0] = 0;
@@ -538,9 +543,14 @@ mbed_connect_step2(struct connectdata *conn,
   struct ssl_connect_data *connssl = &conn->ssl[sockindex];
   struct ssl_backend_data *backend = connssl->backend;
   const mbedtls_x509_crt *peercert;
+#ifndef CURL_DISABLE_PROXY
   const char * const pinnedpubkey = SSL_IS_PROXY() ?
     data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY] :
     data->set.str[STRING_SSL_PINNEDPUBLICKEY_ORIG];
+#else
+  const char * const pinnedpubkey =
+    data->set.str[STRING_SSL_PINNEDPUBLICKEY_ORIG];
+#endif
 
   conn->recv[sockindex] = mbed_recv;
   conn->send[sockindex] = mbed_send;
diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c
index 790d358..2e9f900 100644
--- a/lib/vtls/openssl.c
+++ b/lib/vtls/openssl.c
@@ -31,6 +31,11 @@
 
 #include <limits.h>
 
+/* Wincrypt must be included before anything that could include OpenSSL. */
+#if defined(USE_WIN32_CRYPTO)
+#include <wincrypt.h>
+#endif
+
 #include "urldata.h"
 #include "sendf.h"
 #include "formdata.h" /* for the boundary function */
@@ -48,10 +53,6 @@
 #include "strerror.h"
 #include "curl_printf.h"
 
-#if defined(USE_WIN32_CRYPTO)
-#include <wincrypt.h>
-#endif
-
 #include <openssl/ssl.h>
 #include <openssl/rand.h>
 #include <openssl/x509v3.h>
@@ -1635,7 +1636,7 @@ static CURLcode verifyhost(struct connectdata *conn, X509 *server_cert)
              type itself: for example for an IA5String the data will be ASCII"
 
              It has been however verified that in 0.9.6 and 0.9.7, IA5String
-             is always zero-terminated.
+             is always null-terminated.
           */
           if((altlen == strlen(altptr)) &&
              /* if this isn't true, there was an embedded zero in the name
diff --git a/lib/vtls/vtls.c b/lib/vtls/vtls.c
index dfe2601..c3a55fb 100644
--- a/lib/vtls/vtls.c
+++ b/lib/vtls/vtls.c
@@ -63,6 +63,7 @@
 #include "warnless.h"
 #include "curl_base64.h"
 #include "curl_printf.h"
+#include "strdup.h"
 
 /* The last #include files should be: */
 #include "curl_memory.h"
@@ -82,6 +83,44 @@
   else                                       \
     dest->var = NULL;
 
+#define CLONE_BLOB(var)                         \
+  if(blobdup(&dest->var, source->var))         \
+    return FALSE;
+
+static CURLcode blobdup(struct curl_blob **dest,
+                        struct curl_blob *src)
+{
+  DEBUGASSERT(dest);
+  DEBUGASSERT(!*dest);
+  if(src) {
+    /* only if there's data to dupe! */
+    struct curl_blob *d;
+    d = malloc(sizeof(struct curl_blob) + src->len);
+    if(!d)
+      return CURLE_OUT_OF_MEMORY;
+    d->len = src->len;
+    /* Always duplicate because the connection may survive longer than the
+       handle that passed in the blob. */
+    d->flags = CURL_BLOB_COPY;
+    d->data = (void *)((char *)d + sizeof(struct curl_blob));
+    memcpy(d->data, src->data, src->len);
+    *dest = d;
+  }
+  return CURLE_OK;
+}
+
+/* returns TRUE if the blobs are identical */
+static bool blobcmp(struct curl_blob *first, struct curl_blob *second)
+{
+  if(!first && !second) /* both are NULL */
+    return TRUE;
+  if(!first || !second) /* one is NULL */
+    return FALSE;
+  if(first->len != second->len) /* different sizes */
+    return FALSE;
+  return !memcmp(first->data, second->data, first->len); /* same data */
+}
+
 bool
 Curl_ssl_config_matches(struct ssl_primary_config *data,
                         struct ssl_primary_config *needle)
@@ -91,6 +130,7 @@ Curl_ssl_config_matches(struct ssl_primary_config *data,
      (data->verifypeer == needle->verifypeer) &&
      (data->verifyhost == needle->verifyhost) &&
      (data->verifystatus == needle->verifystatus) &&
+     blobcmp(data->cert_blob, needle->cert_blob) &&
      Curl_safe_strcasecompare(data->CApath, needle->CApath) &&
      Curl_safe_strcasecompare(data->CAfile, needle->CAfile) &&
      Curl_safe_strcasecompare(data->clientcert, needle->clientcert) &&
@@ -115,6 +155,7 @@ Curl_clone_primary_ssl_config(struct ssl_primary_config *source,
   dest->verifystatus = source->verifystatus;
   dest->sessionid = source->sessionid;
 
+  CLONE_BLOB(cert_blob);
   CLONE_STRING(CApath);
   CLONE_STRING(CAfile);
   CLONE_STRING(clientcert);
@@ -137,6 +178,7 @@ void Curl_free_primary_ssl_config(struct ssl_primary_config *sslc)
   Curl_safefree(sslc->cipher_list);
   Curl_safefree(sslc->cipher_list13);
   Curl_safefree(sslc->pinned_key);
+  Curl_safefree(sslc->cert_blob);
 }
 
 #ifdef USE_SSL
@@ -706,7 +748,7 @@ CURLcode Curl_ssl_init_certinfo(struct Curl_easy *data, int num)
 }
 
 /*
- * 'value' is NOT a zero terminated string
+ * 'value' is NOT a null-terminated string
  */
 CURLcode Curl_ssl_push_certinfo_len(struct Curl_easy *data,
                                     int certnum,
@@ -728,10 +770,10 @@ CURLcode Curl_ssl_push_certinfo_len(struct Curl_easy *data,
   /* sprintf the label and colon */
   msnprintf(output, outlen, "%s:", label);
 
-  /* memcpy the value (it might not be zero terminated) */
+  /* memcpy the value (it might not be null-terminated) */
   memcpy(&output[labellen + 1], value, valuelen);
 
-  /* zero terminate the output */
+  /* null-terminate the output */
   output[labellen + 1 + valuelen] = 0;
 
   nl = Curl_slist_append_nodup(ci->certinfo[certnum], output);
-- 
cgit v0.12