From: Daniel Stenberg Date: Wed, 19 Feb 2025 07:49:54 +0000 (+0100) Subject: strparse: provide access functions X-Git-Tag: curl-8_13_0~422 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=f0d73181932d1048703ba309a6051c298c013c28;p=thirdparty%2Fcurl.git strparse: provide access functions To access the string and the length without having to directly use the struct field names. Gives more freedom, flexbility and keeps implementation specifics out of users' code. Closes #16386 --- diff --git a/docs/internals/STRPARSE.md b/docs/internals/STRPARSE.md index 0784bb9345..2ae92595e6 100644 --- a/docs/internals/STRPARSE.md +++ b/docs/internals/STRPARSE.md @@ -30,6 +30,19 @@ struct Curl_str { }; ~~~ +Access the struct fields with `Curl_str()` for the pointer and `Curl_strlen()` +for the length rather than using the struct fields directly. + +## `Curl_str_init` + +~~~c +void Curl_str_init(struct Curl_str *out) +~~~ + +This initiates a string struct. The parser functions that store info in +strings always init the string themselves, so this stand-alone use is often +not necessary. + ## `Curl_str_word` ~~~c diff --git a/lib/altsvc.c b/lib/altsvc.c index c3aaf1dad6..58416ea994 100644 --- a/lib/altsvc.c +++ b/lib/altsvc.c @@ -138,12 +138,14 @@ static struct altsvc *altsvc_create(struct Curl_str *srchost, size_t srcport, size_t dstport) { - enum alpnid dstalpnid = Curl_alpn2alpnid(dstalpn->str, dstalpn->len); - enum alpnid srcalpnid = Curl_alpn2alpnid(srcalpn->str, srcalpn->len); + enum alpnid dstalpnid = + Curl_alpn2alpnid(Curl_str(dstalpn), Curl_strlen(dstalpn)); + enum alpnid srcalpnid = + Curl_alpn2alpnid(Curl_str(srcalpn), Curl_strlen(srcalpn)); if(!srcalpnid || !dstalpnid) return NULL; - return altsvc_createid(srchost->str, srchost->len, - dsthost->str, dsthost->len, + return altsvc_createid(Curl_str(srchost), Curl_strlen(srchost), + Curl_str(dsthost), Curl_strlen(dsthost), srcalpnid, dstalpnid, srcport, dstport); } @@ -190,8 +192,8 @@ static CURLcode altsvc_add(struct altsvcinfo *asi, const char *line) /* The date parser works on a null terminated string. The maximum length is upheld by Curl_str_quotedword(). */ - memcpy(dbuf, date.str, date.len); - dbuf[date.len] = 0; + memcpy(dbuf, Curl_str(&date), Curl_strlen(&date)); + dbuf[Curl_strlen(&date)] = 0; expires = Curl_getdate_capped(dbuf); as = altsvc_create(&srchost, &dsthost, &srcalpn, &dstalpn, (size_t)srcport, (size_t)dstport); diff --git a/lib/cookie.c b/lib/cookie.c index 22a7681752..9ef7375327 100644 --- a/lib/cookie.c +++ b/lib/cookie.c @@ -507,15 +507,14 @@ parse_cookie_header(struct Curl_easy *data, Curl_str_trimblanks(&val); /* Reject cookies with a TAB inside the value */ - if(memchr(val.str, '\t', val.len)) { + if(memchr(Curl_str(&val), '\t', Curl_strlen(&val))) { infof(data, "cookie contains TAB, dropping"); return CERR_TAB; } } } else { - val.str = NULL; - val.len = 0; + Curl_str_init(&val); } /* @@ -523,10 +522,11 @@ parse_cookie_header(struct Curl_easy *data, * combination of name + contents. Chrome and Firefox support 4095 or * 4096 bytes combo */ - if(name.len >= (MAX_NAME-1) || val.len >= (MAX_NAME-1) || - ((name.len + val.len) > MAX_NAME)) { + if(Curl_strlen(&name) >= (MAX_NAME-1) || + Curl_strlen(&val) >= (MAX_NAME-1) || + ((Curl_strlen(&name) + Curl_strlen(&val)) > MAX_NAME)) { infof(data, "oversized cookie dropped, name/val %zu + %zu bytes", - name.len, val.len); + Curl_strlen(&name), Curl_strlen(&val)); return CERR_TOO_BIG; } @@ -536,9 +536,9 @@ parse_cookie_header(struct Curl_easy *data, * "the rest". Prefixes must start with '__' and end with a '-', so * only test for names where that can possibly be true. */ - if(strncasecompare("__Secure-", name.str, 9)) + if(strncasecompare("__Secure-", Curl_str(&name), 9)) co->prefix_secure = TRUE; - else if(strncasecompare("__Host-", name.str, 7)) + else if(strncasecompare("__Host-", Curl_str(&name), 7)) co->prefix_host = TRUE; /* @@ -553,8 +553,8 @@ parse_cookie_header(struct Curl_easy *data, /* Bad name/value pair. */ return CERR_NO_SEP; - strstore(&co->name, name.str, name.len); - strstore(&co->value, val.str, val.len); + strstore(&co->name, Curl_str(&name), Curl_strlen(&name)); + strstore(&co->value, Curl_str(&val), Curl_strlen(&val)); done = TRUE; if(!co->name || !co->value) return CERR_NO_NAME_VALUE; @@ -564,7 +564,7 @@ parse_cookie_header(struct Curl_easy *data, return CERR_INVALID_OCTET; } } - else if(!val.len) { + else if(!Curl_strlen(&val)) { /* * this was a "=" with no content, and we must allow * 'secure' and 'httponly' specified this weirdly @@ -592,7 +592,7 @@ parse_cookie_header(struct Curl_easy *data, if(done) ; else if(Curl_str_casecompare(&name, "path")) { - strstore(&co->path, val.str, val.len); + strstore(&co->path, Curl_str(&val), Curl_strlen(&val)); if(!co->path) return CERR_OUT_OF_MEMORY; free(co->spath); /* if this is set again */ @@ -600,15 +600,15 @@ parse_cookie_header(struct Curl_easy *data, if(!co->spath) return CERR_OUT_OF_MEMORY; } - else if(Curl_str_casecompare(&name, "domain") && val.len) { + else if(Curl_str_casecompare(&name, "domain") && Curl_strlen(&val)) { bool is_ip; - + const char *v = Curl_str(&val); /* * Now, we make sure that our host is within the given domain, or * the given domain is not valid and thus cannot be set. */ - if('.' == val.str[0]) + if('.' == *v) Curl_str_nudge(&val, 1); #ifndef USE_LIBPSL @@ -617,17 +617,18 @@ parse_cookie_header(struct Curl_easy *data, * TLD or otherwise "protected" suffix. To reduce risk, we require a * dot OR the exact hostname being "localhost". */ - if(bad_domain(val.str, val.len)) + if(bad_domain(Curl_str(&val), Curl_strlen(&val))) domain = ":"; #endif - is_ip = Curl_host_is_ipnum(domain ? domain : val.str); + is_ip = Curl_host_is_ipnum(domain ? domain : Curl_str(&val)); if(!domain - || (is_ip && !strncmp(val.str, domain, val.len) && - (val.len == strlen(domain))) - || (!is_ip && cookie_tailmatch(val.str, val.len, domain))) { - strstore(&co->domain, val.str, val.len); + || (is_ip && !strncmp(Curl_str(&val), domain, Curl_strlen(&val)) && + (Curl_strlen(&val) == strlen(domain))) + || (!is_ip && cookie_tailmatch(Curl_str(&val), + Curl_strlen(&val), domain))) { + strstore(&co->domain, Curl_str(&val), Curl_strlen(&val)); if(!co->domain) return CERR_OUT_OF_MEMORY; @@ -641,14 +642,14 @@ parse_cookie_header(struct Curl_easy *data, * not a domain to which the current host belongs. Mark as bad. */ infof(data, "skipped cookie with bad tailmatch domain: %s", - val.str); + Curl_str(&val)); return CERR_NO_TAILMATCH; } } else if(Curl_str_casecompare(&name, "version")) { /* just ignore */ } - else if(Curl_str_casecompare(&name, "max-age") && val.len) { + else if(Curl_str_casecompare(&name, "max-age") && Curl_strlen(&val)) { /* * Defined in RFC2109: * @@ -659,7 +660,7 @@ parse_cookie_header(struct Curl_easy *data, * cookie should be discarded immediately. */ int rc; - const char *maxage = val.str; + const char *maxage = Curl_str(&val); if(*maxage == '\"') maxage++; rc = Curl_str_number(&maxage, &co->expires, CURL_OFF_T_MAX); @@ -686,8 +687,8 @@ parse_cookie_header(struct Curl_easy *data, } cap_expires(now, co); } - else if(Curl_str_casecompare(&name, "expires") && val.len) { - if(!co->expires && (val.len < MAX_DATE_LENGTH)) { + else if(Curl_str_casecompare(&name, "expires") && Curl_strlen(&val)) { + if(!co->expires && (Curl_strlen(&val) < MAX_DATE_LENGTH)) { /* * Let max-age have priority. * @@ -695,8 +696,8 @@ parse_cookie_header(struct Curl_easy *data, * will be treated as a session cookie */ char dbuf[MAX_DATE_LENGTH + 1]; - memcpy(dbuf, val.str, val.len); - dbuf[val.len] = 0; + memcpy(dbuf, Curl_str(&val), Curl_strlen(&val)); + dbuf[Curl_strlen(&val)] = 0; co->expires = Curl_getdate_capped(dbuf); /* diff --git a/lib/curl_trc.c b/lib/curl_trc.c index fb3654e53b..2dcd6a974d 100644 --- a/lib/curl_trc.c +++ b/lib/curl_trc.c @@ -394,7 +394,7 @@ static CURLcode trc_opt(const char *config) struct Curl_str out; while(!Curl_str_until(&config, &out, 32, ',')) { int lvl = CURL_LOG_LVL_INFO; - const char *token = out.str; + const char *token = Curl_str(&out); if(*token == '-') { lvl = CURL_LOG_LVL_NONE; diff --git a/lib/hostip.c b/lib/hostip.c index 9d0dd10404..e0589adbfb 100644 --- a/lib/hostip.c +++ b/lib/hostip.c @@ -1150,7 +1150,8 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data) if(!Curl_str_number(&host, &num, 0xffff)) { /* Create an entry id, based upon the hostname and port */ - entry_len = create_hostcache_id(source.str, source.len, (int)num, + entry_len = create_hostcache_id(Curl_str(&source), + Curl_strlen(&source), (int)num, entry_id, sizeof(entry_id)); if(data->share) Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); @@ -1224,11 +1225,11 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data) } #endif - if(target.len >= sizeof(address)) + if(Curl_strlen(&target) >= sizeof(address)) goto err; - memcpy(address, target.str, target.len); - address[target.len] = '\0'; + memcpy(address, Curl_str(&target), Curl_strlen(&target)); + address[Curl_strlen(&target)] = '\0'; ai = Curl_str2addr(address, (int)port); if(!ai) { @@ -1260,7 +1261,8 @@ err: } /* Create an entry id, based upon the hostname and port */ - entry_len = create_hostcache_id(source.str, source.len, (int)port, + entry_len = create_hostcache_id(Curl_str(&source), Curl_strlen(&source), + (int)port, entry_id, sizeof(entry_id)); if(data->share) @@ -1271,7 +1273,8 @@ err: if(dns) { infof(data, "RESOLVE %.*s:%" CURL_FORMAT_CURL_OFF_T - " - old addresses discarded", (int)source.len, source.str, port); + " - old addresses discarded", (int)Curl_strlen(&source), + Curl_str(&source), port); /* delete old entry, there are two reasons for this 1. old entry may have different addresses. 2. even if entry with correct addresses is already in the cache, @@ -1287,8 +1290,8 @@ err: } /* put this new host in the cache */ - dns = Curl_cache_addr(data, head, source.str, source.len, (int)port, - permanent); + dns = Curl_cache_addr(data, head, Curl_str(&source), + Curl_strlen(&source), (int)port, permanent); if(dns) { /* release the returned reference; the cache itself will keep the * entry alive: */ @@ -1304,12 +1307,12 @@ err: } #ifndef CURL_DISABLE_VERBOSE_STRINGS infof(data, "Added %.*s:%" CURL_FORMAT_CURL_OFF_T ":%s to DNS cache%s", - (int)source.len, source.str, port, addresses, + (int)Curl_strlen(&source), Curl_str(&source), port, addresses, permanent ? "" : " (non-permanent)"); #endif /* Wildcard hostname */ - if((source.len == 1) && (source.str[0] == '*')) { + if(Curl_str_casecompare(&source, "*")) { infof(data, "RESOLVE *:%" CURL_FORMAT_CURL_OFF_T " using wildcard", port); data->state.wildcard_resolve = TRUE; diff --git a/lib/hsts.c b/lib/hsts.c index 7fb3652598..9ea4789e77 100644 --- a/lib/hsts.c +++ b/lib/hsts.c @@ -433,26 +433,26 @@ static CURLcode hsts_add(struct hsts *h, const char *line) struct stsentry *e; char dbuf[MAX_HSTS_DATELEN + 1]; time_t expires; + const char *hp = Curl_str(&host); /* The date parser works on a null terminated string. The maximum length is upheld by Curl_str_quotedword(). */ - memcpy(dbuf, date.str, date.len); - dbuf[date.len] = 0; + memcpy(dbuf, Curl_str(&date), Curl_strlen(&date)); + dbuf[Curl_strlen(&date)] = 0; expires = strcmp(dbuf, UNLIMITED) ? Curl_getdate_capped(dbuf) : TIME_T_MAX; - if(host.str[0] == '.') { - host.str++; - host.len--; + if(hp[0] == '.') { + Curl_str_nudge(&host, 1); subdomain = TRUE; } /* only add it if not already present */ - e = Curl_hsts(h, host.str, host.len, subdomain); + e = Curl_hsts(h, Curl_str(&host), Curl_strlen(&host), subdomain); if(!e) - result = hsts_create(h, host.str, host.len, subdomain, expires); - else if((strlen(e->host) == host.len) && - strncasecompare(host.str, e->host, host.len)) { + result = hsts_create(h, Curl_str(&host), Curl_strlen(&host), + subdomain, expires); + else if(Curl_str_casecompare(&host, e->host)) { /* the same hostname, use the largest expire time */ if(expires > e->expires) e->expires = expires; diff --git a/lib/http_aws_sigv4.c b/lib/http_aws_sigv4.c index 97835eac31..4ed55f0cee 100644 --- a/lib/http_aws_sigv4.c +++ b/lib/http_aws_sigv4.c @@ -652,7 +652,7 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) /* nothing to do */ } - if(!service.len) { + if(!Curl_strlen(&service)) { const char *p = hostname; if(Curl_str_until(&p, &service, MAX_SIGV4_LEN, '.') || Curl_str_single(&p, '.')) { @@ -662,9 +662,9 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) } infof(data, "aws_sigv4: picked service %.*s from host", - (int)service.len, service.str); + (int)Curl_strlen(&service), Curl_str(&service)); - if(!region.len) { + if(!Curl_strlen(®ion)) { if(Curl_str_until(&p, ®ion, MAX_SIGV4_LEN, '.') || Curl_str_single(&p, '.')) { failf(data, "aws-sigv4: region missing in parameters and hostname"); @@ -672,14 +672,15 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) goto fail; } infof(data, "aws_sigv4: picked region %.*s from host", - (int)region.len, region.str); + (int)Curl_strlen(®ion), Curl_str(®ion)); } } Curl_http_method(data, conn, &method, &httpreq); - payload_hash = parse_content_sha_hdr(data, provider1.str, provider1.len, - &payload_hash_len); + payload_hash = + parse_content_sha_hdr(data, Curl_str(&provider1), Curl_strlen(&provider1), + &payload_hash_len); if(!payload_hash) { /* AWS S3 requires a x-amz-content-sha256 header, and supports special @@ -688,9 +689,9 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) Curl_str_casecompare(&service, "s3"); if(sign_as_s3) - result = calc_s3_payload_hash(data, httpreq, - provider1.str, provider1.len, - sha_hash, sha_hex, content_sha256_hdr); + result = calc_s3_payload_hash(data, httpreq, Curl_str(&provider1), + Curl_strlen(&provider1), sha_hash, sha_hex, + content_sha256_hdr); else result = calc_payload_hash(data, sha_hash, sha_hex); if(result) @@ -722,7 +723,7 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) } result = make_headers(data, hostname, timestamp, - provider1.str, provider1.len, + Curl_str(&provider1), Curl_strlen(&provider1), &date_header, content_sha256_hdr, &canonical_headers, &signed_headers); if(result) @@ -767,17 +768,18 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) DEBUGF(infof(data, "Canonical request: %s", canonical_request)); - request_type = aprintf("%.*s4_request", (int)provider0.len, provider0.str); + request_type = aprintf("%.*s4_request", + (int)Curl_strlen(&provider0), Curl_str(&provider0)); if(!request_type) goto fail; /* provider0 is lowercased *after* aprintf() so that the buffer can be written to */ - Curl_strntolower(request_type, request_type, provider0.len); + Curl_strntolower(request_type, request_type, Curl_strlen(&provider0)); - credential_scope = aprintf("%s/%.*s/%.*s/%s", - date, (int)region.len, region.str, - (int)service.len, service.str, + credential_scope = aprintf("%s/%.*s/%.*s/%s", date, + (int)Curl_strlen(®ion), Curl_str(®ion), + (int)Curl_strlen(&service), Curl_str(&service), request_type); if(!credential_scope) goto fail; @@ -796,7 +798,7 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) "%s\n" /* RequestDateTime */ "%s\n" /* CredentialScope */ "%s", /* HashedCanonicalRequest in hex */ - (int)provider0.len, provider0.str, + (int)Curl_strlen(&provider0), Curl_str(&provider0), timestamp, credential_scope, sha_hex); @@ -804,19 +806,21 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) goto fail; /* make provider0 part done uppercase */ - Curl_strntoupper(str_to_sign, provider0.str, provider0.len); + Curl_strntoupper(str_to_sign, Curl_str(&provider0), Curl_strlen(&provider0)); - secret = aprintf("%.*s4%s", (int)provider0.len, provider0.str, - data->state.aptr.passwd ? + secret = aprintf("%.*s4%s", (int)Curl_strlen(&provider0), + Curl_str(&provider0), data->state.aptr.passwd ? data->state.aptr.passwd : ""); if(!secret) goto fail; /* make provider0 part done uppercase */ - Curl_strntoupper(secret, provider0.str, provider0.len); + Curl_strntoupper(secret, Curl_str(&provider0), Curl_strlen(&provider0)); HMAC_SHA256(secret, strlen(secret), date, strlen(date), sign0); - HMAC_SHA256(sign0, sizeof(sign0), region.str, region.len, sign1); - HMAC_SHA256(sign1, sizeof(sign1), service.str, service.len, sign0); + HMAC_SHA256(sign0, sizeof(sign0), + Curl_str(®ion), Curl_strlen(®ion), sign1); + HMAC_SHA256(sign1, sizeof(sign1), + Curl_str(&service), Curl_strlen(&service), sign0); HMAC_SHA256(sign0, sizeof(sign0), request_type, strlen(request_type), sign1); HMAC_SHA256(sign1, sizeof(sign1), str_to_sign, strlen(str_to_sign), sign0); @@ -833,7 +837,7 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) */ "%s" "%s", /* optional sha256 header includes \r\n */ - (int)provider0.len, provider0.str, + (int)Curl_strlen(&provider0), Curl_str(&provider0), user, credential_scope, Curl_dyn_ptr(&signed_headers), @@ -845,7 +849,7 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) } /* provider 0 uppercase */ Curl_strntoupper(&auth_headers[sizeof("Authorization: ") - 1], - provider0.str, provider0.len); + Curl_str(&provider0), Curl_strlen(&provider0)); Curl_safefree(data->state.aptr.userpwd); data->state.aptr.userpwd = auth_headers; diff --git a/lib/strparse.c b/lib/strparse.c index 5bfbdcbe0e..076b91d69f 100644 --- a/lib/strparse.c +++ b/lib/strparse.c @@ -25,6 +25,12 @@ #include "strparse.h" #include "strcase.h" +void Curl_str_init(struct Curl_str *out) +{ + out->str = NULL; + out->len = 0; +} + /* Get a word until the first DELIM or end of string. At least one byte long. return non-zero on error */ int Curl_str_until(const char **linep, struct Curl_str *out, @@ -34,8 +40,7 @@ int Curl_str_until(const char **linep, struct Curl_str *out, size_t len = 0; DEBUGASSERT(linep && *linep && out && max && delim); - out->str = NULL; - out->len = 0; + Curl_str_init(out); while(*s && (*s != delim)) { s++; if(++len > max) { @@ -68,8 +73,7 @@ int Curl_str_quotedword(const char **linep, struct Curl_str *out, size_t len = 0; DEBUGASSERT(linep && *linep && out && max); - out->str = NULL; - out->len = 0; + Curl_str_init(out); if(*s != '\"') return STRE_BEGQUOTE; s++; @@ -226,8 +230,7 @@ int Curl_str_cspn(const char **linep, struct Curl_str *out, const char *reject) *linep = &s[len]; return STRE_OK; } - out->str = NULL; - out->len = 0; + Curl_str_init(out); return STRE_SHORT; } diff --git a/lib/strparse.h b/lib/strparse.h index 41aa3fed10..f096526452 100644 --- a/lib/strparse.h +++ b/lib/strparse.h @@ -35,11 +35,18 @@ #define STRE_OVERFLOW 7 #define STRE_NO_NUM 8 +/* public struct, but all accesses should be done using the provided + functions */ struct Curl_str { const char *str; size_t len; }; +void Curl_str_init(struct Curl_str *out); + +#define Curl_str(x) ((x)->str) +#define Curl_strlen(x) ((x)->len) + /* Get a word until the first space return non-zero on error */ int Curl_str_word(const char **linep, struct Curl_str *out, const size_t max);