From 9d51329047952ebfc2b944b7448b8f87f9e6ed51 Mon Sep 17 00:00:00 2001 From: Patrick Monnerat Date: Thu, 15 Sep 2022 13:30:09 +0200 Subject: [PATCH] setopt: use the handler table for protocol name to number conversions 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 | 83 +++++++++-------------------------------- lib/transfer.c | 2 +- lib/url.c | 12 ++++-- lib/url.h | 3 +- lib/urlapi.c | 12 +++--- tests/data/test1401 | 5 +++ tests/libtest/lib1597.c | 70 ++++++++++++++++++++++++---------- tests/libtest/lib1911.c | 1 + 8 files changed, 90 insertions(+), 98 deletions(-) diff --git a/lib/setopt.c b/lib/setopt.c index 7289a4e78b..bc98d199f8 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -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; } diff --git a/lib/transfer.c b/lib/transfer.c index fcc4006af3..441da73429 100644 --- a/lib/transfer.c +++ b/lib/transfer.c @@ -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); diff --git a/lib/url.c b/lib/url.c index 4a3a0e50c8..cb76a56de9 100644 --- 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)) { diff --git a/lib/url.h b/lib/url.h index e3b2940305..ba4270d523 100644 --- 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, diff --git a/lib/urlapi.c b/lib/urlapi.c index 2276b93dd4..c28960ac10 100644 --- a/lib/urlapi.c +++ b/lib/urlapi.c @@ -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 */ diff --git a/tests/data/test1401 b/tests/data/test1401 index e4bc3b7361..7edf76cc53 100644 --- a/tests/data/test1401 +++ b/tests/data/test1401 @@ -31,6 +31,11 @@ http --libcurl for GET with various options + +http +ftp +file + SSL_CERT_FILE= diff --git a/tests/libtest/lib1597.c b/tests/libtest/lib1597.c index d39fae6636..cb167e84fa 100644 --- a/tests/libtest/lib1597.c +++ b/tests/libtest/lib1597.c @@ -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; diff --git a/tests/libtest/lib1911.c b/tests/libtest/lib1911.c index e78f644848..97b45040fc 100644 --- a/tests/libtest/lib1911.c +++ b/tests/libtest/lib1911.c @@ -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 */