553 lines
16 KiB
C
553 lines
16 KiB
C
/***************************************************************************
|
|
* _ _ ____ _
|
|
* 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 "unitcheck.h"
|
|
|
|
#include "doh.h" /* from the lib dir */
|
|
|
|
/* DoH + HTTPSRR are required */
|
|
#if !defined(CURL_DISABLE_DOH) && defined(USE_HTTPSRR)
|
|
|
|
static CURLcode t1658_setup(void)
|
|
{
|
|
/* whatever you want done first */
|
|
curl_global_init(CURL_GLOBAL_ALL);
|
|
return CURLE_OK;
|
|
}
|
|
|
|
extern CURLcode doh_resp_decode_httpsrr(struct Curl_easy *data,
|
|
const unsigned char *cp, size_t len,
|
|
struct Curl_https_rrinfo **hrr);
|
|
extern void doh_print_httpsrr(struct Curl_easy *data,
|
|
struct Curl_https_rrinfo *hrr);
|
|
|
|
/*
|
|
* The idea here is that we pass one DNS packet at the time to the decoder. we
|
|
* then generate a string output with the results and compare if it matches
|
|
* the expected. One by one.
|
|
*/
|
|
|
|
static char rrbuffer[256];
|
|
static void rrresults(struct Curl_https_rrinfo *rr, CURLcode result)
|
|
{
|
|
char *p = rrbuffer;
|
|
char *pend = rrbuffer + sizeof(rrbuffer);
|
|
curl_msnprintf(rrbuffer, sizeof(rrbuffer), "r:%d|", (int)result);
|
|
p += strlen(rrbuffer);
|
|
|
|
if(rr) {
|
|
unsigned int i;
|
|
curl_msnprintf(p, pend - p, "p:%d|", rr->priority);
|
|
p += strlen(p);
|
|
|
|
curl_msnprintf(p, pend - p, "%s|", rr->target ? rr->target : "-");
|
|
p += strlen(p);
|
|
|
|
for(i = 0; i < MAX_HTTPSRR_ALPNS && rr->alpns[i] != ALPN_none; i++) {
|
|
curl_msnprintf(p, pend - p, "alpn:%x|", rr->alpns[i]);
|
|
p += strlen(p);
|
|
}
|
|
if(rr->no_def_alpn) {
|
|
curl_msnprintf(p, pend - p, "no-def-alpn|");
|
|
p += strlen(p);
|
|
}
|
|
if(rr->port >= 0) {
|
|
curl_msnprintf(p, pend - p, "port:%d|", rr->port);
|
|
p += strlen(p);
|
|
}
|
|
if(rr->ipv4hints) {
|
|
for(i = 0; i < rr->ipv4hints_len; i += 4) {
|
|
curl_msnprintf(p, pend - p, "ipv4:%d.%d.%d.%d|",
|
|
rr->ipv4hints[i],
|
|
rr->ipv4hints[i + 1],
|
|
rr->ipv4hints[i + 2],
|
|
rr->ipv4hints[i + 3]);
|
|
p += strlen(p);
|
|
}
|
|
}
|
|
if(rr->echconfiglist) {
|
|
curl_msnprintf(p, pend - p, "ech:");
|
|
p += strlen(p);
|
|
for(i = 0; i < rr->echconfiglist_len; i++) {
|
|
curl_msnprintf(p, pend - p, "%02x", rr->echconfiglist[i]);
|
|
p += strlen(p);
|
|
}
|
|
curl_msnprintf(p, pend - p, "|");
|
|
p += strlen(p);
|
|
}
|
|
if(rr->ipv6hints) {
|
|
for(i = 0; i < rr->ipv6hints_len; i += 16) {
|
|
int x;
|
|
curl_msnprintf(p, pend - p, "ipv6:");
|
|
p += strlen(p);
|
|
for(x = 0; x < 16; x += 2) {
|
|
curl_msnprintf(p, pend - p, "%s%02x%02x",
|
|
x ? ":" : "",
|
|
rr->ipv6hints[i + x],
|
|
rr->ipv6hints[i + x + 1]);
|
|
p += strlen(p);
|
|
}
|
|
curl_msnprintf(p, pend - p, "|");
|
|
p += strlen(p);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static CURLcode test_unit1658(const char *arg)
|
|
{
|
|
UNITTEST_BEGIN(t1658_setup())
|
|
|
|
/* The "SvcParamKeys" specified within the HTTPS RR packet *must* be
|
|
provided in numerical order. */
|
|
|
|
struct test {
|
|
const char *name;
|
|
const unsigned char *dns;
|
|
size_t len; /* size of the dns packet */
|
|
const char *expect;
|
|
};
|
|
|
|
static const struct test t[] = {
|
|
{
|
|
"single h2 alpn",
|
|
(const unsigned char *)"\x00\x00" /* 16-bit prio */
|
|
"\x04name\x00" /* RNAME */
|
|
"\x00\x01" /* RR (1 == ALPN) */
|
|
"\x00\x03" /* data size */
|
|
"\x02" /* length byte */
|
|
"h2",
|
|
15,
|
|
"r:0|p:0|name.|alpn:10|"
|
|
},
|
|
{
|
|
"single h2 alpn missing last byte",
|
|
(const unsigned char *)"\x00\x00" /* 16-bit prio */
|
|
"\x04name\x00" /* RNAME */
|
|
"\x00\x01" /* RR (1 == ALPN) */
|
|
"\x00\x03" /* data size */
|
|
"\x02" /* length byte */
|
|
"h", /* missing byte */
|
|
14,
|
|
"r:8|"
|
|
},
|
|
{
|
|
"two alpns",
|
|
(const unsigned char *)"\x00\x00" /* 16-bit prio */
|
|
"\x04name\x04some\x00" /* RNAME */
|
|
"\x00\x01" /* RR (1 == ALPN) */
|
|
"\x00\x06" /* data size */
|
|
"\x02" /* ALPN length byte */
|
|
"h2"
|
|
"\x02" /* APLN length byte */
|
|
"h3",
|
|
23,
|
|
"r:0|p:0|name.some.|alpn:10|alpn:20|"
|
|
},
|
|
{
|
|
"wrong syntax alpns",
|
|
(const unsigned char *)"\x00\x00" /* 16-bit prio */
|
|
"\x04name\x04some\x00" /* RNAME */
|
|
"\x00\x01" /* RR (1 == ALPN) */
|
|
"\x00\x06" /* data size */
|
|
"\x02" /* ALPN length byte */
|
|
"h2"
|
|
"\x03" /* APLN length byte (WRONG) */
|
|
"h3",
|
|
23,
|
|
"r:61|"
|
|
},
|
|
{
|
|
"five alpns (ignore dupes)", /* we only support four */
|
|
(const unsigned char *)"\x00\x00" /* 16-bit prio */
|
|
"\x04name\x04some\x00" /* RNAME */
|
|
"\x00\x01" /* RR (1 == ALPN) */
|
|
"\x00\x0f" /* data size */
|
|
"\x02" /* ALPN length byte */
|
|
"h2"
|
|
"\x02" /* ALPN length byte */
|
|
"h2"
|
|
"\x02" /* ALPN length byte */
|
|
"h2"
|
|
"\x02" /* ALPN length byte */
|
|
"h2"
|
|
"\x02" /* APLN length byte */
|
|
"h3",
|
|
32,
|
|
"r:0|p:0|name.some.|alpn:10|alpn:20|"
|
|
},
|
|
{
|
|
"rname only",
|
|
(const unsigned char *)"\x00\x00" /* 16-bit prio */
|
|
"\x04name\x04some\x00", /* RNAME */
|
|
13,
|
|
"r:0|p:0|name.some.|"
|
|
},
|
|
{
|
|
"rname with low ascii byte",
|
|
(const unsigned char *)"\x00\x00" /* 16-bit prio */
|
|
"\x04name\x04som\x03\x00", /* RNAME */
|
|
13,
|
|
"r:8|"
|
|
},
|
|
{
|
|
"rname with null byte",
|
|
(const unsigned char *)"\x00\x00" /* 16-bit prio */
|
|
"\x04sa\x00e\x04some\x00", /* RNAME */
|
|
13,
|
|
"r:27|"
|
|
},
|
|
{
|
|
"rname only (missing byte)",
|
|
(const unsigned char *)"\x00\x00" /* 16-bit prio */
|
|
"\x04name\x05some\x00", /* RNAME */
|
|
/* it lacks a byte */
|
|
13,
|
|
"r:27|"
|
|
},
|
|
{
|
|
"unrecognized alpn",
|
|
(const unsigned char *)"\x00\x00" /* 16-bit prio */
|
|
"\x04name\x04some\x00" /* RNAME */
|
|
"\x00\x01" /* RR (1 == ALPN) */
|
|
"\x00\x06" /* data size */
|
|
"\x02" /* ALPN length byte */
|
|
"h8" /* unrecognized */
|
|
"\x02" /* APLN length byte */
|
|
"h1",
|
|
23,
|
|
"r:0|p:0|name.some.|alpn:8|"
|
|
},
|
|
{
|
|
"alnt + no-default-alpn",
|
|
(const unsigned char *)"\x00\x00" /* 16-bit prio */
|
|
"\x04name\x04some\x00" /* RNAME */
|
|
"\x00\x01" /* RR (1 == ALPN) */
|
|
"\x00\x03" /* data size */
|
|
"\x02" /* ALPN length byte */
|
|
"h2"
|
|
"\x00\x02" /* RR (2 == NO DEFAULT ALPN) */
|
|
"\x00\x00", /* must be zero */
|
|
24,
|
|
"r:0|p:0|name.some.|alpn:10|no-def-alpn|"
|
|
},
|
|
{
|
|
"alnt + no-default-alpn with size",
|
|
(const unsigned char *)"\x00\x00" /* 16-bit prio */
|
|
"\x04name\x04some\x00" /* RNAME */
|
|
"\x00\x01" /* RR (1 == ALPN) */
|
|
"\x00\x03" /* data size */
|
|
"\x02" /* ALPN length byte */
|
|
"h2"
|
|
"\x00\x02" /* RR (2 == NO DEFAULT ALPN) */
|
|
"\x00\x01" /* must be zero */
|
|
"\xff",
|
|
25,
|
|
"r:43|"
|
|
},
|
|
{
|
|
"alnt + no-default-alpn with size too short package",
|
|
(const unsigned char *)"\x00\x00" /* 16-bit prio */
|
|
"\x04name\x04some\x00" /* RNAME */
|
|
"\x00\x01" /* RR (1 == ALPN) */
|
|
"\x00\x03" /* data size */
|
|
"\x02" /* ALPN length byte */
|
|
"h2"
|
|
"\x00\x02" /* RR (2 == NO DEFAULT ALPN) */
|
|
"\x00\x01", /* must be zero */
|
|
/* missing last byte in the packet */
|
|
24,
|
|
"r:8|"
|
|
},
|
|
{
|
|
"rname + blank alpn field",
|
|
(const unsigned char *)"\x11\x11" /* 16-bit prio */
|
|
"\x04name\x04some\x00" /* RNAME */
|
|
"\x00\x01" /* RR (1 == ALPN) */
|
|
"\x00\x00", /* data size, strictly speaking this is illegal:
|
|
"one or more alpn-ids" */
|
|
17,
|
|
"r:0|p:4369|name.some.|"
|
|
},
|
|
{
|
|
"no rname + blank alpn",
|
|
(const unsigned char *)"\x00\x11" /* 16-bit prio */
|
|
"\x00" /* no RNAME */
|
|
"\x00\x01" /* RR (1 == ALPN) */
|
|
"\x00\x00", /* data size */
|
|
7,
|
|
"r:0|p:17|.|"
|
|
},
|
|
{
|
|
"unsupported field",
|
|
(const unsigned char *)"\xff\xff" /* 16-bit prio */
|
|
"\x00" /* no RNAME */
|
|
"\x00\x07" /* RR (7 == not a supported data) */
|
|
"\x00\x02" /* data size */
|
|
"FF", /* unknown to curl */
|
|
9,
|
|
"r:0|p:65535|.|"
|
|
},
|
|
{
|
|
"unsupported field (wrong size)",
|
|
(const unsigned char *)"\xff\xff" /* 16-bit prio */
|
|
"\x00" /* no RNAME */
|
|
"\x00\x07" /* RR (7 == not a supported data) */
|
|
"\x00\x02" /* data size */
|
|
"F", /* unknown to curl */
|
|
8,
|
|
"r:8|"
|
|
},
|
|
{
|
|
"port number",
|
|
(const unsigned char *)"\x00\x10" /* 16-bit prio */
|
|
"\x00" /* no RNAME */
|
|
"\x00\x01" /* RR (1 == ALPN) */
|
|
"\x00\x03" /* data size */
|
|
"\x02" /* ALPN length byte */
|
|
"h2"
|
|
"\x00\x03" /* RR (3 == PORT) */
|
|
"\x00\x02" /* data size */
|
|
"\x12\x34", /* port number */
|
|
16,
|
|
"r:0|p:16|.|alpn:10|port:4660|"
|
|
},
|
|
{
|
|
"port number with wrong size (3 bytes)",
|
|
(const unsigned char *)"\x00\x10" /* 16-bit prio */
|
|
"\x00" /* no RNAME */
|
|
"\x00\x01" /* RR (1 == ALPN) */
|
|
"\x00\x03" /* data size */
|
|
"\x02" /* ALPN length byte */
|
|
"h2"
|
|
"\x00\x03" /* RR (3 == PORT) */
|
|
"\x00\x03" /* data size */
|
|
"\x12\x34\x00", /* 24 bit port number! */
|
|
17,
|
|
"r:43|"
|
|
},
|
|
{
|
|
"port number with wrong size (1 byte)",
|
|
(const unsigned char *)"\x00\x10" /* 16-bit prio */
|
|
"\x00" /* no RNAME */
|
|
"\x00\x01" /* RR (1 == ALPN) */
|
|
"\x00\x03" /* data size */
|
|
"\x02" /* ALPN length byte */
|
|
"h2"
|
|
"\x00\x03" /* RR (3 == PORT) */
|
|
"\x00\x01" /* data size */
|
|
"\x12", /* 8 bit port number! */
|
|
15,
|
|
"r:43|"
|
|
},
|
|
{
|
|
"alpn + two ipv4 addresses",
|
|
(const unsigned char *)"\x00\x10" /* 16-bit prio */
|
|
"\x00" /* no RNAME */
|
|
"\x00\x01" /* RR (1 == ALPN) */
|
|
"\x00\x03" /* data size */
|
|
"\x02" /* ALPN length byte */
|
|
"h2"
|
|
"\x00\x04" /* RR (4 == Ipv4hints) */
|
|
"\x00\x08" /* data size */
|
|
"\xc0\xa8\x00\x01" /* 32 bits */
|
|
"\xc0\xa8\x00\x02", /* 32 bits */
|
|
22,
|
|
"r:0|p:16|.|alpn:10|ipv4:192.168.0.1|ipv4:192.168.0.2|"
|
|
},
|
|
{
|
|
"alpn + two ipv4 addresses in wrong order",
|
|
(const unsigned char *)"\x00\x10" /* 16-bit prio */
|
|
"\x00" /* no RNAME */
|
|
"\x00\x04" /* RR (4 == Ipv4hints) */
|
|
"\x00\x08" /* data size */
|
|
"\xc0\xa8\x00\x01" /* 32 bits */
|
|
"\xc0\xa8\x00\x02" /* 32 bits */
|
|
"\x00\x01" /* RR (1 == ALPN) */
|
|
"\x00\x03" /* data size */
|
|
"\x02" /* ALPN length byte */
|
|
"h2",
|
|
22,
|
|
"r:8|"
|
|
},
|
|
{
|
|
"alpn + ipv4 address with wrong size",
|
|
(const unsigned char *)"\x00\x10" /* 16-bit prio */
|
|
"\x00" /* no RNAME */
|
|
"\x00\x01" /* RR (1 == ALPN) */
|
|
"\x00\x03" /* data size */
|
|
"\x02" /* ALPN length byte */
|
|
"h2"
|
|
"\x00\x04" /* RR (4 == Ipv4hints) */
|
|
"\x00\x05" /* data size */
|
|
"\xc0\xa8\x00\x01\xff", /* 32 + 8 bits */
|
|
19,
|
|
"r:43|"
|
|
},
|
|
{
|
|
"alpn + one ipv6 address",
|
|
(const unsigned char *)"\x00\x10" /* 16-bit prio */
|
|
"\x00" /* no RNAME */
|
|
"\x00\x01" /* RR (1 == ALPN) */
|
|
"\x00\x03" /* data size */
|
|
"\x02" /* ALPN length byte */
|
|
"h2"
|
|
"\x00\x06" /* RR (6 == Ipv6hints) */
|
|
"\x00\x10" /* data size */
|
|
"\xfe\x80\xda\xbb\xc1\xff\xfe\xa3\x8a\x22\x12\x34\x56\x78\x91\x23",
|
|
30,
|
|
"r:0|p:16|.|alpn:10|ipv6:fe80:dabb:c1ff:fea3:8a22:1234:5678:9123|"
|
|
},
|
|
{
|
|
"alpn + one ipv6 address with wrong size",
|
|
(const unsigned char *)"\x00\x10" /* 16-bit prio */
|
|
"\x00" /* no RNAME */
|
|
"\x00\x01" /* RR (1 == ALPN) */
|
|
"\x00\x03" /* data size */
|
|
"\x02" /* ALPN length byte */
|
|
"h2"
|
|
"\x00\x06" /* RR (6 == Ipv6hints) */
|
|
"\x00\x11" /* data size */
|
|
"\xfe\x80\xda\xbb\xc1\xff\xfe\xa3\x8a\x22\x12\x34\x56\x78\x91\x23\x45",
|
|
31,
|
|
"r:43|"
|
|
},
|
|
{
|
|
"alpn + two ipv6 addresses",
|
|
(const unsigned char *)"\x00\x10" /* 16-bit prio */
|
|
"\x00" /* no RNAME */
|
|
"\x00\x01" /* RR (1 == ALPN) */
|
|
"\x00\x03" /* data size */
|
|
"\x02" /* ALPN length byte */
|
|
"h2"
|
|
"\x00\x06" /* RR (6 == Ipv6hints) */
|
|
"\x00\x20" /* data size */
|
|
"\xfe\x80\xda\xbb\xc1\xff\xfe\xa3\x8a\x22\x12\x34\x56\x78\x91\x23"
|
|
"\xee\x80\xda\xbb\xc1\xff\xfe\xa3\x8a\x22\x12\x34\x56\x78\x91\x25",
|
|
46,
|
|
"r:0|p:16|.|alpn:10|ipv6:fe80:dabb:c1ff:fea3:8a22:1234:5678:9123|"
|
|
"ipv6:ee80:dabb:c1ff:fea3:8a22:1234:5678:9125|"
|
|
},
|
|
{
|
|
"alpn + ech",
|
|
(const unsigned char *)"\x00\x10" /* 16-bit prio */
|
|
"\x00" /* no RNAME */
|
|
"\x00\x01" /* RR (1 == ALPN) */
|
|
"\x00\x03" /* data size */
|
|
"\x02" /* ALPN length byte */
|
|
"h2"
|
|
"\x00\x05" /* RR (5 == ECH) */
|
|
"\x00\x10" /* data size */
|
|
"\xfe\x80\xda\xbb\xc1\xff\xfe\xa3\x8a\x22\x12\x34\x56\x78\x91\x23",
|
|
30,
|
|
"r:0|p:16|.|alpn:10|ech:fe80dabbc1fffea38a22123456789123|"
|
|
},
|
|
{
|
|
"fully packed",
|
|
(const unsigned char *)"\xa0\x0b" /* 16-bit prio */
|
|
"\x00" /* no RNAME */
|
|
"\x00\x00" /* RR (0 == MANDATORY) */
|
|
"\x00\x00" /* data size */
|
|
"\x00\x01" /* RR (1 == ALPN) */
|
|
"\x00\x06" /* data size */
|
|
"\x02" /* ALPN length byte */
|
|
"h2"
|
|
"\x02" /* ALPN length byte */
|
|
"h1"
|
|
"\x00\x02" /* RR (2 == NO DEFAULT ALPN) */
|
|
"\x00\x00" /* must be zero */
|
|
"\x00\x03" /* RR (3 == PORT) */
|
|
"\x00\x02" /* data size */
|
|
"\xbc\x71" /* port number */
|
|
"\x00\x04" /* RR (4 == Ipv4hints) */
|
|
"\x00\x08" /* data size */
|
|
"\xc0\xa8\x00\x01" /* 32 bits */
|
|
"\xc0\xa8\x00\x02" /* 32 bits */
|
|
"\x00\x05" /* RR (5 == ECH) */
|
|
"\x00\x10" /* data size */
|
|
"\xfe\x80\xda\xbb\xc1\xff\x7e\xb3\x8a\x22\x12\x34\x56\x78\x91\x23"
|
|
"\x00\x06" /* RR (6 == Ipv6hints) */
|
|
"\x00\x20" /* data size */
|
|
"\xfe\x80\xda\xbb\xc1\xff\xfe\xa3\x8a\x22\x12\x34\x56\x78\x91\x23"
|
|
"\xee\x80\xda\xbb\xc1\xff\xfe\xa3\x8a\x22\x12\x34\x56\x78\x91\x25"
|
|
"\x01\x07" /* RR (263 == not supported) */
|
|
"\x00\x04" /* data size */
|
|
"FFAA", /* unknown to the world */
|
|
103,
|
|
"r:0|p:40971|.|alpn:10|alpn:8|no-def-alpn|port:48241|"
|
|
"ipv4:192.168.0.1|ipv4:192.168.0.2|"
|
|
"ech:fe80dabbc1ff7eb38a22123456789123|"
|
|
"ipv6:fe80:dabb:c1ff:fea3:8a22:1234:5678:9123|"
|
|
"ipv6:ee80:dabb:c1ff:fea3:8a22:1234:5678:9125|"
|
|
}
|
|
};
|
|
|
|
CURLcode result = CURLE_OUT_OF_MEMORY;
|
|
CURL *easy;
|
|
|
|
easy = curl_easy_init();
|
|
/* so that we get the log output: */
|
|
curl_easy_setopt(easy, CURLOPT_VERBOSE, 1L);
|
|
if(easy) {
|
|
unsigned int i;
|
|
|
|
for(i = 0; i < CURL_ARRAYSIZE(t); i++) {
|
|
struct Curl_https_rrinfo *hrr;
|
|
|
|
curl_mprintf("test %u: %s\n", i, t[i].name);
|
|
|
|
result = doh_resp_decode_httpsrr(easy, t[i].dns, t[i].len, &hrr);
|
|
|
|
/* create an output */
|
|
rrresults(hrr, result);
|
|
|
|
/* is the output the expected? */
|
|
if(strcmp(rrbuffer, t[i].expect)) {
|
|
curl_mfprintf(stderr, "Test %s (%u) failed\n"
|
|
"Expected: %s\n"
|
|
"Received: %s\n", t[i].name, i, t[i].expect, rrbuffer);
|
|
unitfail++;
|
|
}
|
|
|
|
/* free the generated struct */
|
|
if(hrr) {
|
|
Curl_httpsrr_cleanup(hrr);
|
|
curl_free(hrr);
|
|
}
|
|
}
|
|
curl_easy_cleanup(easy);
|
|
}
|
|
|
|
UNITTEST_END(curl_global_cleanup())
|
|
}
|
|
|
|
#else /* CURL_DISABLE_DOH or not HTTPSRR enabled */
|
|
|
|
static CURLcode test_unit1658(const char *arg)
|
|
{
|
|
UNITTEST_BEGIN_SIMPLE
|
|
UNITTEST_END_SIMPLE
|
|
}
|
|
|
|
#endif
|