parent
5b5e2f7318
commit
d485177151
246
lib/altsvc.c
246
lib/altsvc.c
@ -407,26 +407,6 @@ CURLcode Curl_altsvc_save(struct Curl_easy *data,
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static CURLcode getalnum(const char **ptr, char *alpnbuf, size_t buflen)
|
|
||||||
{
|
|
||||||
size_t len;
|
|
||||||
const char *protop;
|
|
||||||
const char *p = *ptr;
|
|
||||||
while(ISBLANK(*p))
|
|
||||||
p++;
|
|
||||||
protop = p;
|
|
||||||
while(*p && !ISBLANK(*p) && (*p != ';') && (*p != '='))
|
|
||||||
p++;
|
|
||||||
len = p - protop;
|
|
||||||
*ptr = p;
|
|
||||||
|
|
||||||
if(!len || (len >= buflen))
|
|
||||||
return CURLE_BAD_FUNCTION_ARGUMENT;
|
|
||||||
memcpy(alpnbuf, protop, len);
|
|
||||||
alpnbuf[len] = 0;
|
|
||||||
return CURLE_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* hostcompare() returns true if 'host' matches 'check'. The first host
|
/* hostcompare() returns true if 'host' matches 'check'. The first host
|
||||||
* argument may have a trailing dot present that will be ignored.
|
* argument may have a trailing dot present that will be ignored.
|
||||||
*/
|
*/
|
||||||
@ -497,144 +477,124 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data,
|
|||||||
unsigned short srcport)
|
unsigned short srcport)
|
||||||
{
|
{
|
||||||
const char *p = value;
|
const char *p = value;
|
||||||
char alpnbuf[MAX_ALTSVC_ALPNLEN] = "";
|
|
||||||
struct altsvc *as;
|
struct altsvc *as;
|
||||||
unsigned short dstport = srcport; /* the same by default */
|
unsigned short dstport = srcport; /* the same by default */
|
||||||
CURLcode result = getalnum(&p, alpnbuf, sizeof(alpnbuf));
|
|
||||||
size_t entries = 0;
|
size_t entries = 0;
|
||||||
size_t alpnlen = strlen(alpnbuf);
|
struct Curl_str alpn;
|
||||||
size_t srchostlen = strlen(srchost);
|
const char *sp;
|
||||||
|
time_t maxage = 24 * 3600; /* default is 24 hours */
|
||||||
|
bool persist = FALSE;
|
||||||
#ifdef CURL_DISABLE_VERBOSE_STRINGS
|
#ifdef CURL_DISABLE_VERBOSE_STRINGS
|
||||||
(void)data;
|
(void)data;
|
||||||
#endif
|
#endif
|
||||||
if(result) {
|
|
||||||
infof(data, "Excessive alt-svc header, ignoring.");
|
|
||||||
return CURLE_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEBUGASSERT(asi);
|
DEBUGASSERT(asi);
|
||||||
|
|
||||||
/* "clear" is a magic keyword */
|
/* initial check for "clear" */
|
||||||
if(strcasecompare(alpnbuf, "clear")) {
|
if(!Curl_str_until(&p, &alpn, MAX_ALTSVC_LINE, ';') &&
|
||||||
/* Flush cached alternatives for this source origin */
|
!Curl_str_single(&p, ';')) {
|
||||||
altsvc_flush(asi, srcalpnid, srchost, srcport);
|
Curl_str_trimblanks(&alpn);
|
||||||
return CURLE_OK;
|
/* "clear" is a magic keyword */
|
||||||
|
if(Curl_str_casecompare(&alpn, "clear")) {
|
||||||
|
/* Flush cached alternatives for this source origin */
|
||||||
|
altsvc_flush(asi, srcalpnid, srchost, srcport);
|
||||||
|
return CURLE_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p = value;
|
||||||
|
|
||||||
|
if(Curl_str_until(&p, &alpn, MAX_ALTSVC_LINE, '='))
|
||||||
|
return CURLE_OK; /* strange line */
|
||||||
|
|
||||||
|
Curl_str_trimblanks(&alpn);
|
||||||
|
|
||||||
|
/* Handle the optional 'ma' and 'persist' flags once first, as they need to
|
||||||
|
be known for each alternative service. Unknown flags are skipped. */
|
||||||
|
sp = strchr(p, ';');
|
||||||
|
if(sp) {
|
||||||
|
sp++; /* pass the semicolon */
|
||||||
|
for(;;) {
|
||||||
|
struct Curl_str name;
|
||||||
|
struct Curl_str val;
|
||||||
|
const char *vp;
|
||||||
|
curl_off_t num;
|
||||||
|
bool quoted;
|
||||||
|
/* allow some extra whitespaces around name and value */
|
||||||
|
if(Curl_str_until(&sp, &name, 20, '=') ||
|
||||||
|
Curl_str_single(&sp, '=') ||
|
||||||
|
Curl_str_until(&sp, &val, 80, ';'))
|
||||||
|
break;
|
||||||
|
Curl_str_trimblanks(&name);
|
||||||
|
Curl_str_trimblanks(&val);
|
||||||
|
/* the value might be quoted */
|
||||||
|
vp = Curl_str(&val);
|
||||||
|
quoted = (*vp == '\"');
|
||||||
|
if(quoted)
|
||||||
|
vp++;
|
||||||
|
if(!Curl_str_number(&vp, &num, TIME_T_MAX)) {
|
||||||
|
if(Curl_str_casecompare(&name, "ma"))
|
||||||
|
maxage = (time_t)num;
|
||||||
|
else if(Curl_str_casecompare(&name, "persist") && (num == 1))
|
||||||
|
persist = TRUE;
|
||||||
|
}
|
||||||
|
if(quoted && Curl_str_single(&sp, '\"'))
|
||||||
|
break;
|
||||||
|
if(Curl_str_single(&sp, ';'))
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
do {
|
do {
|
||||||
if(*p == '=') {
|
if(!Curl_str_single(&p, '=')) {
|
||||||
/* [protocol]="[host][:port]" */
|
/* [protocol]="[host][:port], [protocol]="[host][:port]" */
|
||||||
enum alpnid dstalpnid = Curl_alpn2alpnid(alpnbuf, alpnlen);
|
enum alpnid dstalpnid =
|
||||||
p++;
|
Curl_alpn2alpnid(Curl_str(&alpn), Curl_strlen(&alpn));
|
||||||
if(*p == '\"') {
|
if(!Curl_str_single(&p, '\"')) {
|
||||||
const char *dsthost = "";
|
struct Curl_str dsthost;
|
||||||
size_t dstlen = 0; /* destination hostname length */
|
curl_off_t port = 0;
|
||||||
const char *value_ptr;
|
if(Curl_str_single(&p, ':')) {
|
||||||
char option[32];
|
|
||||||
curl_off_t num;
|
|
||||||
bool quoted = FALSE;
|
|
||||||
time_t maxage = 24 * 3600; /* default is 24 hours */
|
|
||||||
bool persist = FALSE;
|
|
||||||
bool valid = TRUE;
|
|
||||||
p++;
|
|
||||||
if(*p != ':') {
|
|
||||||
/* hostname starts here */
|
/* hostname starts here */
|
||||||
const char *hostp = p;
|
if(Curl_str_single(&p, '[')) {
|
||||||
if(*p == '[') {
|
if(Curl_str_until(&p, &dsthost, MAX_ALTSVC_HOSTLEN, ':')) {
|
||||||
/* pass all valid IPv6 letters - does not handle zone id */
|
infof(data, "Bad alt-svc hostname, ignoring.");
|
||||||
dstlen = strspn(++p, "0123456789abcdefABCDEF:.");
|
|
||||||
if(p[dstlen] != ']')
|
|
||||||
/* invalid host syntax, bail out */
|
|
||||||
break;
|
break;
|
||||||
/* we store the IPv6 numerical address *with* brackets */
|
}
|
||||||
dstlen += 2;
|
|
||||||
p = &p[dstlen-1];
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
while(*p && (ISALNUM(*p) || (*p == '.') || (*p == '-')))
|
/* IPv6 host name */
|
||||||
p++;
|
if(Curl_str_until(&p, &dsthost, MAX_IPADR_LEN, ']') ||
|
||||||
dstlen = p - hostp;
|
Curl_str_single(&p, ']')) {
|
||||||
}
|
infof(data, "Bad alt-svc IPv6 hostname, ignoring.");
|
||||||
if(!dstlen || (dstlen >= MAX_ALTSVC_HOSTLEN)) {
|
break;
|
||||||
infof(data, "Excessive alt-svc hostname, ignoring.");
|
}
|
||||||
valid = FALSE;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
dsthost = hostp;
|
|
||||||
}
|
}
|
||||||
|
if(Curl_str_single(&p, ':'))
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
/* no destination name, use source host */
|
/* no destination name, use source host */
|
||||||
dsthost = srchost;
|
Curl_str_assign(&dsthost, srchost, strlen(srchost));
|
||||||
dstlen = strlen(srchost);
|
|
||||||
}
|
if(Curl_str_number(&p, &port, 0xffff)) {
|
||||||
if(*p == ':') {
|
infof(data, "Unknown alt-svc port number, ignoring.");
|
||||||
curl_off_t port = 0;
|
|
||||||
p++;
|
|
||||||
if(Curl_str_number(&p, &port, 0xffff) || (*p != '\"')) {
|
|
||||||
infof(data, "Unknown alt-svc port number, ignoring.");
|
|
||||||
valid = FALSE;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
dstport = (unsigned short)port;
|
|
||||||
}
|
|
||||||
if(*p++ != '\"')
|
|
||||||
break;
|
break;
|
||||||
/* Handle the optional 'ma' and 'persist' flags. Unknown flags
|
|
||||||
are skipped. */
|
|
||||||
for(;;) {
|
|
||||||
while(ISBLANK(*p))
|
|
||||||
p++;
|
|
||||||
if(*p != ';')
|
|
||||||
break;
|
|
||||||
p++; /* pass the semicolon */
|
|
||||||
if(!*p || ISNEWLINE(*p))
|
|
||||||
break;
|
|
||||||
result = getalnum(&p, option, sizeof(option));
|
|
||||||
if(result) {
|
|
||||||
/* skip option if name is too long */
|
|
||||||
option[0] = '\0';
|
|
||||||
}
|
|
||||||
while(ISBLANK(*p))
|
|
||||||
p++;
|
|
||||||
if(*p != '=')
|
|
||||||
return CURLE_OK;
|
|
||||||
p++;
|
|
||||||
while(ISBLANK(*p))
|
|
||||||
p++;
|
|
||||||
if(!*p)
|
|
||||||
return CURLE_OK;
|
|
||||||
if(*p == '\"') {
|
|
||||||
/* quoted value */
|
|
||||||
p++;
|
|
||||||
quoted = TRUE;
|
|
||||||
}
|
|
||||||
value_ptr = p;
|
|
||||||
if(quoted) {
|
|
||||||
while(*p && *p != '\"')
|
|
||||||
p++;
|
|
||||||
if(!*p++)
|
|
||||||
return CURLE_OK;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
while(*p && !ISBLANK(*p) && *p!= ';' && *p != ',')
|
|
||||||
p++;
|
|
||||||
}
|
|
||||||
if(!Curl_str_number(&value_ptr, &num, TIME_T_MAX)) {
|
|
||||||
if(strcasecompare("ma", option))
|
|
||||||
maxage = (time_t)num;
|
|
||||||
else if(strcasecompare("persist", option) && (num == 1))
|
|
||||||
persist = TRUE;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if(dstalpnid && valid) {
|
|
||||||
|
dstport = (unsigned short)port;
|
||||||
|
|
||||||
|
if(Curl_str_single(&p, '\"'))
|
||||||
|
break;
|
||||||
|
|
||||||
|
if(dstalpnid) {
|
||||||
if(!entries++)
|
if(!entries++)
|
||||||
/* Flush cached alternatives for this source origin, if any - when
|
/* Flush cached alternatives for this source origin, if any - when
|
||||||
this is the first entry of the line. */
|
this is the first entry of the line. */
|
||||||
altsvc_flush(asi, srcalpnid, srchost, srcport);
|
altsvc_flush(asi, srcalpnid, srchost, srcport);
|
||||||
|
|
||||||
as = altsvc_createid(srchost, srchostlen,
|
as = altsvc_createid(srchost, strlen(srchost),
|
||||||
dsthost, dstlen,
|
Curl_str(&dsthost),
|
||||||
|
Curl_strlen(&dsthost),
|
||||||
srcalpnid, dstalpnid,
|
srcalpnid, dstalpnid,
|
||||||
srcport, dstport);
|
srcport, dstport);
|
||||||
if(as) {
|
if(as) {
|
||||||
@ -647,26 +607,28 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data,
|
|||||||
as->expires = maxage + secs;
|
as->expires = maxage + secs;
|
||||||
as->persist = persist;
|
as->persist = persist;
|
||||||
Curl_llist_append(&asi->list, as, &as->node);
|
Curl_llist_append(&asi->list, as, &as->node);
|
||||||
infof(data, "Added alt-svc: %s:%d over %s", dsthost, dstport,
|
infof(data, "Added alt-svc: %.*s:%d over %s",
|
||||||
Curl_alpnid2str(dstalpnid));
|
(int)Curl_strlen(&dsthost), Curl_str(&dsthost),
|
||||||
|
dstport, Curl_alpnid2str(dstalpnid));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* after the double quote there can be a comma if there is another
|
/* after the double quote there can be a comma if there is another
|
||||||
string or a semicolon if no more */
|
string or a semicolon if no more */
|
||||||
if(*p == ',') {
|
if(Curl_str_single(&p, ','))
|
||||||
/* comma means another alternative is presented */
|
break;
|
||||||
p++;
|
|
||||||
result = getalnum(&p, alpnbuf, sizeof(alpnbuf));
|
/* comma means another alternative is present */
|
||||||
if(result)
|
if(Curl_str_until(&p, &alpn, MAX_ALTSVC_LINE, '='))
|
||||||
break;
|
break;
|
||||||
}
|
Curl_str_trimblanks(&alpn);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
break;
|
break;
|
||||||
} while(*p && (*p != ';') && (*p != '\n') && (*p != '\r'));
|
} while(1);
|
||||||
|
|
||||||
return CURLE_OK;
|
return CURLE_OK;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -31,6 +31,12 @@ void Curl_str_init(struct Curl_str *out)
|
|||||||
out->len = 0;
|
out->len = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Curl_str_assign(struct Curl_str *out, const char *str, size_t len)
|
||||||
|
{
|
||||||
|
out->str = str;
|
||||||
|
out->len = len;
|
||||||
|
}
|
||||||
|
|
||||||
/* Get a word until the first DELIM or end of string. At least one byte long.
|
/* Get a word until the first DELIM or end of string. At least one byte long.
|
||||||
return non-zero on error */
|
return non-zero on error */
|
||||||
int Curl_str_until(const char **linep, struct Curl_str *out,
|
int Curl_str_until(const char **linep, struct Curl_str *out,
|
||||||
|
|||||||
@ -43,6 +43,7 @@ struct Curl_str {
|
|||||||
};
|
};
|
||||||
|
|
||||||
void Curl_str_init(struct Curl_str *out);
|
void Curl_str_init(struct Curl_str *out);
|
||||||
|
void Curl_str_assign(struct Curl_str *out, const char *str, size_t len);
|
||||||
|
|
||||||
#define Curl_str(x) ((x)->str)
|
#define Curl_str(x) ((x)->str)
|
||||||
#define Curl_strlen(x) ((x)->len)
|
#define Curl_strlen(x) ((x)->len)
|
||||||
|
|||||||
@ -71,7 +71,8 @@ UNITTEST_START
|
|||||||
fail_unless(Curl_llist_count(&asi->list) == 6, "wrong number of entries");
|
fail_unless(Curl_llist_count(&asi->list) == 6, "wrong number of entries");
|
||||||
|
|
||||||
result = Curl_altsvc_parse(curl, asi,
|
result = Curl_altsvc_parse(curl, asi,
|
||||||
"h2=\"example.com:8080\", h3=\"yesyes.com\"\r\n",
|
"h2=\"example.com:8080\", "
|
||||||
|
"h3=\"yesyes.com:8080\"\r\n",
|
||||||
ALPN_h1, "3.example.org", 8080);
|
ALPN_h1, "3.example.org", 8080);
|
||||||
fail_if(result, "Curl_altsvc_parse(3) failed!");
|
fail_if(result, "Curl_altsvc_parse(3) failed!");
|
||||||
/* that one should make two entries */
|
/* that one should make two entries */
|
||||||
@ -92,7 +93,8 @@ UNITTEST_START
|
|||||||
|
|
||||||
result =
|
result =
|
||||||
Curl_altsvc_parse(curl, asi,
|
Curl_altsvc_parse(curl, asi,
|
||||||
"h2=\":443\", h3=\":443\"; ma = 120; persist = 1\r\n",
|
"h2=\":443\", h3=\":443\"; "
|
||||||
|
"persist = \"1\"; ma = 120;\r\n",
|
||||||
ALPN_h1, "curl.se", 80);
|
ALPN_h1, "curl.se", 80);
|
||||||
fail_if(result, "Curl_altsvc_parse(5) failed!");
|
fail_if(result, "Curl_altsvc_parse(5) failed!");
|
||||||
fail_unless(Curl_llist_count(&asi->list) == 12, "wrong number of entries");
|
fail_unless(Curl_llist_count(&asi->list) == 12, "wrong number of entries");
|
||||||
@ -103,6 +105,26 @@ UNITTEST_START
|
|||||||
fail_if(result, "Curl_altsvc_parse(6) failed!");
|
fail_if(result, "Curl_altsvc_parse(6) failed!");
|
||||||
fail_unless(Curl_llist_count(&asi->list) == 10, "wrong number of entries");
|
fail_unless(Curl_llist_count(&asi->list) == 10, "wrong number of entries");
|
||||||
|
|
||||||
|
/* only a non-existing alpn */
|
||||||
|
result = Curl_altsvc_parse(curl, asi,
|
||||||
|
"h6=\"example.net:443\"; ma=\"180\";\r\n",
|
||||||
|
ALPN_h2, "5.example.net", 80);
|
||||||
|
|
||||||
|
/* missing quote in alpn host */
|
||||||
|
result = Curl_altsvc_parse(curl, asi,
|
||||||
|
"h2=\"example.net:443,; ma=\"180\";\r\n",
|
||||||
|
ALPN_h2, "6.example.net", 80);
|
||||||
|
|
||||||
|
/* missing port in host name */
|
||||||
|
result = Curl_altsvc_parse(curl, asi,
|
||||||
|
"h2=\"example.net\"; ma=\"180\";\r\n",
|
||||||
|
ALPN_h2, "7.example.net", 80);
|
||||||
|
|
||||||
|
/* illegal port in host name */
|
||||||
|
result = Curl_altsvc_parse(curl, asi,
|
||||||
|
"h2=\"example.net:70000\"; ma=\"180\";\r\n",
|
||||||
|
ALPN_h2, "8.example.net", 80);
|
||||||
|
|
||||||
Curl_altsvc_save(curl, asi, outname);
|
Curl_altsvc_save(curl, asi, outname);
|
||||||
|
|
||||||
curl_easy_cleanup(curl);
|
curl_easy_cleanup(curl);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user