]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
setopt: use the handler table for protocol name to number conversions
authorPatrick Monnerat <patrick@monnerat.net>
Thu, 15 Sep 2022 11:30:09 +0000 (13:30 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Fri, 16 Sep 2022 21:29:01 +0000 (23:29 +0200)
This also returns error CURLE_UNSUPPORTED_PROTOCOL rather than
CURLE_BAD_FUNCTION_ARGUMENT when a listed protocol name is not found.

A new schemelen parameter is added to Curl_builtin_scheme() to support
this extended use.

Note that disabled protocols are not recognized anymore.

Tests adapted accordingly.

Closes #9472

lib/setopt.c
lib/transfer.c
lib/url.c
lib/url.h
lib/urlapi.c
tests/data/test1401
tests/libtest/lib1597.c
tests/libtest/lib1911.c

index 7289a4e78bdd0bb3a9eda049edb6b0597d369192..bc98d199f8c5de499ca7b8e8432c0f6ee2e82082 100644 (file)
@@ -148,85 +148,36 @@ static CURLcode setstropt_userpwd(char *option, char **userp, char **passwdp)
 #define C_SSLVERSION_VALUE(x) (x & 0xffff)
 #define C_SSLVERSION_MAX_VALUE(x) (x & 0xffff0000)
 
-static CURLcode protocol2num(char *str, curl_prot_t *val)
+static CURLcode protocol2num(const char *str, curl_prot_t *val)
 {
-  bool found_comma = FALSE;
-  static struct scheme {
-    const char *name;
-    curl_prot_t bit;
-  } const protos[] = {
-    { "dict", CURLPROTO_DICT },
-    { "file", CURLPROTO_FILE },
-    { "ftp", CURLPROTO_FTP },
-    { "ftps", CURLPROTO_FTPS },
-    { "gopher", CURLPROTO_GOPHER },
-    { "gophers", CURLPROTO_GOPHERS },
-    { "http", CURLPROTO_HTTP },
-    { "https", CURLPROTO_HTTPS },
-    { "imap", CURLPROTO_IMAP },
-    { "imaps", CURLPROTO_IMAPS },
-    { "ldap", CURLPROTO_LDAP },
-    { "ldaps", CURLPROTO_LDAPS },
-    { "mqtt", CURLPROTO_MQTT },
-    { "pop3", CURLPROTO_POP3 },
-    { "pop3s", CURLPROTO_POP3S },
-    { "rtmp", CURLPROTO_RTMP },
-    { "rtmpe", CURLPROTO_RTMPE },
-    { "rtmps", CURLPROTO_RTMPS },
-    { "rtmpt", CURLPROTO_RTMPT },
-    { "rtmpte", CURLPROTO_RTMPTE },
-    { "rtmpts", CURLPROTO_RTMPTS },
-    { "rtsp", CURLPROTO_RTSP },
-    { "scp", CURLPROTO_SCP },
-    { "sftp", CURLPROTO_SFTP },
-    { "smb", CURLPROTO_SMB },
-    { "smbs", CURLPROTO_SMBS },
-    { "smtp", CURLPROTO_SMTP },
-    { "smtps", CURLPROTO_SMTPS },
-    { "telnet", CURLPROTO_TELNET },
-    { "tftp", CURLPROTO_TFTP },
-#ifdef USE_WEBSOCKETS
-    { "ws", CURLPROTO_WS },
-    { "wss", CURLPROTO_WSS },
-#endif
-    { NULL, 0 }
-  };
-
   if(!str)
     return CURLE_BAD_FUNCTION_ARGUMENT;
-  else if(curl_strequal(str, "all")) {
-    *val = (curl_prot_t)~0;
+
+  if(curl_strequal(str, "all")) {
+    *val = ~(curl_prot_t) 0;
     return CURLE_OK;
   }
 
   *val = 0;
 
   do {
+    const char *token = str;
     size_t tlen;
-    struct scheme const *pp;
-    char *token;
-    token = strchr(str, ',');
-    found_comma = token ? TRUE : FALSE;
-    if(!token)
-      token = strchr(str, '\0');
-    tlen = token - str;
+
+    str = strchr(str, ',');
+    tlen = str? (size_t) (str - token): strlen(token);
     if(tlen) {
-      for(pp = protos; pp->name; pp++) {
-        if((strlen(pp->name) == tlen) &&
-           curl_strnequal(str, pp->name, tlen)) {
-          *val |= pp->bit;
-          break;
-        }
-      }
-      if(!(pp->name))
-        /* protocol name didn't match */
-        return CURLE_BAD_FUNCTION_ARGUMENT;
+      const struct Curl_handler *h = Curl_builtin_scheme(token, tlen);
+
+      if(!h)
+        return CURLE_UNSUPPORTED_PROTOCOL;
+
+      *val |= h->protocol;
     }
-    if(found_comma)
-      str = token + 1;
-  } while(found_comma);
+  } while(str++);
+
   if(!*val)
-    /* no matching protocol */
+    /* no protocol listed */
     return CURLE_BAD_FUNCTION_ARGUMENT;
   return CURLE_OK;
 }
index fcc4006af3ac5e5d90e48be140fc4b1c6ccc1829..441da7342915c1f4e0b9b926b602ef6e5bd36e75 100644 (file)
@@ -1700,7 +1700,7 @@ CURLcode Curl_follow(struct Curl_easy *data,
           return Curl_uc_to_curlcode(uc);
         }
 
-        p = Curl_builtin_scheme(scheme);
+        p = Curl_builtin_scheme(scheme, CURL_ZERO_TERMINATED);
         if(p && (p->protocol != data->info.conn_protocol)) {
           infof(data, "Clear auth, redirects scheme from %s to %s",
                 data->info.conn_scheme, scheme);
index 4a3a0e50c850cbb3830dac6439fa8557b306ef84..cb76a56de928dd84f1ba40bf7fb3163b98482792 100644 (file)
--- a/lib/url.c
+++ b/lib/url.c
@@ -1853,15 +1853,18 @@ static struct connectdata *allocate_conn(struct Curl_easy *data)
 }
 
 /* returns the handler if the given scheme is built-in */
-const struct Curl_handler *Curl_builtin_scheme(const char *scheme)
+const struct Curl_handler *Curl_builtin_scheme(const char *scheme,
+                                               size_t schemelen)
 {
   const struct Curl_handler * const *pp;
   const struct Curl_handler *p;
   /* Scan protocol handler table and match against 'scheme'. The handler may
      be changed later when the protocol specific setup function is called. */
+  if(schemelen == CURL_ZERO_TERMINATED)
+    schemelen = strlen(scheme);
   for(pp = protocols; (p = *pp) != NULL; pp++)
-    if(strcasecompare(p->scheme, scheme))
-      /* Protocol found in table. Check if allowed */
+    if(strncasecompare(p->scheme, scheme, schemelen) && !p->scheme[schemelen])
+      /* Protocol found in table. */
       return p;
   return NULL; /* not found */
 }
@@ -1871,7 +1874,8 @@ static CURLcode findprotocol(struct Curl_easy *data,
                              struct connectdata *conn,
                              const char *protostr)
 {
-  const struct Curl_handler *p = Curl_builtin_scheme(protostr);
+  const struct Curl_handler *p = Curl_builtin_scheme(protostr,
+                                                     CURL_ZERO_TERMINATED);
 
   if(p && /* Protocol found in table. Check if allowed */
      (data->set.allowed_protocols & p->protocol)) {
index e3b2940305d68d64471b1b4194a72975824e7384..ba4270d5238efecdbefbae1c7372793ebaf59d04 100644 (file)
--- a/lib/url.h
+++ b/lib/url.h
@@ -46,7 +46,8 @@ CURLcode Curl_parse_login_details(const char *login, const size_t len,
                                   char **userptr, char **passwdptr,
                                   char **optionsptr);
 
-const struct Curl_handler *Curl_builtin_scheme(const char *scheme);
+const struct Curl_handler *Curl_builtin_scheme(const char *scheme,
+                                               size_t schemelen);
 
 bool Curl_is_ASCII_name(const char *hostname);
 CURLcode Curl_idnconvert_hostname(struct Curl_easy *data,
index 2276b93dd4315d4f353e3274761ba780fe066df0..c28960ac107b807da1e1f293b9a1bcb0db285363 100644 (file)
@@ -431,7 +431,7 @@ static CURLUcode parse_hostname_login(struct Curl_URL *u,
 
   /* if this is a known scheme, get some details */
   if(u->scheme)
-    h = Curl_builtin_scheme(u->scheme);
+    h = Curl_builtin_scheme(u->scheme, CURL_ZERO_TERMINATED);
 
   /* We could use the login information in the URL so extract it. Only parse
      options if the handler says we should. Note that 'h' might be NULL! */
@@ -1071,7 +1071,7 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags)
       }
 
       schemep = schemebuf;
-      if(!Curl_builtin_scheme(schemep) &&
+      if(!Curl_builtin_scheme(schemep, CURL_ZERO_TERMINATED) &&
          !(flags & CURLU_NON_SUPPORT_SCHEME)) {
         result = CURLUE_UNSUPPORTED_SCHEME;
         goto fail;
@@ -1412,7 +1412,7 @@ CURLUcode curl_url_get(CURLU *u, CURLUPart what,
       /* there's no stored port number, but asked to deliver
          a default one for the scheme */
       const struct Curl_handler *h =
-        Curl_builtin_scheme(u->scheme);
+        Curl_builtin_scheme(u->scheme, CURL_ZERO_TERMINATED);
       if(h) {
         msnprintf(portbuf, sizeof(portbuf), "%u", h->defport);
         ptr = portbuf;
@@ -1422,7 +1422,7 @@ CURLUcode curl_url_get(CURLU *u, CURLUPart what,
       /* there is a stored port number, but ask to inhibit if
          it matches the default one for the scheme */
       const struct Curl_handler *h =
-        Curl_builtin_scheme(u->scheme);
+        Curl_builtin_scheme(u->scheme, CURL_ZERO_TERMINATED);
       if(h && (h->defport == u->portnum) &&
          (flags & CURLU_NO_DEFAULT_PORT))
         ptr = NULL;
@@ -1468,7 +1468,7 @@ CURLUcode curl_url_get(CURLU *u, CURLUPart what,
       else
         return CURLUE_NO_SCHEME;
 
-      h = Curl_builtin_scheme(scheme);
+      h = Curl_builtin_scheme(scheme, CURL_ZERO_TERMINATED);
       if(!port && (flags & CURLU_DEFAULT_PORT)) {
         /* there's no stored port number, but asked to deliver
            a default one for the scheme */
@@ -1674,7 +1674,7 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
       return CURLUE_BAD_SCHEME;
     if(!(flags & CURLU_NON_SUPPORT_SCHEME) &&
        /* verify that it is a fine scheme */
-       !Curl_builtin_scheme(part))
+       !Curl_builtin_scheme(part, CURL_ZERO_TERMINATED))
       return CURLUE_UNSUPPORTED_SCHEME;
     storep = &u->scheme;
     urlencode = FALSE; /* never */
index e4bc3b7361b7d98052f0a54d5da7e7bf58caf712..7edf76cc53daf62277ec1c9301dc984425f984ee 100644 (file)
@@ -31,6 +31,11 @@ http
  <name>
 --libcurl for GET with various options
  </name>
+<features>
+http
+ftp
+file
+</features>
 <setenv>
 SSL_CERT_FILE=
 </setenv>
index d39fae663645b01af1d80cafb68ce2b3bd1db853..cb167e84fa7e944d9cb0e1e1de99ab1cc059fc38 100644 (file)
@@ -30,7 +30,7 @@
 
 struct pair {
   const char *in;
-  CURLcode exp;
+  CURLcode *exp;
 };
 
 int test(char *URL)
@@ -38,27 +38,34 @@ int test(char *URL)
   CURL *curl = NULL;
   int res = 0;
   CURLcode result = CURLE_OK;
+  CURLcode ok = CURLE_OK;
+  CURLcode bad = CURLE_BAD_FUNCTION_ARGUMENT;
+  CURLcode unsup = CURLE_UNSUPPORTED_PROTOCOL;
+  CURLcode httpcode = CURLE_UNSUPPORTED_PROTOCOL;
+  CURLcode httpscode = CURLE_UNSUPPORTED_PROTOCOL;
+  curl_version_info_data *curlinfo;
+  const char *const *proto;
+  char protolist[1024];
+  int n;
   int i;
 
   struct pair prots[] = {
-    {"goobar", CURLE_BAD_FUNCTION_ARGUMENT},
-    {"http ", CURLE_BAD_FUNCTION_ARGUMENT},
-    {" http", CURLE_BAD_FUNCTION_ARGUMENT},
-    {"http", CURLE_OK},
-    {"http,", CURLE_OK},
-    {"https,", CURLE_OK},
-    {"https,http", CURLE_OK},
-    {"http,http", CURLE_OK},
-    {"HTTP,HTTP", CURLE_OK},
-    {",HTTP,HTTP", CURLE_OK},
-    {"http,http,ft", CURLE_BAD_FUNCTION_ARGUMENT},
-    {"", CURLE_BAD_FUNCTION_ARGUMENT},
-    {",,", CURLE_BAD_FUNCTION_ARGUMENT},
-    {"DICT,FILE,FTP,FTPS,GOPHER,GOPHERS,HTTP,HTTPS,IMAP,IMAPS,LDAP,LDAPS,"
-     "POP3,POP3S,RTMP,RTMPE,RTMPS,RTMPT,RTMPTE,RTMPTS,RTSP,SCP,SFTP,SMB,"
-     "SMBS,SMTP,SMTPS,TELNET,TFTP", CURLE_OK},
-    {"all", CURLE_OK},
-    {NULL, CURLE_OK},
+    {"goobar", &unsup},
+    {"http ", &unsup},
+    {" http", &unsup},
+    {"http", &httpcode},
+    {"http,", &httpcode},
+    {"https,", &httpscode},
+    {"https,http", &httpscode},
+    {"http,http", &httpcode},
+    {"HTTP,HTTP", &httpcode},
+    {",HTTP,HTTP", &httpcode},
+    {"http,http,ft", &unsup},
+    {"", &bad},
+    {",,", &bad},
+    {protolist, &ok},
+    {"all", &ok},
+    {NULL, NULL},
   };
   (void)URL;
 
@@ -66,9 +73,32 @@ int test(char *URL)
 
   easy_init(curl);
 
+  /* Get enabled protocols.*/
+  curlinfo = curl_version_info(CURLVERSION_NOW);
+  if(!curlinfo) {
+    fputs("curl_version_info failed\n", stderr);
+    res = (int) TEST_ERR_FAILURE;
+    goto test_cleanup;
+  }
+
+  n = 0;
+  for(proto = curlinfo->protocols; *proto; proto++) {
+    if((size_t) n >= sizeof(protolist)) {
+      puts("protolist buffer too small\n");
+      res = (int) TEST_ERR_FAILURE;
+      goto test_cleanup;
+    }
+    n += msnprintf(protolist + n, sizeof(protolist) - n, ",%s", *proto);
+    if(curl_strequal(*proto, "http"))
+      httpcode = CURLE_OK;
+    if(curl_strequal(*proto, "https"))
+      httpscode = CURLE_OK;
+  }
+
+  /* Run the tests. */
   for(i = 0; prots[i].in; i++) {
     result = curl_easy_setopt(curl, CURLOPT_PROTOCOLS_STR, prots[i].in);
-    if(result != prots[i].exp) {
+    if(result != *prots[i].exp) {
       printf("unexpectedly '%s' returned %u\n",
              prots[i].in, result);
       break;
index e78f64484866eb48afe066a548a1ba091c80ac7a..97b45040fcfd3528624487a2bbd7e7468e9d2dd1 100644 (file)
@@ -79,6 +79,7 @@ int test(char *URL)
       case CURLE_BAD_FUNCTION_ARGUMENT: /* the most normal */
       case CURLE_UNKNOWN_OPTION: /* left out from the build */
       case CURLE_NOT_BUILT_IN: /* not supported */
+      case CURLE_UNSUPPORTED_PROTOCOL: /* detected by protocol2num() */
         break;
       default:
         /* all other return codes are unexpected */