mirror of
https://github.com/curl/curl.git
synced 2025-04-12 16:20:35 +08:00
strparse: string parsing helper functions
Designed to aid converting off from sscanf parsers. sscanf is hard to use right, easy to mess up and often makes for sloppy error checking. The new parsers allow more exact and pedandic parsing. This new set of functions should be possible to use (and extend) and switch over other libcurl parser code to use going forward. Adapts the following to use the new functions: - altsvc.c - hsts.c - http_aws_sigv4.c Bonus: fewer memory copies, fewer stack buffers. Test: Unit test1664 Docs: docs/internals/STRPARSE.md Closes #15692
This commit is contained in:
parent
ff5091aa9f
commit
d5c738c608
@ -56,6 +56,7 @@ INTERNALDOCS = \
|
||||
internals/NEW-PROTOCOL.md \
|
||||
internals/README.md \
|
||||
internals/SPLAY.md \
|
||||
internals/STRPARSE.md \
|
||||
internals/WEBSOCKET.md
|
||||
|
||||
EXTRA_DIST = \
|
||||
|
109
docs/internals/STRPARSE.md
Normal file
109
docs/internals/STRPARSE.md
Normal file
@ -0,0 +1,109 @@
|
||||
<!--
|
||||
Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
|
||||
SPDX-License-Identifier: curl
|
||||
-->
|
||||
|
||||
# String parsing with `strparse`
|
||||
|
||||
The functions take input via a pointer to a pointer, which allows the
|
||||
functions to advance the pointer on success which then by extension allows
|
||||
"chaining" of functions like this example that gets a word, a space and then a
|
||||
second word:
|
||||
|
||||
~~~c
|
||||
if(Curl_str_word(&line, &word1, MAX) ||
|
||||
Curl_str_singlespace(&line) ||
|
||||
Curl_str_word(&line, &word2, MAX))
|
||||
fprintf(stderr, "ERROR\n");
|
||||
~~~
|
||||
|
||||
## Strings
|
||||
|
||||
The functions that return string information does so by populating a
|
||||
`struct Curl_str`:
|
||||
|
||||
~~~c
|
||||
struct Curl_str {
|
||||
char *str;
|
||||
size_t len;
|
||||
};
|
||||
~~~
|
||||
|
||||
## `Curl_str_word`
|
||||
|
||||
~~~c
|
||||
int Curl_str_word(char **linep, struct Curl_str *out, const size_t max);
|
||||
~~~
|
||||
|
||||
Get a sequence of bytes until the first space or the end of the string. Return
|
||||
non-zero on error. There is no way to include a space in the word, no sort of
|
||||
escaping. The word must be at least one byte, otherwise it is considered an
|
||||
error.
|
||||
|
||||
`max` is the longest accepted word, or it returns error.
|
||||
|
||||
On a successful return, `linep` is updated to point to the byte immediately
|
||||
following the parsed word.
|
||||
|
||||
## `Curl_str_until`
|
||||
|
||||
~~~c
|
||||
int Curl_str_until(char **linep, struct Curl_str *out, const size_t max,
|
||||
char delim);
|
||||
~~~
|
||||
|
||||
Like `Curl_str_word` but instead of parsing to space, it parses to a given
|
||||
custom delimiter non-zero byte `delim`.
|
||||
|
||||
`max` is the longest accepted word, or it returns error.
|
||||
|
||||
The parsed word must be at least one byte, otherwise it is considered an
|
||||
error.
|
||||
|
||||
## `Curl_str_quotedword`
|
||||
|
||||
~~~c
|
||||
int Curl_str_quotedword(char **linep, struct Curl_str *out, const size_t max);
|
||||
~~~
|
||||
|
||||
Get a "quoted" word. This means everything that is provided within a leading
|
||||
and an ending double character. No escaping possible.
|
||||
|
||||
`max` is the longest accepted word, or it returns error.
|
||||
|
||||
The parsed word must be at least one byte, otherwise it is considered an
|
||||
error.
|
||||
|
||||
## `Curl_str_single`
|
||||
|
||||
~~~c
|
||||
int Curl_str_single(char **linep, char byte);
|
||||
~~~
|
||||
|
||||
Advance over a single character provided in `byte`. Return non-zero on error.
|
||||
|
||||
## `Curl_str_singlespace`
|
||||
|
||||
~~~c
|
||||
int Curl_str_singlespace(char **linep);
|
||||
~~~
|
||||
|
||||
Advance over a single ASCII space. Return non-zero on error.
|
||||
|
||||
## `Curl_str_number`
|
||||
|
||||
~~~c
|
||||
int Curl_str_number(char **linep, size_t *nump, size_t max);
|
||||
~~~
|
||||
|
||||
Get an unsigned decimal number. Leading zeroes are just swallowed. Return
|
||||
non-zero on error.
|
||||
|
||||
## `Curl_str_newline`
|
||||
|
||||
~~~c
|
||||
int Curl_str_newline(char **linep);
|
||||
~~~
|
||||
|
||||
Check for a single CR or LF. Return non-zero on error */
|
@ -223,6 +223,7 @@ LIB_CFILES = \
|
||||
strcase.c \
|
||||
strdup.c \
|
||||
strerror.c \
|
||||
strparse.c \
|
||||
strtok.c \
|
||||
strtoofft.c \
|
||||
system_win32.c \
|
||||
@ -358,6 +359,7 @@ LIB_HFILES = \
|
||||
strcase.h \
|
||||
strdup.h \
|
||||
strerror.h \
|
||||
strparse.h \
|
||||
strtok.h \
|
||||
strtoofft.h \
|
||||
system_win32.h \
|
||||
|
130
lib/altsvc.c
130
lib/altsvc.c
@ -40,6 +40,7 @@
|
||||
#include "rename.h"
|
||||
#include "strdup.h"
|
||||
#include "inet_pton.h"
|
||||
#include "strparse.h"
|
||||
|
||||
/* The last 3 #include files should be in this order */
|
||||
#include "curl_printf.h"
|
||||
@ -47,25 +48,26 @@
|
||||
#include "memdebug.h"
|
||||
|
||||
#define MAX_ALTSVC_LINE 4095
|
||||
#define MAX_ALTSVC_DATELENSTR "64"
|
||||
#define MAX_ALTSVC_DATELEN 64
|
||||
#define MAX_ALTSVC_HOSTLENSTR "512"
|
||||
#define MAX_ALTSVC_HOSTLEN 512
|
||||
#define MAX_ALTSVC_ALPNLENSTR "10"
|
||||
#define MAX_ALTSVC_DATELEN 256
|
||||
#define MAX_ALTSVC_HOSTLEN 2048
|
||||
#define MAX_ALTSVC_ALPNLEN 10
|
||||
|
||||
#define H3VERSION "h3"
|
||||
|
||||
static enum alpnid alpn2alpnid(char *name)
|
||||
static enum alpnid alpn2alpnid(char *name, size_t len)
|
||||
{
|
||||
if(strcasecompare(name, "h1"))
|
||||
return ALPN_h1;
|
||||
if(strcasecompare(name, "h2"))
|
||||
return ALPN_h2;
|
||||
if(strcasecompare(name, H3VERSION))
|
||||
return ALPN_h3;
|
||||
if(strcasecompare(name, "http/1.1"))
|
||||
return ALPN_h1;
|
||||
if(len == 2) {
|
||||
if(strncasecompare(name, "h1", 2))
|
||||
return ALPN_h1;
|
||||
if(strncasecompare(name, "h2", 2))
|
||||
return ALPN_h2;
|
||||
if(strncasecompare(name, "h3", 2))
|
||||
return ALPN_h3;
|
||||
}
|
||||
else if(len == 8) {
|
||||
if(strncasecompare(name, "http/1.1", 8))
|
||||
return ALPN_h1;
|
||||
}
|
||||
return ALPN_none; /* unknown, probably rubbish input */
|
||||
}
|
||||
|
||||
@ -93,18 +95,17 @@ static void altsvc_free(struct altsvc *as)
|
||||
}
|
||||
|
||||
static struct altsvc *altsvc_createid(const char *srchost,
|
||||
size_t hlen,
|
||||
const char *dsthost,
|
||||
size_t dlen, /* dsthost length */
|
||||
enum alpnid srcalpnid,
|
||||
enum alpnid dstalpnid,
|
||||
unsigned int srcport,
|
||||
unsigned int dstport)
|
||||
size_t srcport,
|
||||
size_t dstport)
|
||||
{
|
||||
struct altsvc *as = calloc(1, sizeof(struct altsvc));
|
||||
size_t hlen;
|
||||
if(!as)
|
||||
return NULL;
|
||||
hlen = strlen(srchost);
|
||||
DEBUGASSERT(hlen);
|
||||
DEBUGASSERT(dlen);
|
||||
if(!hlen || !dlen) {
|
||||
@ -136,8 +137,8 @@ static struct altsvc *altsvc_createid(const char *srchost,
|
||||
|
||||
as->src.alpnid = srcalpnid;
|
||||
as->dst.alpnid = dstalpnid;
|
||||
as->src.port = curlx_ultous(srcport);
|
||||
as->dst.port = curlx_ultous(dstport);
|
||||
as->src.port = (unsigned short)srcport;
|
||||
as->dst.port = (unsigned short)dstport;
|
||||
|
||||
return as;
|
||||
error:
|
||||
@ -145,18 +146,19 @@ error:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct altsvc *altsvc_create(char *srchost,
|
||||
char *dsthost,
|
||||
char *srcalpn,
|
||||
char *dstalpn,
|
||||
unsigned int srcport,
|
||||
unsigned int dstport)
|
||||
static struct altsvc *altsvc_create(struct Curl_str *srchost,
|
||||
struct Curl_str *dsthost,
|
||||
struct Curl_str *srcalpn,
|
||||
struct Curl_str *dstalpn,
|
||||
size_t srcport,
|
||||
size_t dstport)
|
||||
{
|
||||
enum alpnid dstalpnid = alpn2alpnid(dstalpn);
|
||||
enum alpnid srcalpnid = alpn2alpnid(srcalpn);
|
||||
enum alpnid dstalpnid = alpn2alpnid(dstalpn->str, dstalpn->len);
|
||||
enum alpnid srcalpnid = alpn2alpnid(srcalpn->str, srcalpn->len);
|
||||
if(!srcalpnid || !dstalpnid)
|
||||
return NULL;
|
||||
return altsvc_createid(srchost, dsthost, strlen(dsthost),
|
||||
return altsvc_createid(srchost->str, srchost->len,
|
||||
dsthost->str, dsthost->len,
|
||||
srcalpnid, dstalpnid,
|
||||
srcport, dstport);
|
||||
}
|
||||
@ -167,31 +169,50 @@ static CURLcode altsvc_add(struct altsvcinfo *asi, char *line)
|
||||
/* Example line:
|
||||
h2 example.com 443 h3 shiny.example.com 8443 "20191231 10:00:00" 1
|
||||
*/
|
||||
char srchost[MAX_ALTSVC_HOSTLEN + 1];
|
||||
char dsthost[MAX_ALTSVC_HOSTLEN + 1];
|
||||
char srcalpn[MAX_ALTSVC_ALPNLEN + 1];
|
||||
char dstalpn[MAX_ALTSVC_ALPNLEN + 1];
|
||||
char date[MAX_ALTSVC_DATELEN + 1];
|
||||
unsigned int srcport;
|
||||
unsigned int dstport;
|
||||
unsigned int prio;
|
||||
unsigned int persist;
|
||||
int rc;
|
||||
struct Curl_str srchost;
|
||||
struct Curl_str dsthost;
|
||||
struct Curl_str srcalpn;
|
||||
struct Curl_str dstalpn;
|
||||
struct Curl_str date;
|
||||
size_t srcport;
|
||||
size_t dstport;
|
||||
size_t persist;
|
||||
size_t prio;
|
||||
|
||||
rc = sscanf(line,
|
||||
"%" MAX_ALTSVC_ALPNLENSTR "s %" MAX_ALTSVC_HOSTLENSTR "s %u "
|
||||
"%" MAX_ALTSVC_ALPNLENSTR "s %" MAX_ALTSVC_HOSTLENSTR "s %u "
|
||||
"\"%" MAX_ALTSVC_DATELENSTR "[^\"]\" %u %u",
|
||||
srcalpn, srchost, &srcport,
|
||||
dstalpn, dsthost, &dstport,
|
||||
date, &persist, &prio);
|
||||
if(9 == rc) {
|
||||
if(Curl_str_word(&line, &srcalpn, MAX_ALTSVC_ALPNLEN) ||
|
||||
Curl_str_singlespace(&line) ||
|
||||
Curl_str_word(&line, &srchost, MAX_ALTSVC_HOSTLEN) ||
|
||||
Curl_str_singlespace(&line) ||
|
||||
Curl_str_number(&line, &srcport, 65535) ||
|
||||
Curl_str_singlespace(&line) ||
|
||||
Curl_str_word(&line, &dstalpn, MAX_ALTSVC_ALPNLEN) ||
|
||||
Curl_str_singlespace(&line) ||
|
||||
Curl_str_word(&line, &dsthost, MAX_ALTSVC_HOSTLEN) ||
|
||||
Curl_str_singlespace(&line) ||
|
||||
Curl_str_number(&line, &dstport, 65535) ||
|
||||
Curl_str_singlespace(&line) ||
|
||||
Curl_str_quotedword(&line, &date, MAX_ALTSVC_DATELEN) ||
|
||||
Curl_str_singlespace(&line) ||
|
||||
Curl_str_number(&line, &persist, 1) ||
|
||||
Curl_str_singlespace(&line) ||
|
||||
Curl_str_number(&line, &prio, 0) ||
|
||||
Curl_str_newline(&line))
|
||||
;
|
||||
else {
|
||||
struct altsvc *as;
|
||||
time_t expires = Curl_getdate_capped(date);
|
||||
as = altsvc_create(srchost, dsthost, srcalpn, dstalpn, srcport, dstport);
|
||||
char dbuf[MAX_ALTSVC_DATELEN + 1];
|
||||
time_t expires;
|
||||
|
||||
/* 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;
|
||||
expires = Curl_getdate_capped(dbuf);
|
||||
as = altsvc_create(&srchost, &dsthost, &srcalpn, &dstalpn, srcport,
|
||||
dstport);
|
||||
if(as) {
|
||||
as->expires = expires;
|
||||
as->prio = prio;
|
||||
as->prio = 0; /* not supported to just set zero */
|
||||
as->persist = persist ? 1 : 0;
|
||||
Curl_llist_append(&asi->list, as, &as->node);
|
||||
}
|
||||
@ -471,8 +492,6 @@ static time_t altsvc_debugtime(void *unused)
|
||||
#define time(x) altsvc_debugtime(x)
|
||||
#endif
|
||||
|
||||
#define ISNEWLINE(x) (((x) == '\n') || (x) == '\r')
|
||||
|
||||
/*
|
||||
* Curl_altsvc_parse() takes an incoming alt-svc response header and stores
|
||||
* the data correctly in the cache.
|
||||
@ -495,6 +514,8 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data,
|
||||
unsigned short dstport = srcport; /* the same by default */
|
||||
CURLcode result = getalnum(&p, alpnbuf, sizeof(alpnbuf));
|
||||
size_t entries = 0;
|
||||
size_t alpnlen = strlen(alpnbuf);
|
||||
size_t srchostlen = strlen(srchost);
|
||||
#ifdef CURL_DISABLE_VERBOSE_STRINGS
|
||||
(void)data;
|
||||
#endif
|
||||
@ -515,7 +536,7 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data,
|
||||
do {
|
||||
if(*p == '=') {
|
||||
/* [protocol]="[host][:port]" */
|
||||
enum alpnid dstalpnid = alpn2alpnid(alpnbuf); /* the same by default */
|
||||
enum alpnid dstalpnid = alpn2alpnid(alpnbuf, alpnlen);
|
||||
p++;
|
||||
if(*p == '\"') {
|
||||
const char *dsthost = "";
|
||||
@ -633,7 +654,8 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data,
|
||||
this is the first entry of the line. */
|
||||
altsvc_flush(asi, srcalpnid, srchost, srcport);
|
||||
|
||||
as = altsvc_createid(srchost, dsthost, dstlen,
|
||||
as = altsvc_createid(srchost, srchostlen,
|
||||
dsthost, dstlen,
|
||||
srcalpnid, dstalpnid,
|
||||
srcport, dstport);
|
||||
if(as) {
|
||||
|
@ -46,6 +46,6 @@
|
||||
#define ISURLPUNTCS(x) (((x) == '-') || ((x) == '.') || ((x) == '_') || \
|
||||
((x) == '~'))
|
||||
#define ISUNRESERVED(x) (ISALNUM(x) || ISURLPUNTCS(x))
|
||||
|
||||
#define ISNEWLINE(x) (((x) == '\n') || (x) == '\r')
|
||||
|
||||
#endif /* HEADER_CURL_CTYPE_H */
|
||||
|
63
lib/hsts.c
63
lib/hsts.c
@ -41,6 +41,7 @@
|
||||
#include "rename.h"
|
||||
#include "share.h"
|
||||
#include "strdup.h"
|
||||
#include "strparse.h"
|
||||
|
||||
/* The last 3 #include files should be in this order */
|
||||
#include "curl_printf.h"
|
||||
@ -48,10 +49,8 @@
|
||||
#include "memdebug.h"
|
||||
|
||||
#define MAX_HSTS_LINE 4095
|
||||
#define MAX_HSTS_HOSTLEN 256
|
||||
#define MAX_HSTS_HOSTLENSTR "256"
|
||||
#define MAX_HSTS_DATELEN 64
|
||||
#define MAX_HSTS_DATELENSTR "64"
|
||||
#define MAX_HSTS_HOSTLEN 2048
|
||||
#define MAX_HSTS_DATELEN 256
|
||||
#define UNLIMITED "unlimited"
|
||||
|
||||
#if defined(DEBUGBUILD) || defined(UNITTESTS)
|
||||
@ -109,14 +108,13 @@ void Curl_hsts_cleanup(struct hsts **hp)
|
||||
|
||||
static CURLcode hsts_create(struct hsts *h,
|
||||
const char *hostname,
|
||||
size_t hlen,
|
||||
bool subdomains,
|
||||
curl_off_t expires)
|
||||
{
|
||||
size_t hlen;
|
||||
DEBUGASSERT(h);
|
||||
DEBUGASSERT(hostname);
|
||||
|
||||
hlen = strlen(hostname);
|
||||
if(hlen && (hostname[hlen - 1] == '.'))
|
||||
/* strip off any trailing dot */
|
||||
--hlen;
|
||||
@ -150,6 +148,7 @@ CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname,
|
||||
bool subdomains = FALSE;
|
||||
struct stsentry *sts;
|
||||
time_t now = time(NULL);
|
||||
size_t hlen = strlen(hostname);
|
||||
|
||||
if(Curl_host_is_ipnum(hostname))
|
||||
/* "explicit IP address identification of all forms is excluded."
|
||||
@ -218,7 +217,7 @@ CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname,
|
||||
|
||||
if(!expires) {
|
||||
/* remove the entry if present verbatim (without subdomain match) */
|
||||
sts = Curl_hsts(h, hostname, FALSE);
|
||||
sts = Curl_hsts(h, hostname, hlen, FALSE);
|
||||
if(sts) {
|
||||
Curl_node_remove(&sts->node);
|
||||
hsts_free(sts);
|
||||
@ -233,14 +232,14 @@ CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname,
|
||||
expires += now;
|
||||
|
||||
/* check if it already exists */
|
||||
sts = Curl_hsts(h, hostname, FALSE);
|
||||
sts = Curl_hsts(h, hostname, hlen, FALSE);
|
||||
if(sts) {
|
||||
/* just update these fields */
|
||||
sts->expires = expires;
|
||||
sts->includeSubDomains = subdomains;
|
||||
}
|
||||
else
|
||||
return hsts_create(h, hostname, subdomains, expires);
|
||||
return hsts_create(h, hostname, hlen, subdomains, expires);
|
||||
|
||||
return CURLE_OK;
|
||||
}
|
||||
@ -252,12 +251,11 @@ CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname,
|
||||
* attempted.
|
||||
*/
|
||||
struct stsentry *Curl_hsts(struct hsts *h, const char *hostname,
|
||||
bool subdomain)
|
||||
size_t hlen, bool subdomain)
|
||||
{
|
||||
struct stsentry *bestsub = NULL;
|
||||
if(h) {
|
||||
time_t now = time(NULL);
|
||||
size_t hlen = strlen(hostname);
|
||||
struct Curl_llist_node *e;
|
||||
struct Curl_llist_node *n;
|
||||
size_t blen = 0;
|
||||
@ -424,29 +422,40 @@ static CURLcode hsts_add(struct hsts *h, char *line)
|
||||
example.com "20191231 10:00:00"
|
||||
.example.net "20191231 10:00:00"
|
||||
*/
|
||||
char host[MAX_HSTS_HOSTLEN + 1];
|
||||
char date[MAX_HSTS_DATELEN + 1];
|
||||
int rc;
|
||||
struct Curl_str host;
|
||||
struct Curl_str date;
|
||||
|
||||
rc = sscanf(line,
|
||||
"%" MAX_HSTS_HOSTLENSTR "s \"%" MAX_HSTS_DATELENSTR "[^\"]\"",
|
||||
host, date);
|
||||
if(2 == rc) {
|
||||
time_t expires = strcmp(date, UNLIMITED) ? Curl_getdate_capped(date) :
|
||||
TIME_T_MAX;
|
||||
if(Curl_str_word(&line, &host, MAX_HSTS_HOSTLEN) ||
|
||||
Curl_str_singlespace(&line) ||
|
||||
Curl_str_quotedword(&line, &date, MAX_HSTS_DATELEN) ||
|
||||
Curl_str_newline(&line))
|
||||
;
|
||||
else {
|
||||
CURLcode result = CURLE_OK;
|
||||
char *p = host;
|
||||
bool subdomain = FALSE;
|
||||
struct stsentry *e;
|
||||
if(p[0] == '.') {
|
||||
p++;
|
||||
char dbuf[MAX_HSTS_DATELEN + 1];
|
||||
time_t expires;
|
||||
|
||||
/* 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;
|
||||
|
||||
expires = strcmp(dbuf, UNLIMITED) ? Curl_getdate_capped(dbuf) :
|
||||
TIME_T_MAX;
|
||||
|
||||
if(host.str[0] == '.') {
|
||||
host.str++;
|
||||
host.len--;
|
||||
subdomain = TRUE;
|
||||
}
|
||||
/* only add it if not already present */
|
||||
e = Curl_hsts(h, p, subdomain);
|
||||
e = Curl_hsts(h, host.str, host.len, subdomain);
|
||||
if(!e)
|
||||
result = hsts_create(h, p, subdomain, expires);
|
||||
else if(strcasecompare(p, e->host)) {
|
||||
result = hsts_create(h, host.str, host.len, subdomain, expires);
|
||||
else if((strlen(e->host) == host.len) &&
|
||||
strncasecompare(host.str, e->host, host.len)) {
|
||||
/* the same hostname, use the largest expire time */
|
||||
if(expires > e->expires)
|
||||
e->expires = expires;
|
||||
@ -488,7 +497,7 @@ static CURLcode hsts_pull(struct Curl_easy *data, struct hsts *h)
|
||||
expires = Curl_getdate_capped(e.expire);
|
||||
else
|
||||
expires = TIME_T_MAX; /* the end of time */
|
||||
result = hsts_create(h, e.name,
|
||||
result = hsts_create(h, e.name, strlen(e.name),
|
||||
/* bitfield to bool conversion: */
|
||||
e.includeSubDomains ? TRUE : FALSE,
|
||||
expires);
|
||||
|
@ -52,7 +52,7 @@ void Curl_hsts_cleanup(struct hsts **hp);
|
||||
CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname,
|
||||
const char *sts);
|
||||
struct stsentry *Curl_hsts(struct hsts *h, const char *hostname,
|
||||
bool subdomain);
|
||||
size_t hlen, bool subdomain);
|
||||
CURLcode Curl_hsts_save(struct Curl_easy *data, struct hsts *h,
|
||||
const char *file);
|
||||
CURLcode Curl_hsts_loadfile(struct Curl_easy *data,
|
||||
|
@ -35,6 +35,7 @@
|
||||
#include "parsedate.h"
|
||||
#include "sendf.h"
|
||||
#include "escape.h"
|
||||
#include "strparse.h"
|
||||
|
||||
#include <time.h>
|
||||
|
||||
@ -118,8 +119,6 @@ static void trim_headers(struct curl_slist *head)
|
||||
|
||||
/* maximum length for the aws sivg4 parts */
|
||||
#define MAX_SIGV4_LEN 64
|
||||
#define MAX_SIGV4_LEN_TXT "64"
|
||||
|
||||
#define DATE_HDR_KEY_LEN (MAX_SIGV4_LEN + sizeof("X--Date"))
|
||||
|
||||
/* string been x-PROVIDER-date:TIMESTAMP, I need +1 for ':' */
|
||||
@ -160,7 +159,8 @@ static int compare_header_names(const char *a, const char *b)
|
||||
static CURLcode make_headers(struct Curl_easy *data,
|
||||
const char *hostname,
|
||||
char *timestamp,
|
||||
char *provider1,
|
||||
const char *provider1,
|
||||
size_t plen, /* length of provider1 */
|
||||
char **date_header,
|
||||
char *content_sha256_header,
|
||||
struct dynbuf *canonical_headers,
|
||||
@ -174,16 +174,16 @@ static CURLcode make_headers(struct Curl_easy *data,
|
||||
struct curl_slist *l;
|
||||
bool again = TRUE;
|
||||
|
||||
/* provider1 mid */
|
||||
Curl_strntolower(provider1, provider1, strlen(provider1));
|
||||
provider1[0] = Curl_raw_toupper(provider1[0]);
|
||||
msnprintf(date_hdr_key, DATE_HDR_KEY_LEN, "X-%.*s-Date",
|
||||
(int)plen, provider1);
|
||||
/* provider1 ucfirst */
|
||||
Curl_strntolower(&date_hdr_key[2], provider1, plen);
|
||||
date_hdr_key[2] = Curl_raw_toupper(provider1[0]);
|
||||
|
||||
msnprintf(date_hdr_key, DATE_HDR_KEY_LEN, "X-%s-Date", provider1);
|
||||
|
||||
/* provider1 lowercase */
|
||||
Curl_strntolower(provider1, provider1, 1); /* first byte only */
|
||||
msnprintf(date_full_hdr, DATE_FULL_HDR_LEN,
|
||||
"x-%s-date:%s", provider1, timestamp);
|
||||
"x-%.*s-date:%s", (int)plen, provider1, timestamp);
|
||||
/* provider1 lowercase */
|
||||
Curl_strntolower(&date_full_hdr[2], provider1, plen);
|
||||
|
||||
if(!Curl_checkheaders(data, STRCONST("Host"))) {
|
||||
char *fullhost;
|
||||
@ -336,6 +336,7 @@ fail:
|
||||
/* try to parse a payload hash from the content-sha256 header */
|
||||
static char *parse_content_sha_hdr(struct Curl_easy *data,
|
||||
const char *provider1,
|
||||
size_t plen,
|
||||
size_t *value_len)
|
||||
{
|
||||
char key[CONTENT_SHA256_KEY_LEN];
|
||||
@ -343,7 +344,8 @@ static char *parse_content_sha_hdr(struct Curl_easy *data,
|
||||
char *value;
|
||||
size_t len;
|
||||
|
||||
key_len = msnprintf(key, sizeof(key), "x-%s-content-sha256", provider1);
|
||||
key_len = msnprintf(key, sizeof(key), "x-%.*s-content-sha256",
|
||||
(int)plen, provider1);
|
||||
|
||||
value = Curl_checkheaders(data, key, key_len);
|
||||
if(!value)
|
||||
@ -389,6 +391,7 @@ static CURLcode calc_payload_hash(struct Curl_easy *data,
|
||||
|
||||
static CURLcode calc_s3_payload_hash(struct Curl_easy *data,
|
||||
Curl_HttpReq httpreq, char *provider1,
|
||||
size_t plen,
|
||||
unsigned char *sha_hash,
|
||||
char *sha_hex, char *header)
|
||||
{
|
||||
@ -415,7 +418,7 @@ static CURLcode calc_s3_payload_hash(struct Curl_easy *data,
|
||||
|
||||
/* format the required content-sha256 header */
|
||||
msnprintf(header, CONTENT_SHA256_HDR_LEN,
|
||||
"x-%s-content-sha256: %s", provider1, sha_hex);
|
||||
"x-%.*s-content-sha256: %s", (int)plen, provider1, sha_hex);
|
||||
|
||||
ret = CURLE_OK;
|
||||
fail:
|
||||
@ -571,12 +574,11 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
|
||||
CURLcode result = CURLE_OUT_OF_MEMORY;
|
||||
struct connectdata *conn = data->conn;
|
||||
size_t len;
|
||||
const char *arg;
|
||||
char provider0[MAX_SIGV4_LEN + 1]="";
|
||||
char provider1[MAX_SIGV4_LEN + 1]="";
|
||||
char region[MAX_SIGV4_LEN + 1]="";
|
||||
char service[MAX_SIGV4_LEN + 1]="";
|
||||
bool sign_as_s3 = FALSE;
|
||||
char *line;
|
||||
struct Curl_str provider0;
|
||||
struct Curl_str provider1;
|
||||
struct Curl_str region = { NULL, 0};
|
||||
struct Curl_str service = { NULL, 0};
|
||||
const char *hostname = conn->host.name;
|
||||
time_t clock;
|
||||
struct tm tm;
|
||||
@ -625,27 +627,31 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
|
||||
* AWS is the default because most of non-amazon providers
|
||||
* are still using aws:amz as a prefix.
|
||||
*/
|
||||
arg = data->set.str[STRING_AWS_SIGV4] ?
|
||||
data->set.str[STRING_AWS_SIGV4] : "aws:amz";
|
||||
line = data->set.str[STRING_AWS_SIGV4] ?
|
||||
data->set.str[STRING_AWS_SIGV4] : (char *)"aws:amz";
|
||||
|
||||
/* provider1[:provider2[:region[:service]]]
|
||||
/* provider0[:provider1[:region[:service]]]
|
||||
|
||||
No string can be longer than N bytes of non-whitespace
|
||||
*/
|
||||
(void)sscanf(arg, "%" MAX_SIGV4_LEN_TXT "[^:]"
|
||||
":%" MAX_SIGV4_LEN_TXT "[^:]"
|
||||
":%" MAX_SIGV4_LEN_TXT "[^:]"
|
||||
":%" MAX_SIGV4_LEN_TXT "s",
|
||||
provider0, provider1, region, service);
|
||||
if(!provider0[0]) {
|
||||
if(Curl_str_until(&line, &provider0, MAX_SIGV4_LEN, ':')) {
|
||||
failf(data, "first aws-sigv4 provider cannot be empty");
|
||||
result = CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
goto fail;
|
||||
}
|
||||
else if(!provider1[0])
|
||||
strcpy(provider1, provider0);
|
||||
if(Curl_str_single(&line, ':') ||
|
||||
Curl_str_until(&line, &provider1, MAX_SIGV4_LEN, ':')) {
|
||||
provider1.str = provider0.str;
|
||||
provider1.len = provider0.len;
|
||||
}
|
||||
else if(Curl_str_single(&line, ':') ||
|
||||
Curl_str_until(&line, ®ion, MAX_SIGV4_LEN, ':') ||
|
||||
Curl_str_single(&line, ':') ||
|
||||
Curl_str_until(&line, &service, MAX_SIGV4_LEN, ':')) {
|
||||
/* nothing to do */
|
||||
}
|
||||
|
||||
if(!service[0]) {
|
||||
if(!service.len) {
|
||||
char *hostdot = strchr(hostname, '.');
|
||||
if(!hostdot) {
|
||||
failf(data, "aws-sigv4: service missing in parameters and hostname");
|
||||
@ -658,12 +664,13 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
|
||||
result = CURLE_URL_MALFORMAT;
|
||||
goto fail;
|
||||
}
|
||||
memcpy(service, hostname, len);
|
||||
service[len] = '\0';
|
||||
service.str = (char *)hostname;
|
||||
service.len = len;
|
||||
|
||||
infof(data, "aws_sigv4: picked service %s from host", service);
|
||||
infof(data, "aws_sigv4: picked service %.*s from host",
|
||||
(int)service.len, service.str);
|
||||
|
||||
if(!region[0]) {
|
||||
if(!region.len) {
|
||||
const char *reg = hostdot + 1;
|
||||
const char *hostreg = strchr(reg, '.');
|
||||
if(!hostreg) {
|
||||
@ -677,25 +684,29 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
|
||||
result = CURLE_URL_MALFORMAT;
|
||||
goto fail;
|
||||
}
|
||||
memcpy(region, reg, len);
|
||||
region[len] = '\0';
|
||||
infof(data, "aws_sigv4: picked region %s from host", region);
|
||||
region.str = (char *)reg;
|
||||
region.len = len;
|
||||
infof(data, "aws_sigv4: picked region %.*s from host",
|
||||
(int)region.len, region.str);
|
||||
}
|
||||
}
|
||||
|
||||
Curl_http_method(data, conn, &method, &httpreq);
|
||||
|
||||
/* AWS S3 requires a x-amz-content-sha256 header, and supports special
|
||||
* values like UNSIGNED-PAYLOAD */
|
||||
sign_as_s3 = (strcasecompare(provider0, "aws") &&
|
||||
strcasecompare(service, "s3"));
|
||||
|
||||
payload_hash = parse_content_sha_hdr(data, provider1, &payload_hash_len);
|
||||
payload_hash = parse_content_sha_hdr(data, provider1.str, provider1.len,
|
||||
&payload_hash_len);
|
||||
|
||||
if(!payload_hash) {
|
||||
/* AWS S3 requires a x-amz-content-sha256 header, and supports special
|
||||
* values like UNSIGNED-PAYLOAD */
|
||||
bool sign_as_s3 = ((provider0.len == 3) &&
|
||||
strncasecompare(provider0.str, "aws", 3)) &&
|
||||
((service.len == 2) && strncasecompare(service.str, "s3", 2));
|
||||
|
||||
if(sign_as_s3)
|
||||
result = calc_s3_payload_hash(data, httpreq, provider1, sha_hash,
|
||||
sha_hex, content_sha256_hdr);
|
||||
result = calc_s3_payload_hash(data, httpreq,
|
||||
provider1.str, provider1.len,
|
||||
sha_hash, sha_hex, content_sha256_hdr);
|
||||
else
|
||||
result = calc_payload_hash(data, sha_hash, sha_hex);
|
||||
if(result)
|
||||
@ -726,7 +737,8 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
result = make_headers(data, hostname, timestamp, provider1,
|
||||
result = make_headers(data, hostname, timestamp,
|
||||
provider1.str, provider1.len,
|
||||
&date_header, content_sha256_hdr,
|
||||
&canonical_headers, &signed_headers);
|
||||
if(result)
|
||||
@ -771,14 +783,18 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
|
||||
|
||||
DEBUGF(infof(data, "Canonical request: %s", canonical_request));
|
||||
|
||||
/* provider 0 lowercase */
|
||||
Curl_strntolower(provider0, provider0, strlen(provider0));
|
||||
request_type = aprintf("%s4_request", provider0);
|
||||
request_type = aprintf("%.*s4_request", (int)provider0.len, provider0.str);
|
||||
if(!request_type)
|
||||
goto fail;
|
||||
|
||||
credential_scope = aprintf("%s/%s/%s/%s",
|
||||
date, region, service, request_type);
|
||||
/* provider0 is lowercased *after* aprintf() so that the buffer can be
|
||||
written to */
|
||||
Curl_strntolower(request_type, request_type, provider0.len);
|
||||
|
||||
credential_scope = aprintf("%s/%.*s/%.*s/%s",
|
||||
date, (int)region.len, region.str,
|
||||
(int)service.len, service.str,
|
||||
request_type);
|
||||
if(!credential_scope)
|
||||
goto fail;
|
||||
|
||||
@ -788,42 +804,41 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
|
||||
|
||||
sha256_to_hex(sha_hex, sha_hash);
|
||||
|
||||
/* provider 0 uppercase */
|
||||
Curl_strntoupper(provider0, provider0, strlen(provider0));
|
||||
|
||||
/*
|
||||
* Google allows using RSA key instead of HMAC, so this code might change
|
||||
* in the future. For now we only support HMAC.
|
||||
*/
|
||||
str_to_sign = aprintf("%s4-HMAC-SHA256\n" /* Algorithm */
|
||||
str_to_sign = aprintf("%.*s4-HMAC-SHA256\n" /* Algorithm */
|
||||
"%s\n" /* RequestDateTime */
|
||||
"%s\n" /* CredentialScope */
|
||||
"%s", /* HashedCanonicalRequest in hex */
|
||||
provider0,
|
||||
(int)provider0.len, provider0.str,
|
||||
timestamp,
|
||||
credential_scope,
|
||||
sha_hex);
|
||||
if(!str_to_sign) {
|
||||
if(!str_to_sign)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* provider 0 uppercase */
|
||||
secret = aprintf("%s4%s", provider0,
|
||||
/* make provider0 part done uppercase */
|
||||
Curl_strntoupper(str_to_sign, provider0.str, provider0.len);
|
||||
|
||||
secret = aprintf("%.*s4%s", (int)provider0.len, provider0.str,
|
||||
data->state.aptr.passwd ?
|
||||
data->state.aptr.passwd : "");
|
||||
if(!secret)
|
||||
goto fail;
|
||||
/* make provider0 part done uppercase */
|
||||
Curl_strntoupper(secret, provider0.str, provider0.len);
|
||||
|
||||
HMAC_SHA256(secret, strlen(secret), date, strlen(date), sign0);
|
||||
HMAC_SHA256(sign0, sizeof(sign0), region, strlen(region), sign1);
|
||||
HMAC_SHA256(sign1, sizeof(sign1), service, strlen(service), 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), request_type, strlen(request_type), sign1);
|
||||
HMAC_SHA256(sign1, sizeof(sign1), str_to_sign, strlen(str_to_sign), sign0);
|
||||
|
||||
sha256_to_hex(sha_hex, sign0);
|
||||
|
||||
/* provider 0 uppercase */
|
||||
auth_headers = aprintf("Authorization: %s4-HMAC-SHA256 "
|
||||
auth_headers = aprintf("Authorization: %.*s4-HMAC-SHA256 "
|
||||
"Credential=%s/%s, "
|
||||
"SignedHeaders=%s, "
|
||||
"Signature=%s\r\n"
|
||||
@ -834,7 +849,7 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
|
||||
*/
|
||||
"%s"
|
||||
"%s", /* optional sha256 header includes \r\n */
|
||||
provider0,
|
||||
(int)provider0.len, provider0.str,
|
||||
user,
|
||||
credential_scope,
|
||||
Curl_dyn_ptr(&signed_headers),
|
||||
@ -844,6 +859,9 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
|
||||
if(!auth_headers) {
|
||||
goto fail;
|
||||
}
|
||||
/* provider 0 uppercase */
|
||||
Curl_strntoupper(&auth_headers[sizeof("Authorization: ") - 1],
|
||||
provider0.str, provider0.len);
|
||||
|
||||
Curl_safefree(data->state.aptr.userpwd);
|
||||
data->state.aptr.userpwd = auth_headers;
|
||||
|
136
lib/strparse.c
Normal file
136
lib/strparse.c
Normal file
@ -0,0 +1,136 @@
|
||||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) 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
|
||||
* are also available at https://curl.se/docs/copyright.html.
|
||||
*
|
||||
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||
* copies of the Software, and permit persons to whom the Software is
|
||||
* furnished to do so, under the terms of the COPYING file.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
* SPDX-License-Identifier: curl
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
#include "strparse.h"
|
||||
|
||||
/* 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(char **linep, struct Curl_str *out,
|
||||
const size_t max, char delim)
|
||||
{
|
||||
char *s = *linep;
|
||||
size_t len = 0;
|
||||
DEBUGASSERT(linep && *linep && out && max && delim);
|
||||
|
||||
out->str = NULL;
|
||||
out->len = 0;
|
||||
while(*s && (*s != delim)) {
|
||||
s++;
|
||||
if(++len > max) {
|
||||
return STRE_BIG;
|
||||
}
|
||||
}
|
||||
if(!len)
|
||||
return STRE_SHORT;
|
||||
out->str = *linep;
|
||||
out->len = len;
|
||||
*linep = s; /* point to the first byte after the word */
|
||||
return STRE_OK;
|
||||
}
|
||||
|
||||
/* Get a word until the first space or end of string. At least one byte long.
|
||||
return non-zero on error */
|
||||
int Curl_str_word(char **linep, struct Curl_str *out,
|
||||
const size_t max)
|
||||
{
|
||||
return Curl_str_until(linep, out, max, ' ');
|
||||
}
|
||||
|
||||
|
||||
/* Get a "quoted" word. No escaping possible.
|
||||
return non-zero on error */
|
||||
int Curl_str_quotedword(char **linep, struct Curl_str *out,
|
||||
const size_t max)
|
||||
{
|
||||
char *s = *linep;
|
||||
size_t len = 0;
|
||||
DEBUGASSERT(linep && *linep && out && max);
|
||||
|
||||
out->str = NULL;
|
||||
out->len = 0;
|
||||
if(*s != '\"')
|
||||
return STRE_BEGQUOTE;
|
||||
s++;
|
||||
while(*s && (*s != '\"')) {
|
||||
s++;
|
||||
if(++len > max)
|
||||
return STRE_BIG;
|
||||
}
|
||||
if(*s != '\"')
|
||||
return STRE_ENDQUOTE;
|
||||
out->str = (*linep) + 1;
|
||||
out->len = len;
|
||||
*linep = s + 1;
|
||||
return STRE_OK;
|
||||
}
|
||||
|
||||
/* Advance over a single character.
|
||||
return non-zero on error */
|
||||
int Curl_str_single(char **linep, char byte)
|
||||
{
|
||||
DEBUGASSERT(linep && *linep);
|
||||
if(**linep != byte)
|
||||
return STRE_BYTE;
|
||||
(*linep)++; /* move over it */
|
||||
return STRE_OK;
|
||||
}
|
||||
|
||||
/* Advance over a single space.
|
||||
return non-zero on error */
|
||||
int Curl_str_singlespace(char **linep)
|
||||
{
|
||||
return Curl_str_single(linep, ' ');
|
||||
}
|
||||
|
||||
/* Get an unsigned number. Leading zeroes are accepted.
|
||||
return non-zero on error */
|
||||
int Curl_str_number(char **linep, size_t *nump, size_t max)
|
||||
{
|
||||
size_t num = 0;
|
||||
DEBUGASSERT(linep && *linep && nump);
|
||||
*nump = 0;
|
||||
while(ISDIGIT(**linep)) {
|
||||
int n = **linep - '0';
|
||||
if(num > ((SIZE_T_MAX - n) / 10))
|
||||
return STRE_OVERFLOW;
|
||||
num = num * 10 + n;
|
||||
if(num > max)
|
||||
return STRE_BIG; /** too big */
|
||||
(*linep)++;
|
||||
}
|
||||
*nump = num;
|
||||
return STRE_OK;
|
||||
}
|
||||
|
||||
/* CR or LF
|
||||
return non-zero on error */
|
||||
int Curl_str_newline(char **linep)
|
||||
{
|
||||
DEBUGASSERT(linep && *linep);
|
||||
if(ISNEWLINE(**linep)) {
|
||||
(*linep)++;
|
||||
return STRE_OK; /* yessir */
|
||||
}
|
||||
return STRE_NEWLINE;
|
||||
}
|
71
lib/strparse.h
Normal file
71
lib/strparse.h
Normal file
@ -0,0 +1,71 @@
|
||||
#ifndef HEADER_CURL_STRPARSE_H
|
||||
#define HEADER_CURL_STRPARSE_H
|
||||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) 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
|
||||
* are also available at https://curl.se/docs/copyright.html.
|
||||
*
|
||||
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||
* copies of the Software, and permit persons to whom the Software is
|
||||
* furnished to do so, under the terms of the COPYING file.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
* SPDX-License-Identifier: curl
|
||||
*
|
||||
***************************************************************************/
|
||||
#include "curl_setup.h"
|
||||
|
||||
#define STRE_OK 0
|
||||
#define STRE_BIG 1
|
||||
#define STRE_SHORT 2
|
||||
#define STRE_BEGQUOTE 3
|
||||
#define STRE_ENDQUOTE 4
|
||||
#define STRE_BYTE 5
|
||||
#define STRE_NEWLINE 6
|
||||
#define STRE_OVERFLOW 7
|
||||
|
||||
struct Curl_str {
|
||||
char *str;
|
||||
size_t len;
|
||||
};
|
||||
|
||||
/* Get a word until the first space
|
||||
return non-zero on error */
|
||||
int Curl_str_word(char **linep, struct Curl_str *out, const size_t max);
|
||||
|
||||
/* Get a word until the first DELIM or end of string
|
||||
return non-zero on error */
|
||||
int Curl_str_until(char **linep, struct Curl_str *out, const size_t max,
|
||||
char delim);
|
||||
|
||||
/* Get a "quoted" word. No escaping possible.
|
||||
return non-zero on error */
|
||||
int Curl_str_quotedword(char **linep, struct Curl_str *out, const size_t max);
|
||||
|
||||
/* Advance over a single character.
|
||||
return non-zero on error */
|
||||
int Curl_str_single(char **linep, char byte);
|
||||
|
||||
/* Advance over a single space.
|
||||
return non-zero on error */
|
||||
int Curl_str_singlespace(char **linep);
|
||||
|
||||
/* Get an unsigned number
|
||||
return non-zero on error */
|
||||
int Curl_str_number(char **linep, size_t *nump, size_t max);
|
||||
|
||||
/* Check for CR or LF
|
||||
return non-zero on error */
|
||||
int Curl_str_newline(char **linep);
|
||||
|
||||
#endif /* HEADER_CURL_STRPARSE_H */
|
@ -1827,7 +1827,7 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data,
|
||||
/* HSTS upgrade */
|
||||
if(data->hsts && strcasecompare("http", data->state.up.scheme)) {
|
||||
/* This MUST use the IDN decoded name */
|
||||
if(Curl_hsts(data->hsts, conn->host.name, TRUE)) {
|
||||
if(Curl_hsts(data->hsts, conn->host.name, strlen(conn->host.name), TRUE)) {
|
||||
char *url;
|
||||
Curl_safefree(data->state.up.scheme);
|
||||
uc = curl_url_set(uh, CURLUPART_SCHEME, "https", 0);
|
||||
|
@ -453,6 +453,7 @@ Features testable here are:
|
||||
- `Kerberos`
|
||||
- `Largefile`
|
||||
- `large-time` (time_t is larger than 32-bit)
|
||||
- `large-size` (size_t is larger than 32-bit)
|
||||
- `ld_preload`
|
||||
- `libssh2`
|
||||
- `libssh`
|
||||
|
@ -218,7 +218,7 @@ test1620 test1621 \
|
||||
test1630 test1631 test1632 test1633 test1634 test1635 \
|
||||
\
|
||||
test1650 test1651 test1652 test1653 test1654 test1655 test1656 \
|
||||
test1660 test1661 test1662 test1663 \
|
||||
test1660 test1661 test1662 test1663 test1664 \
|
||||
\
|
||||
test1670 test1671 \
|
||||
\
|
||||
|
@ -27,13 +27,13 @@ alt-svc
|
||||
%LOGDIR/%TESTNUMBER
|
||||
</command>
|
||||
<file name="%LOGDIR/%TESTNUMBER" mode="text">
|
||||
h2 example.com 443 h3 shiny.example.com 8443 "20191231 00:00:00" 0 1
|
||||
h2 example.com 443 h3 shiny.example.com 8443 "20191231 00:00:00" 0 0
|
||||
# a comment
|
||||
h2 foo.example.com 443 h3 shiny.example.com 8443 "20291231 23:30:00" 0 1
|
||||
h1 example.com 443 h3 shiny.example.com 8443 "20121231 00:00:01" 0 1
|
||||
h3 example.com 443 h3 shiny.example.com 8443 "20131231 00:00:00" 0 1
|
||||
h2 foo.example.com 443 h3 shiny.example.com 8443 "20291231 23:30:00" 0 0
|
||||
h1 example.com 443 h3 shiny.example.com 8443 "20121231 00:00:01" 0 0
|
||||
h3 example.com 443 h3 shiny.example.com 8443 "20131231 00:00:00" 0 0
|
||||
# also a comment
|
||||
bad example.com 443 h3 shiny.example.com 8443 "20191231 00:00:00" 0 1
|
||||
bad example.com 443 h3 shiny.example.com 8443 "20191231 00:00:00" 0 0
|
||||
rubbish
|
||||
</file>
|
||||
</client>
|
||||
@ -41,10 +41,10 @@ rubbish
|
||||
<file name="%LOGDIR/%TESTNUMBER-out" mode="text">
|
||||
# Your alt-svc cache. https://curl.se/docs/alt-svc.html
|
||||
# This file was generated by libcurl! Edit at your own risk.
|
||||
h2 example.com 443 h3 shiny.example.com 8443 "20191231 00:00:00" 0 1
|
||||
h2 foo.example.com 443 h3 shiny.example.com 8443 "20291231 23:30:00" 0 1
|
||||
h1 example.com 443 h3 shiny.example.com 8443 "20121231 00:00:01" 0 1
|
||||
h3 example.com 443 h3 shiny.example.com 8443 "20131231 00:00:00" 0 1
|
||||
h2 example.com 443 h3 shiny.example.com 8443 "20191231 00:00:00" 0 0
|
||||
h2 foo.example.com 443 h3 shiny.example.com 8443 "20291231 23:30:00" 0 0
|
||||
h1 example.com 443 h3 shiny.example.com 8443 "20121231 00:00:01" 0 0
|
||||
h3 example.com 443 h3 shiny.example.com 8443 "20131231 00:00:00" 0 0
|
||||
h1 example.org 8080 h2 example.com 8080 "20190125 22:34:21" 0 0
|
||||
h1 2.example.org 8080 h3 2.example.org 8080 "20190125 22:34:21" 0 0
|
||||
h1 3.example.org 8080 h2 example.com 8080 "20190125 22:34:21" 0 0
|
||||
|
120
tests/data/test1664
Normal file
120
tests/data/test1664
Normal file
@ -0,0 +1,120 @@
|
||||
<testcase>
|
||||
<info>
|
||||
<keywords>
|
||||
unittest
|
||||
strparse
|
||||
</keywords>
|
||||
</info>
|
||||
|
||||
#
|
||||
# Client-side
|
||||
<client>
|
||||
<server>
|
||||
none
|
||||
</server>
|
||||
<features>
|
||||
unittest
|
||||
large-size
|
||||
</features>
|
||||
<name>
|
||||
unit tests for strparse.c string parsing functions
|
||||
</name>
|
||||
</client>
|
||||
|
||||
<verify>
|
||||
<stdout>
|
||||
Curl_str_word
|
||||
0: ("word") 0, "word" [4], line 4
|
||||
1: ("word ") 0, "word" [4], line 4
|
||||
2: (" word ") 2, "" [0], line 0
|
||||
3: ("wo rd") 0, "wo" [2], line 2
|
||||
4: ("word(") 0, "word(" [5], line 5
|
||||
5: ("wor(d") 0, "wor(d" [5], line 5
|
||||
6: ("perfect") 0, "perfect" [7], line 7
|
||||
7: ("") 2, "" [0], line 0
|
||||
8: ("longerth") 1, "" [0], line 0
|
||||
Curl_str_until
|
||||
0: ("word") 0, "wor" [3], line 3
|
||||
1: ("word ") 0, "wor" [3], line 3
|
||||
2: (" word ") 0, " wor" [4], line 4
|
||||
3: ("wo rd") 0, "wo r" [4], line 4
|
||||
4: ("word(") 0, "wor" [3], line 3
|
||||
5: ("wor(d") 0, "wor(" [4], line 4
|
||||
6: ("perfect") 0, "perfect" [7], line 7
|
||||
7: ("") 2, "" [0], line 0
|
||||
8: ("longerth") 1, "" [0], line 0
|
||||
Curl_str_quotedword
|
||||
0: (""word"") 0, "word" [4], line 6
|
||||
1: (""word") 4, "" [0], line 0
|
||||
2: ("word"") 3, "" [0], line 0
|
||||
3: (""word""") 0, "word" [4], line 6
|
||||
4: (""word" ") 0, "word" [4], line 6
|
||||
5: (" "word"") 3, "" [0], line 0
|
||||
6: (""perfect"") 0, "perfect" [7], line 9
|
||||
7: (""p r e t"") 0, "p r e t" [7], line 9
|
||||
8: (""perfec\"") 0, "perfec\" [7], line 9
|
||||
9: ("""") 0, "" [0], line 2
|
||||
10: ("") 3, "" [0], line 0
|
||||
11: (""longerth"") 1, "" [0], line 0
|
||||
Curl_str_single
|
||||
0: ("a") 0, line 1
|
||||
1: ("aa") 0, line 1
|
||||
2: ("A") 5, line 0
|
||||
3: ("b") 5, line 0
|
||||
4: ("\") 5, line 0
|
||||
5: (" ") 5, line 0
|
||||
6: ("") 5, line 0
|
||||
Curl_str_singlespace
|
||||
0: ("a") 5, line 0
|
||||
1: ("aa") 5, line 0
|
||||
2: ("A") 5, line 0
|
||||
3: ("b") 5, line 0
|
||||
4: ("\") 5, line 0
|
||||
5: (" ") 0, line 1
|
||||
6: (" ") 5, line 0
|
||||
7: ("
|
||||
") 5, line 0
|
||||
8: ("") 5, line 0
|
||||
Curl_str_single
|
||||
0: ("a") 0, line 1
|
||||
1: ("aa") 0, line 1
|
||||
2: ("A") 5, line 0
|
||||
3: ("b") 5, line 0
|
||||
4: ("\") 5, line 0
|
||||
5: (" ") 5, line 0
|
||||
6: ("") 5, line 0
|
||||
Curl_str_number
|
||||
0: ("1") 0, [1] line 1
|
||||
1: ("10000") 1, [0] line 4
|
||||
2: ("1234") 0, [1234] line 4
|
||||
3: ("1235") 0, [1235] line 4
|
||||
4: ("1236") 1, [0] line 3
|
||||
5: ("01234") 0, [1234] line 5
|
||||
6: ("00000000000000000000000000001234") 0, [1234] line 32
|
||||
7: ("0123 345") 0, [123] line 4
|
||||
8: ("0123O345") 0, [123] line 4
|
||||
9: ("-12") 0, [0] line 0
|
||||
10: (" 123") 0, [0] line 0
|
||||
11: ("") 0, [0] line 0
|
||||
Curl_str_number / max
|
||||
0: ("9223372036854775808") 0, [9223372036854775808] line 19
|
||||
1: ("9223372036854775809") 0, [9223372036854775809] line 19
|
||||
2: ("18446744073709551615") 0, [18446744073709551615] line 20
|
||||
3: ("18446744073709551616") 7, [0] line 19
|
||||
4: ("18446744073709551617") 7, [0] line 19
|
||||
Curl_str_newline
|
||||
0: ("a") 6, line 0
|
||||
1: ("aa") 6, line 0
|
||||
2: ("A") 6, line 0
|
||||
3: ("b") 6, line 0
|
||||
4: ("\") 6, line 0
|
||||
5: (" ") 6, line 0
|
||||
6: ("
|
||||
") 0, line 1
|
||||
7: ("
") 0, line 1
|
||||
8: ("
|
||||
") 0, line 1
|
||||
9: ("") 6, line 0
|
||||
</stdout>
|
||||
</verify>
|
||||
</testcase>
|
@ -823,6 +823,7 @@ sub checksystemfeatures {
|
||||
$feature{"headers-api"} = 1;
|
||||
$feature{"xattr"} = 1;
|
||||
$feature{"large-time"} = 1;
|
||||
$feature{"large-size"} = 1;
|
||||
$feature{"sha512-256"} = 1;
|
||||
$feature{"local-http"} = servers::localhttp();
|
||||
$feature{"codeset-utf8"} = lc(langinfo(CODESET())) eq "utf-8";
|
||||
|
@ -101,6 +101,9 @@ static const char *disabled[]={
|
||||
#if (SIZEOF_TIME_T < 5)
|
||||
"large-time",
|
||||
#endif
|
||||
#if (SIZEOF_SIZE_T < 5)
|
||||
"large-size",
|
||||
#endif
|
||||
#ifndef CURL_HAVE_SHA512_256
|
||||
"sha512-256",
|
||||
#endif
|
||||
|
@ -39,7 +39,7 @@ UNITPROGS = unit1300 unit1302 unit1303 unit1304 unit1305 unit1307 \
|
||||
unit1608 unit1609 unit1610 unit1611 unit1612 unit1614 unit1615 unit1616 \
|
||||
unit1620 unit1621 \
|
||||
unit1650 unit1651 unit1652 unit1653 unit1654 unit1655 unit1656 \
|
||||
unit1660 unit1661 unit1663 \
|
||||
unit1660 unit1661 unit1663 unit1664 \
|
||||
unit2600 unit2601 unit2602 unit2603 unit2604 \
|
||||
unit3200 \
|
||||
unit3205
|
||||
@ -132,6 +132,8 @@ unit1661_SOURCES = unit1661.c $(UNITFILES)
|
||||
|
||||
unit1663_SOURCES = unit1663.c $(UNITFILES)
|
||||
|
||||
unit1664_SOURCES = unit1664.c $(UNITFILES)
|
||||
|
||||
unit2600_SOURCES = unit2600.c $(UNITFILES)
|
||||
|
||||
unit2601_SOURCES = unit2601.c $(UNITFILES)
|
||||
|
@ -156,7 +156,7 @@ UNITTEST_START
|
||||
}
|
||||
|
||||
chost = headers[i].chost ? headers[i].chost : headers[i].host;
|
||||
e = Curl_hsts(h, chost, TRUE);
|
||||
e = Curl_hsts(h, chost, strlen(chost), TRUE);
|
||||
showsts(e, chost);
|
||||
}
|
||||
|
||||
@ -165,7 +165,7 @@ UNITTEST_START
|
||||
/* verify that it is exists for 7 seconds */
|
||||
chost = "expire.example";
|
||||
for(i = 100; i < 110; i++) {
|
||||
e = Curl_hsts(h, chost, TRUE);
|
||||
e = Curl_hsts(h, chost, strlen(chost), TRUE);
|
||||
showsts(e, chost);
|
||||
deltatime++; /* another second passed */
|
||||
}
|
||||
|
254
tests/unit/unit1664.c
Normal file
254
tests/unit/unit1664.c
Normal file
@ -0,0 +1,254 @@
|
||||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) 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
|
||||
* are also available at https://curl.se/docs/copyright.html.
|
||||
*
|
||||
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||
* copies of the Software, and permit persons to whom the Software is
|
||||
* furnished to do so, under the terms of the COPYING file.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
* SPDX-License-Identifier: curl
|
||||
*
|
||||
***************************************************************************/
|
||||
#include "curlcheck.h"
|
||||
|
||||
#ifdef HAVE_NETINET_IN_H
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
#ifdef HAVE_NETINET_IN6_H
|
||||
#include <netinet/in6.h>
|
||||
#endif
|
||||
|
||||
#include <curl/curl.h>
|
||||
|
||||
#include "strparse.h"
|
||||
|
||||
#include "memdebug.h" /* LAST include file */
|
||||
|
||||
static CURLcode unit_setup(void)
|
||||
{
|
||||
CURLcode res = CURLE_OK;
|
||||
global_init(CURL_GLOBAL_ALL);
|
||||
return res;
|
||||
}
|
||||
|
||||
static void unit_stop(void)
|
||||
{
|
||||
curl_global_cleanup();
|
||||
}
|
||||
|
||||
UNITTEST_START
|
||||
{
|
||||
static const char *wordparse[] = {
|
||||
"word",
|
||||
"word ",
|
||||
" word ",
|
||||
"wo rd",
|
||||
"word(",
|
||||
"wor(d",
|
||||
"perfect",
|
||||
"",
|
||||
"longerth",
|
||||
NULL
|
||||
};
|
||||
|
||||
int i;
|
||||
printf("Curl_str_word\n");
|
||||
for(i = 0; wordparse[i]; i++) {
|
||||
struct Curl_str out;
|
||||
char *line = (char *)wordparse[i];
|
||||
char *orgline = line;
|
||||
int rc = Curl_str_word(&line, &out, 7);
|
||||
printf("%u: (\"%s\") %d, \"%.*s\" [%d], line %d\n",
|
||||
i, orgline, rc, (int)out.len, out.str, (int)out.len,
|
||||
(int)(line - orgline));
|
||||
}
|
||||
|
||||
printf("Curl_str_until\n");
|
||||
for(i = 0; wordparse[i]; i++) {
|
||||
struct Curl_str out;
|
||||
char *line = (char *)wordparse[i];
|
||||
char *orgline = line;
|
||||
int rc = Curl_str_until(&line, &out, 7, 'd');
|
||||
printf("%u: (\"%s\") %d, \"%.*s\" [%d], line %d\n",
|
||||
i, orgline, rc, (int)out.len, out.str, (int)out.len,
|
||||
(int)(line - orgline));
|
||||
}
|
||||
|
||||
{
|
||||
static const char *qwords[] = {
|
||||
"\"word\"",
|
||||
"\"word",
|
||||
"word\"",
|
||||
"\"word\"\"",
|
||||
"\"word\" ",
|
||||
" \"word\"",
|
||||
"\"perfect\"",
|
||||
"\"p r e t\"",
|
||||
"\"perfec\\\"",
|
||||
"\"\"",
|
||||
"",
|
||||
"\"longerth\"",
|
||||
NULL
|
||||
};
|
||||
|
||||
printf("Curl_str_quotedword\n");
|
||||
for(i = 0; qwords[i]; i++) {
|
||||
struct Curl_str out;
|
||||
char *line = (char *)qwords[i];
|
||||
char *orgline = line;
|
||||
int rc = Curl_str_quotedword(&line, &out, 7);
|
||||
printf("%u: (\"%s\") %d, \"%.*s\" [%d], line %d\n",
|
||||
i, orgline, rc, (int)out.len, out.str, (int)out.len,
|
||||
(int)(line - orgline));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
static const char *single[] = {
|
||||
"a",
|
||||
"aa",
|
||||
"A",
|
||||
"b",
|
||||
"\\",
|
||||
" ",
|
||||
"",
|
||||
NULL
|
||||
};
|
||||
printf("Curl_str_single\n");
|
||||
for(i = 0; single[i]; i++) {
|
||||
char *line = (char *)single[i];
|
||||
char *orgline = line;
|
||||
int rc = Curl_str_single(&line, 'a');
|
||||
printf("%u: (\"%s\") %d, line %d\n",
|
||||
i, orgline, rc, (int)(line - orgline));
|
||||
}
|
||||
}
|
||||
{
|
||||
static const char *single[] = {
|
||||
"a",
|
||||
"aa",
|
||||
"A",
|
||||
"b",
|
||||
"\\",
|
||||
" ",
|
||||
"\t",
|
||||
"\n",
|
||||
"",
|
||||
NULL
|
||||
};
|
||||
printf("Curl_str_singlespace\n");
|
||||
for(i = 0; single[i]; i++) {
|
||||
char *line = (char *)single[i];
|
||||
char *orgline = line;
|
||||
int rc = Curl_str_singlespace(&line);
|
||||
printf("%u: (\"%s\") %d, line %d\n",
|
||||
i, orgline, rc, (int)(line - orgline));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
static const char *single[] = {
|
||||
"a",
|
||||
"aa",
|
||||
"A",
|
||||
"b",
|
||||
"\\",
|
||||
" ",
|
||||
"",
|
||||
NULL
|
||||
};
|
||||
printf("Curl_str_single\n");
|
||||
for(i = 0; single[i]; i++) {
|
||||
char *line = (char *)single[i];
|
||||
char *orgline = line;
|
||||
int rc = Curl_str_single(&line, 'a');
|
||||
printf("%u: (\"%s\") %d, line %d\n",
|
||||
i, orgline, rc, (int)(line - orgline));
|
||||
}
|
||||
}
|
||||
{
|
||||
static const char *nums[] = {
|
||||
"1",
|
||||
"10000",
|
||||
"1234",
|
||||
"1235",
|
||||
"1236",
|
||||
"01234",
|
||||
"00000000000000000000000000001234",
|
||||
"0123 345",
|
||||
"0123O345",
|
||||
"-12",
|
||||
" 123",
|
||||
"",
|
||||
NULL
|
||||
};
|
||||
printf("Curl_str_number\n");
|
||||
for(i = 0; nums[i]; i++) {
|
||||
size_t num;
|
||||
char *line = (char *)nums[i];
|
||||
char *orgline = line;
|
||||
int rc = Curl_str_number(&line, &num, 1235);
|
||||
printf("%u: (\"%s\") %d, [%u] line %d\n",
|
||||
i, orgline, rc, (int)num, (int)(line - orgline));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
/* SIZE_T_MAX is typically 18446744073709551615 */
|
||||
static const char *nums[] = {
|
||||
"9223372036854775808", /* 2^63 */
|
||||
"9223372036854775809", /* 2^63 + 1 */
|
||||
"18446744073709551615", /* 2^64 - 1 */
|
||||
"18446744073709551616", /* 2^64 */
|
||||
"18446744073709551617", /* 2^64 + 1 */
|
||||
NULL
|
||||
};
|
||||
printf("Curl_str_number / max\n");
|
||||
for(i = 0; nums[i]; i++) {
|
||||
size_t num;
|
||||
char *line = (char *)nums[i];
|
||||
char *orgline = line;
|
||||
int rc = Curl_str_number(&line, &num, SIZE_T_MAX);
|
||||
printf("%u: (\"%s\") %d, [%zu] line %d\n",
|
||||
i, orgline, rc, num, (int)(line - orgline));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
static const char *newl[] = {
|
||||
"a",
|
||||
"aa",
|
||||
"A",
|
||||
"b",
|
||||
"\\",
|
||||
" ",
|
||||
"\n",
|
||||
"\r",
|
||||
"\r\n",
|
||||
"",
|
||||
NULL
|
||||
};
|
||||
printf("Curl_str_newline\n");
|
||||
for(i = 0; newl[i]; i++) {
|
||||
char *line = (char *)newl[i];
|
||||
char *orgline = line;
|
||||
int rc = Curl_str_newline(&line);
|
||||
printf("%u: (\"%s\") %d, line %d\n",
|
||||
i, orgline, rc, (int)(line - orgline));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
UNITTEST_STOP
|
Loading…
x
Reference in New Issue
Block a user