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);