mirror of
https://github.com/curl/curl.git
synced 2025-04-24 16:40:32 +08:00
doh: send HTTPS RR requests for all HTTP(S) transfers
When enabled in the build. Update test2100: verify with HTTPS RR included Adjust runtests and server/disabled.c to include "HTTPSRR" as a feature in the test suite. Also, decode the ALPN list in HTTPS records straight into IDs. There's no point in storing everything in string format. Skip ALPNs we do not support. Closes #16007
This commit is contained in:
parent
5d70a5c5a4
commit
bb93536270
24
lib/altsvc.c
24
lib/altsvc.c
@ -41,6 +41,7 @@
|
||||
#include "strdup.h"
|
||||
#include "inet_pton.h"
|
||||
#include "strparse.h"
|
||||
#include "connect.h"
|
||||
|
||||
/* The last 3 #include files should be in this order */
|
||||
#include "curl_printf.h"
|
||||
@ -54,23 +55,6 @@
|
||||
|
||||
#define H3VERSION "h3"
|
||||
|
||||
static enum alpnid alpn2alpnid(char *name, size_t len)
|
||||
{
|
||||
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 */
|
||||
}
|
||||
|
||||
/* Given the ALPN ID, return the name */
|
||||
const char *Curl_alpnid2str(enum alpnid id)
|
||||
{
|
||||
@ -154,8 +138,8 @@ static struct altsvc *altsvc_create(struct Curl_str *srchost,
|
||||
size_t srcport,
|
||||
size_t dstport)
|
||||
{
|
||||
enum alpnid dstalpnid = alpn2alpnid(dstalpn->str, dstalpn->len);
|
||||
enum alpnid srcalpnid = alpn2alpnid(srcalpn->str, srcalpn->len);
|
||||
enum alpnid dstalpnid = Curl_alpn2alpnid(dstalpn->str, dstalpn->len);
|
||||
enum alpnid srcalpnid = Curl_alpn2alpnid(srcalpn->str, srcalpn->len);
|
||||
if(!srcalpnid || !dstalpnid)
|
||||
return NULL;
|
||||
return altsvc_createid(srchost->str, srchost->len,
|
||||
@ -537,7 +521,7 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data,
|
||||
do {
|
||||
if(*p == '=') {
|
||||
/* [protocol]="[host][:port]" */
|
||||
enum alpnid dstalpnid = alpn2alpnid(alpnbuf, alpnlen);
|
||||
enum alpnid dstalpnid = Curl_alpn2alpnid(alpnbuf, alpnlen);
|
||||
p++;
|
||||
if(*p == '\"') {
|
||||
const char *dsthost = "";
|
||||
|
@ -29,13 +29,6 @@
|
||||
#include <curl/curl.h>
|
||||
#include "llist.h"
|
||||
|
||||
enum alpnid {
|
||||
ALPN_none = 0,
|
||||
ALPN_h1 = CURLALTSVC_H1,
|
||||
ALPN_h2 = CURLALTSVC_H2,
|
||||
ALPN_h3 = CURLALTSVC_H3
|
||||
};
|
||||
|
||||
struct althost {
|
||||
char *host;
|
||||
unsigned short port;
|
||||
|
@ -78,6 +78,7 @@
|
||||
#include "vquic/vquic.h" /* for quic cfilters */
|
||||
#include "http_proxy.h"
|
||||
#include "socks.h"
|
||||
#include "strcase.h"
|
||||
|
||||
/* The last 3 #include files should be in this order */
|
||||
#include "curl_printf.h"
|
||||
@ -88,6 +89,27 @@
|
||||
#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
|
||||
#endif
|
||||
|
||||
#if !defined(CURL_DISABLE_ALTSVC) || defined(USE_HTTPSRR)
|
||||
|
||||
enum alpnid Curl_alpn2alpnid(char *name, size_t len)
|
||||
{
|
||||
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 */
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Curl_timeleft() returns the amount of milliseconds left allowed for the
|
||||
* transfer/connection. If the value is 0, there is no timeout (ie there is
|
||||
|
@ -32,6 +32,8 @@
|
||||
struct Curl_dns_entry;
|
||||
struct ip_quadruple;
|
||||
|
||||
enum alpnid Curl_alpn2alpnid(char *name, size_t len);
|
||||
|
||||
/* generic function that returns how much time there is left to run, according
|
||||
to the timeouts set */
|
||||
timediff_t Curl_timeleft(struct Curl_easy *data,
|
||||
|
59
lib/doh.c
59
lib/doh.c
@ -456,17 +456,8 @@ struct Curl_addrinfo *Curl_doh(struct Curl_easy *data,
|
||||
#endif
|
||||
|
||||
#ifdef USE_HTTPSRR
|
||||
/*
|
||||
* TODO: Figure out the conditions under which we want to make a request for
|
||||
* an HTTPS RR when we are not doing ECH. For now, making this request
|
||||
* breaks a bunch of DoH tests, e.g. test2100, where the additional request
|
||||
* does not match the pre-cooked data files, so there is a bit of work
|
||||
* attached to making the request in a non-ECH use-case. For the present, we
|
||||
* will only make the request when ECH is enabled in the build and is being
|
||||
* used for the curl operation.
|
||||
*/
|
||||
# ifdef USE_ECH
|
||||
if(data->set.tls_ech & (CURLECH_ENABLE|CURLECH_HARD)) {
|
||||
if(conn->handler->protocol & PROTO_FAMILY_HTTP) {
|
||||
/* Only use HTTPS RR for HTTP(S) transfers */
|
||||
char *qname = NULL;
|
||||
if(port != PORT_HTTPS) {
|
||||
qname = aprintf("_%d._https.%s", port, hostname);
|
||||
@ -482,7 +473,6 @@ struct Curl_addrinfo *Curl_doh(struct Curl_easy *data,
|
||||
goto error;
|
||||
dohp->pending++;
|
||||
}
|
||||
# endif
|
||||
#endif
|
||||
*waitp = TRUE; /* this never returns synchronously */
|
||||
return NULL;
|
||||
@ -1075,7 +1065,7 @@ static CURLcode doh_decode_rdata_name(unsigned char **buf, size_t *remaining,
|
||||
}
|
||||
|
||||
static CURLcode doh_decode_rdata_alpn(unsigned char *cp, size_t len,
|
||||
char **alpns)
|
||||
unsigned char *alpns)
|
||||
{
|
||||
/*
|
||||
* spec here is as per RFC 9460, section-7.1.1
|
||||
@ -1088,19 +1078,14 @@ static CURLcode doh_decode_rdata_alpn(unsigned char *cp, size_t len,
|
||||
* backslash - same goes for a backslash character, and of course
|
||||
* we need to use two backslashes in strings when we mean one;-)
|
||||
*/
|
||||
char *oval;
|
||||
struct dynbuf dval;
|
||||
int idnum = 0;
|
||||
|
||||
if(!alpns)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
Curl_dyn_init(&dval, DYN_DOH_RESPONSE);
|
||||
while(len > 0) {
|
||||
size_t tlen = (size_t) *cp++;
|
||||
size_t i;
|
||||
|
||||
/* if not 1st time, add comma */
|
||||
if(Curl_dyn_len(&dval) && Curl_dyn_addn(&dval, ",", 1))
|
||||
goto err;
|
||||
enum alpnid id;
|
||||
len--;
|
||||
if(tlen > len)
|
||||
goto err;
|
||||
@ -1114,12 +1099,18 @@ static CURLcode doh_decode_rdata_alpn(unsigned char *cp, size_t len,
|
||||
goto err;
|
||||
}
|
||||
len -= tlen;
|
||||
|
||||
/* we only store ALPN ids we know about */
|
||||
id = Curl_alpn2alpnid(Curl_dyn_ptr(&dval), Curl_dyn_len(&dval));
|
||||
if(id != ALPN_none) {
|
||||
if(idnum == MAX_HTTPSRR_ALPNS)
|
||||
break;
|
||||
alpns[idnum++] = id;
|
||||
}
|
||||
/* this string is always null terminated */
|
||||
oval = Curl_dyn_ptr(&dval);
|
||||
if(!oval)
|
||||
goto err;
|
||||
*alpns = oval;
|
||||
Curl_dyn_reset(&dval);
|
||||
}
|
||||
if(idnum < MAX_HTTPSRR_ALPNS)
|
||||
alpns[idnum] = ALPN_none; /* terminate the list */
|
||||
return CURLE_OK;
|
||||
err:
|
||||
Curl_dyn_free(&dval);
|
||||
@ -1137,14 +1128,12 @@ static CURLcode doh_test_alpn_escapes(void)
|
||||
0x68, 0x32 /* value "h2" */
|
||||
};
|
||||
size_t example_len = sizeof(example);
|
||||
char *aval = NULL;
|
||||
static const char *expected = "f\\\\oo\\,bar,h2";
|
||||
unsigned char aval[MAX_HTTPSRR_ALPNS] = { 0 };
|
||||
static const char expected[2] = { ALPN_h2, ALPN_none };
|
||||
|
||||
if(doh_decode_rdata_alpn(example, example_len, &aval) != CURLE_OK)
|
||||
if(doh_decode_rdata_alpn(example, example_len, aval) != CURLE_OK)
|
||||
return CURLE_BAD_CONTENT_ENCODING;
|
||||
if(strlen(aval) != strlen(expected))
|
||||
return CURLE_BAD_CONTENT_ENCODING;
|
||||
if(memcmp(aval, expected, strlen(aval)))
|
||||
if(memcmp(aval, expected, sizeof(expected)))
|
||||
return CURLE_BAD_CONTENT_ENCODING;
|
||||
return CURLE_OK;
|
||||
}
|
||||
@ -1181,7 +1170,7 @@ static CURLcode doh_resp_decode_httpsrr(unsigned char *cp, size_t len,
|
||||
len -= 4;
|
||||
switch(pcode) {
|
||||
case HTTPS_RR_CODE_ALPN:
|
||||
if(doh_decode_rdata_alpn(cp, plen, &lhrr->alpns) != CURLE_OK)
|
||||
if(doh_decode_rdata_alpn(cp, plen, lhrr->alpns) != CURLE_OK)
|
||||
goto err;
|
||||
break;
|
||||
case HTTPS_RR_CODE_NO_DEF_ALPN:
|
||||
@ -1228,7 +1217,6 @@ static CURLcode doh_resp_decode_httpsrr(unsigned char *cp, size_t len,
|
||||
err:
|
||||
Curl_safefree(lhrr->target);
|
||||
Curl_safefree(lhrr->echconfiglist);
|
||||
Curl_safefree(lhrr->alpns);
|
||||
Curl_safefree(lhrr);
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
}
|
||||
@ -1240,8 +1228,9 @@ static void doh_print_httpsrr(struct Curl_easy *data,
|
||||
DEBUGASSERT(hrr);
|
||||
infof(data, "HTTPS RR: priority %d, target: %s",
|
||||
hrr->priority, hrr->target);
|
||||
if(hrr->alpns)
|
||||
infof(data, "HTTPS RR: alpns %s", hrr->alpns);
|
||||
if(hrr->alpns[0] != ALPN_none)
|
||||
infof(data, "HTTPS RR: alpns %u %u %u %u",
|
||||
hrr->alpns[0], hrr->alpns[1], hrr->alpns[2], hrr->alpns[3]);
|
||||
else
|
||||
infof(data, "HTTPS RR: no alpns");
|
||||
if(hrr->no_def_alpn)
|
||||
|
@ -1080,7 +1080,6 @@ static void hostcache_unlink_entry(void *entry)
|
||||
#ifdef USE_HTTPSRR
|
||||
if(dns->hinfo) {
|
||||
free(dns->hinfo->target);
|
||||
free(dns->hinfo->alpns);
|
||||
free(dns->hinfo->ipv4hints);
|
||||
free(dns->hinfo->echconfiglist);
|
||||
free(dns->hinfo->ipv6hints);
|
||||
|
11
lib/hostip.h
11
lib/hostip.h
@ -53,6 +53,13 @@ struct hostent;
|
||||
struct Curl_easy;
|
||||
struct connectdata;
|
||||
|
||||
enum alpnid {
|
||||
ALPN_none = 0,
|
||||
ALPN_h1 = CURLALTSVC_H1,
|
||||
ALPN_h2 = CURLALTSVC_H2,
|
||||
ALPN_h3 = CURLALTSVC_H3
|
||||
};
|
||||
|
||||
/*
|
||||
* Curl_global_host_cache_init() initializes and sets up a global DNS cache.
|
||||
* Global DNS cache is general badness. Do not use. This will be removed in
|
||||
@ -65,6 +72,7 @@ struct Curl_hash *Curl_global_host_cache_init(void);
|
||||
#ifdef USE_HTTPSRR
|
||||
|
||||
#define CURL_MAXLEN_host_name 253
|
||||
#define MAX_HTTPSRR_ALPNS 4
|
||||
|
||||
struct Curl_https_rrinfo {
|
||||
/*
|
||||
@ -72,13 +80,14 @@ struct Curl_https_rrinfo {
|
||||
* See https://datatracker.ietf.org/doc/html/rfc9460#section-14.3.2
|
||||
*/
|
||||
char *target;
|
||||
char *alpns; /* keytag = 1 */
|
||||
unsigned char *ipv4hints; /* keytag = 4 */
|
||||
size_t ipv4hints_len;
|
||||
unsigned char *echconfiglist; /* keytag = 5 */
|
||||
size_t echconfiglist_len;
|
||||
unsigned char *ipv6hints; /* keytag = 6 */
|
||||
size_t ipv6hints_len;
|
||||
unsigned char alpns[MAX_HTTPSRR_ALPNS]; /* keytag = 1 */
|
||||
/* store parsed alpnid entries in the array, end with ALPN_none */
|
||||
int port; /* -1 means not set */
|
||||
uint16_t priority;
|
||||
bool no_def_alpn; /* keytag = 2 */
|
||||
|
@ -451,6 +451,7 @@ Features testable here are:
|
||||
- `http/2`
|
||||
- `http/3`
|
||||
- `HTTPS-proxy`
|
||||
- `HTTPSRR`
|
||||
- `IDN`
|
||||
- `IPv6`
|
||||
- `Kerberos`
|
||||
|
@ -52,7 +52,7 @@ DoH
|
||||
IPv6
|
||||
</features>
|
||||
<name>
|
||||
HTTP GET using DoH
|
||||
HTTP GET using DoH (with HTTPS RR)
|
||||
</name>
|
||||
<command>
|
||||
http://foo.example.com:%HTTPPORT/%TESTNUMBER --doh-url http://%HOSTIP:%HTTPPORT/%TESTNUMBER0001
|
||||
@ -70,6 +70,31 @@ http://foo.example.com:%HTTPPORT/%TESTNUMBER --doh-url http://%HOSTIP:%HTTPPORT/
|
||||
s/com\x00\x00(\x1c|\x01)/com-00-00!/g;
|
||||
</strippart>
|
||||
<protocol>
|
||||
%if HTTPSRR
|
||||
POST /%TESTNUMBER0001 HTTP/1.1
|
||||
Host: %HOSTIP:%HTTPPORT
|
||||
Accept: */*
|
||||
Content-Type: application/dns-message
|
||||
Content-Length: 33
|
||||
|
||||
%hex[%00%00%01%00%00%01%00%00%00%00%00%00%03foo%07example%03com-00-00!%00%01]hex%POST /%TESTNUMBER0001 HTTP/1.1
|
||||
Host: %HOSTIP:%HTTPPORT
|
||||
Accept: */*
|
||||
Content-Type: application/dns-message
|
||||
Content-Length: 33
|
||||
|
||||
%hex[%00%00%01%00%00%01%00%00%00%00%00%00%03foo%07example%03com-00-00!%00%01]hex%POST /%TESTNUMBER0001 HTTP/1.1
|
||||
Host: %HOSTIP:%HTTPPORT
|
||||
Accept: */*
|
||||
Content-Type: application/dns-message
|
||||
Content-Length: 47
|
||||
|
||||
%hex[%00%00%01%00%00%01%00%00%00%00%00%00%06_%HTTPPORT%06_https%03foo%07example%03com%00%00A%00%01]hex%GET /%TESTNUMBER HTTP/1.1
|
||||
Host: foo.example.com:%HTTPPORT
|
||||
User-Agent: curl/%VERSION
|
||||
Accept: */*
|
||||
|
||||
%else
|
||||
POST /%TESTNUMBER0001 HTTP/1.1
|
||||
Host: %HOSTIP:%HTTPPORT
|
||||
Accept: */*
|
||||
@ -87,6 +112,7 @@ Host: foo.example.com:%HTTPPORT
|
||||
User-Agent: curl/%VERSION
|
||||
Accept: */*
|
||||
|
||||
%endif
|
||||
</protocol>
|
||||
</verify>
|
||||
</testcase>
|
||||
|
@ -822,6 +822,7 @@ sub checksystemfeatures {
|
||||
$feature{"large-time"} = 1;
|
||||
$feature{"large-size"} = 1;
|
||||
$feature{"sha512-256"} = 1;
|
||||
$feature{"HTTPSRR"} = 1;
|
||||
$feature{"local-http"} = servers::localhttp();
|
||||
$feature{"codeset-utf8"} = lc(langinfo(CODESET())) eq "utf-8";
|
||||
|
||||
|
@ -115,6 +115,9 @@ static const char *disabled[]={
|
||||
#ifndef CURL_CA_SEARCH_SAFE
|
||||
"win32-ca-search-safe",
|
||||
#endif
|
||||
#endif
|
||||
#ifndef USE_HTTPSRR
|
||||
"HTTPSRR",
|
||||
#endif
|
||||
NULL
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user