+22 November 2019: Wouter
+ - Fix dname loop maximum, reported by Eric Sesterhenn from X41 D-Sec.
+
20 November 2019: Wouter
- Fix Out of Bounds Read in rrinternal_get_owner(),
reported by X41 D-Sec.
if(i >= data->count) tp = LDNS_RR_TYPE_RRSIG;
dat = nm;
datlen = nmlen;
- w += sldns_wire2str_dname_scan(&dat, &datlen, &s, &slen, NULL, 0);
+ w += sldns_wire2str_dname_scan(&dat, &datlen, &s, &slen, NULL, 0, NULL);
w += sldns_str_print(&s, &slen, "\t");
w += sldns_str_print(&s, &slen, "%lu\t", (unsigned long)data->rr_ttl[i]);
w += sldns_wire2str_class_print(&s, &slen, cl);
w += sldns_str_print(&s, &slen, "\t");
datlen = data->rr_len[i]-2;
dat = data->rr_data[i]+2;
- w += sldns_wire2str_rdata_scan(&dat, &datlen, &s, &slen, tp, NULL, 0);
+ w += sldns_wire2str_rdata_scan(&dat, &datlen, &s, &slen, tp, NULL, 0, NULL);
if(tp == LDNS_RR_TYPE_DNSKEY) {
w += sldns_str_print(&s, &slen, " ;{id = %u}",
int sldns_wire2str_rr_buf(uint8_t* d, size_t dlen, char* s, size_t slen)
{
/* use arguments as temporary variables */
- return sldns_wire2str_rr_scan(&d, &dlen, &s, &slen, NULL, 0);
+ return sldns_wire2str_rr_scan(&d, &dlen, &s, &slen, NULL, 0, NULL);
}
int sldns_wire2str_rrquestion_buf(uint8_t* d, size_t dlen, char* s, size_t slen)
{
/* use arguments as temporary variables */
- return sldns_wire2str_rrquestion_scan(&d, &dlen, &s, &slen, NULL, 0);
+ return sldns_wire2str_rrquestion_scan(&d, &dlen, &s, &slen, NULL, 0, NULL);
}
int sldns_wire2str_rdata_buf(uint8_t* rdata, size_t rdata_len, char* str,
{
/* use arguments as temporary variables */
return sldns_wire2str_rdata_scan(&rdata, &rdata_len, &str, &str_len,
- rrtype, NULL, 0);
+ rrtype, NULL, 0, NULL);
}
int sldns_wire2str_rr_unknown_buf(uint8_t* d, size_t dlen, char* s, size_t slen)
{
/* use arguments as temporary variables */
- return sldns_wire2str_rr_unknown_scan(&d, &dlen, &s, &slen, NULL, 0);
+ return sldns_wire2str_rr_unknown_scan(&d, &dlen, &s, &slen, NULL, 0, NULL);
}
int sldns_wire2str_rr_comment_buf(uint8_t* rr, size_t rrlen, size_t dname_len,
int sldns_wire2str_dname_buf(uint8_t* d, size_t dlen, char* s, size_t slen)
{
/* use arguments as temporary variables */
- return sldns_wire2str_dname_scan(&d, &dlen, &s, &slen, NULL, 0);
+ return sldns_wire2str_dname_scan(&d, &dlen, &s, &slen, NULL, 0, NULL);
}
int sldns_str_vprint(char** str, size_t* slen, const char* format, va_list args)
int sldns_wire2str_pkt_scan(uint8_t** d, size_t* dlen, char** s, size_t* slen)
{
- int w = 0;
+ int w = 0, comprloop = 0;
unsigned qdcount, ancount, nscount, arcount, i;
uint8_t* pkt = *d;
size_t pktlen = *dlen;
w += sldns_str_print(s, slen, ";; QUESTION SECTION:\n");
for(i=0; i<qdcount; i++) {
w += sldns_wire2str_rrquestion_scan(d, dlen, s, slen,
- pkt, pktlen);
+ pkt, pktlen, &comprloop);
if(!*dlen) break;
}
w += sldns_str_print(s, slen, "\n");
w += sldns_str_print(s, slen, ";; ANSWER SECTION:\n");
for(i=0; i<ancount; i++) {
- w += sldns_wire2str_rr_scan(d, dlen, s, slen, pkt, pktlen);
+ w += sldns_wire2str_rr_scan(d, dlen, s, slen, pkt, pktlen, &comprloop);
if(!*dlen) break;
}
w += sldns_str_print(s, slen, "\n");
w += sldns_str_print(s, slen, ";; AUTHORITY SECTION:\n");
for(i=0; i<nscount; i++) {
- w += sldns_wire2str_rr_scan(d, dlen, s, slen, pkt, pktlen);
+ w += sldns_wire2str_rr_scan(d, dlen, s, slen, pkt, pktlen, &comprloop);
if(!*dlen) break;
}
w += sldns_str_print(s, slen, "\n");
w += sldns_str_print(s, slen, ";; ADDITIONAL SECTION:\n");
for(i=0; i<arcount; i++) {
- w += sldns_wire2str_rr_scan(d, dlen, s, slen, pkt, pktlen);
+ w += sldns_wire2str_rr_scan(d, dlen, s, slen, pkt, pktlen, &comprloop);
if(!*dlen) break;
}
/* other fields: WHEN(time), SERVER(IP) not available here. */
}
int sldns_wire2str_rr_scan(uint8_t** d, size_t* dlen, char** s, size_t* slen,
- uint8_t* pkt, size_t pktlen)
+ uint8_t* pkt, size_t pktlen, int* comprloop)
{
int w = 0;
uint8_t* rr = *d;
/* try to scan the rdata with pretty-printing, but if that fails, then
* scan the rdata as an unknown RR type */
- w += sldns_wire2str_dname_scan(d, dlen, s, slen, pkt, pktlen);
+ w += sldns_wire2str_dname_scan(d, dlen, s, slen, pkt, pktlen, comprloop);
w += sldns_str_print(s, slen, "\t");
dname_off = rrlen-(*dlen);
if(*dlen == 4) {
w += print_remainder_hex(";Error partial rdata 0x", d, dlen, s, slen);
return w + sldns_str_print(s, slen, "\n");
}
- w += sldns_wire2str_rdata_scan(d, &rdlen, s, slen, rrtype, pkt, pktlen);
+ w += sldns_wire2str_rdata_scan(d, &rdlen, s, slen, rrtype, pkt, pktlen,
+ comprloop);
(*dlen) -= (ordlen-rdlen);
/* default comment */
}
int sldns_wire2str_rrquestion_scan(uint8_t** d, size_t* dlen, char** s,
- size_t* slen, uint8_t* pkt, size_t pktlen)
+ size_t* slen, uint8_t* pkt, size_t pktlen, int* comprloop)
{
int w = 0;
uint16_t t, c;
- w += sldns_wire2str_dname_scan(d, dlen, s, slen, pkt, pktlen);
+ w += sldns_wire2str_dname_scan(d, dlen, s, slen, pkt, pktlen, comprloop);
w += sldns_str_print(s, slen, "\t");
if(*dlen < 4) {
if(*dlen == 0)
}
int sldns_wire2str_rr_unknown_scan(uint8_t** d, size_t* dlen, char** s,
- size_t* slen, uint8_t* pkt, size_t pktlen)
+ size_t* slen, uint8_t* pkt, size_t pktlen, int* comprloop)
{
size_t rdlen, ordlen;
int w = 0;
- w += sldns_wire2str_dname_scan(d, dlen, s, slen, pkt, pktlen);
+ w += sldns_wire2str_dname_scan(d, dlen, s, slen, pkt, pktlen, comprloop);
w += sldns_str_print(s, slen, "\t");
w += sldns_rr_tcttl_scan(d, dlen, s, slen);
w += sldns_str_print(s, slen, "\t");
}
int sldns_wire2str_rdata_scan(uint8_t** d, size_t* dlen, char** s,
- size_t* slen, uint16_t rrtype, uint8_t* pkt, size_t pktlen)
+ size_t* slen, uint16_t rrtype, uint8_t* pkt, size_t pktlen,
+ int* comprloop)
{
/* try to prettyprint, but if that fails, use unknown format */
uint8_t* origd = *d;
if(r_cnt != 0)
w += sldns_str_print(s, slen, " ");
n = sldns_wire2str_rdf_scan(d, dlen, s, slen, rdftype,
- pkt, pktlen);
+ pkt, pktlen, comprloop);
if(n == -1) {
failed:
/* failed, use unknown format */
}
int sldns_wire2str_dname_scan(uint8_t** d, size_t* dlen, char** s, size_t* slen,
- uint8_t* pkt, size_t pktlen)
+ uint8_t* pkt, size_t pktlen, int* comprloop)
{
int w = 0;
/* spool labels onto the string, use compression if its there */
uint8_t* pos = *d;
unsigned i, counter=0;
- const unsigned maxcompr = 1000; /* loop detection, max compr ptrs */
+ unsigned maxcompr = 1000; /* loop detection, max compr ptrs */
int in_buf = 1;
+ if(comprloop) {
+ if(*comprloop != 0)
+ maxcompr = 30; /* for like ipv6 reverse name, per label */
+ if(*comprloop > 4)
+ maxcompr = 4; /* just don't want to spend time, any more */
+ }
if(*dlen == 0) return sldns_str_print(s, slen, "ErrorMissingDname");
if(*pos == 0) {
(*d)++;
if(!pkt || target >= pktlen)
return w + sldns_str_print(s, slen,
"ErrorComprPtrOutOfBounds");
- if(counter++ > maxcompr)
+ if(counter++ > maxcompr) {
+ if(comprloop && *comprloop < 10)
+ (*comprloop)++;
return w + sldns_str_print(s, slen,
"ErrorComprPtrLooped");
+ }
in_buf = 0;
pos = pkt+target;
continue;
}
int sldns_wire2str_rdf_scan(uint8_t** d, size_t* dlen, char** s, size_t* slen,
- int rdftype, uint8_t* pkt, size_t pktlen)
+ int rdftype, uint8_t* pkt, size_t pktlen, int* comprloop)
{
if(*dlen == 0) return 0;
switch(rdftype) {
case LDNS_RDF_TYPE_NONE:
return 0;
case LDNS_RDF_TYPE_DNAME:
- return sldns_wire2str_dname_scan(d, dlen, s, slen, pkt, pktlen);
+ return sldns_wire2str_dname_scan(d, dlen, s, slen, pkt, pktlen, comprloop);
case LDNS_RDF_TYPE_INT8:
return sldns_wire2str_int8_scan(d, dlen, s, slen);
case LDNS_RDF_TYPE_INT16:
return sldns_wire2str_atma_scan(d, dlen, s, slen);
case LDNS_RDF_TYPE_IPSECKEY:
return sldns_wire2str_ipseckey_scan(d, dlen, s, slen, pkt,
- pktlen);
+ pktlen, comprloop);
case LDNS_RDF_TYPE_HIP:
return sldns_wire2str_hip_scan(d, dlen, s, slen);
case LDNS_RDF_TYPE_INT16_DATA:
/* internal scan routine that can modify arguments on failure */
static int sldns_wire2str_ipseckey_scan_internal(uint8_t** d, size_t* dl,
- char** s, size_t* sl, uint8_t* pkt, size_t pktlen)
+ char** s, size_t* sl, uint8_t* pkt, size_t pktlen, int* comprloop)
{
/* http://www.ietf.org/internet-drafts/draft-ietf-ipseckey-rr-12.txt*/
uint8_t precedence, gateway_type, algorithm;
w += sldns_wire2str_aaaa_scan(d, dl, s, sl);
break;
case 3: /* dname */
- w += sldns_wire2str_dname_scan(d, dl, s, sl, pkt, pktlen);
+ w += sldns_wire2str_dname_scan(d, dl, s, sl, pkt, pktlen, comprloop);
break;
default: /* unknown */
return -1;
}
int sldns_wire2str_ipseckey_scan(uint8_t** d, size_t* dl, char** s, size_t* sl,
- uint8_t* pkt, size_t pktlen)
+ uint8_t* pkt, size_t pktlen, int* comprloop)
{
uint8_t* od = *d;
char* os = *s;
size_t odl = *dl, osl = *sl;
- int w=sldns_wire2str_ipseckey_scan_internal(d, dl, s, sl, pkt, pktlen);
+ int w=sldns_wire2str_ipseckey_scan_internal(d, dl, s, sl, pkt, pktlen, comprloop);
if(w == -1) {
*d = od;
*s = os;
* @param str_len: length of string buffer.
* @param pkt: packet for decompression, if NULL no decompression.
* @param pktlen: length of packet buffer.
+ * @param comprloop: if pkt, bool detects compression loops.
* @return number of characters (except null) needed to print.
*/
int sldns_wire2str_rr_scan(uint8_t** data, size_t* data_len, char** str,
- size_t* str_len, uint8_t* pkt, size_t pktlen);
+ size_t* str_len, uint8_t* pkt, size_t pktlen, int* comprloop);
/**
* Scan wireformat question rr to string, with user buffers.
* @param str_len: length of string buffer.
* @param pkt: packet for decompression, if NULL no decompression.
* @param pktlen: length of packet buffer.
+ * @param comprloop: if pkt, bool detects compression loops.
* @return number of characters (except null) needed to print.
*/
int sldns_wire2str_rrquestion_scan(uint8_t** data, size_t* data_len, char** str,
- size_t* str_len, uint8_t* pkt, size_t pktlen);
+ size_t* str_len, uint8_t* pkt, size_t pktlen, int* comprloop);
/**
* Scan wireformat RR to string in unknown RR format, with user buffers.
* @param str_len: length of string buffer.
* @param pkt: packet for decompression, if NULL no decompression.
* @param pktlen: length of packet buffer.
+ * @param comprloop: if pkt, bool detects compression loops.
* @return number of characters (except null) needed to print.
*/
int sldns_wire2str_rr_unknown_scan(uint8_t** data, size_t* data_len, char** str,
- size_t* str_len, uint8_t* pkt, size_t pktlen);
+ size_t* str_len, uint8_t* pkt, size_t pktlen, int* comprloop);
/**
* Print to string the RR-information comment in default format,
* @param rrtype: RR type of Rdata, host format.
* @param pkt: packet for decompression, if NULL no decompression.
* @param pktlen: length of packet buffer.
+ * @param comprloop: if pkt, bool detects compression loops.
* @return number of characters (except null) needed to print.
*/
int sldns_wire2str_rdata_scan(uint8_t** data, size_t* data_len, char** str,
- size_t* str_len, uint16_t rrtype, uint8_t* pkt, size_t pktlen);
+ size_t* str_len, uint16_t rrtype, uint8_t* pkt, size_t pktlen,
+ int* comprloop);
/**
* Scan wireformat rdata to string in unknown format, with user buffers.
* @param str_len: length of string buffer.
* @param pkt: packet for decompression, if NULL no decompression.
* @param pktlen: length of packet buffer.
+ * @param comprloop: inout bool, that is set true if compression loop failure
+ * happens. Pass in 0, if passsed in as true, a lower bound is set
+ * on compression loops to stop arbitrary long packet parse times.
+ * This is meant so you can set it to 0 at the start of a list of dnames,
+ * and then scan all of them in sequence, if a loop happens, it becomes
+ * true and then it becomes more strict for the next dnames in the list.
+ * You can leave it at NULL if there is no pkt (pkt is NULL too).
* @return number of characters (except null) needed to print.
*/
int sldns_wire2str_dname_scan(uint8_t** data, size_t* data_len, char** str,
- size_t* str_len, uint8_t* pkt, size_t pktlen);
+ size_t* str_len, uint8_t* pkt, size_t pktlen, int* comprloop);
/**
* Scan wireformat rr type to string, with user buffers.
* @param rdftype: the type of the rdata field, enum sldns_rdf_type.
* @param pkt: packet for decompression, if NULL no decompression.
* @param pktlen: length of packet buffer.
+ * @param comprloop: if pkt, bool detects compression loops.
* @return number of characters (except null) needed to print.
* Can return -1 on failure.
*/
int sldns_wire2str_rdf_scan(uint8_t** data, size_t* data_len, char** str,
- size_t* str_len, int rdftype, uint8_t* pkt, size_t pktlen);
+ size_t* str_len, int rdftype, uint8_t* pkt, size_t pktlen,
+ int* comprloop);
/**
* Scan wireformat int8 field to string, with user buffers.
* @param str_len: length of string buffer.
* @param pkt: packet for decompression, if NULL no decompression.
* @param pktlen: length of packet buffer.
+ * @param comprloop: if pkt, bool detects compression loops.
* @return number of characters (except null) needed to print.
* Can return -1 on failure.
*/
int sldns_wire2str_ipseckey_scan(uint8_t** data, size_t* data_len, char** str,
- size_t* str_len, uint8_t* pkt, size_t pktlen);
+ size_t* str_len, uint8_t* pkt, size_t pktlen, int* comprloop);
/**
* Scan wireformat HIP (algo, HIT, pubkey) field to string, with user buffers.
uint8_t* d;
size_t dl, sl=0;
char* snull = NULL;
+ int comprloop = 0;
if(pktlen < LDNS_HEADER_SIZE)
return 0;
if(LDNS_QDCOUNT(pkt) == 0)
/* skip over dname with dname-scan routine */
d = pkt+LDNS_HEADER_SIZE;
dl = pktlen-LDNS_HEADER_SIZE;
- (void)sldns_wire2str_dname_scan(&d, &dl, &snull, &sl, pkt, pktlen);
+ (void)sldns_wire2str_dname_scan(&d, &dl, &snull, &sl, pkt, pktlen, &comprloop);
if(dl < 2)
return 0;
return sldns_read_uint16(d);
uint8_t* d;
size_t dl, sl=0;
char* snull = NULL;
+ int comprloop = 0;
if(pktlen < LDNS_HEADER_SIZE)
return 0;
if(LDNS_QDCOUNT(pkt) == 0)
/* skip over dname with dname-scan routine */
d = pkt+LDNS_HEADER_SIZE;
dl = pktlen-LDNS_HEADER_SIZE;
- (void)sldns_wire2str_dname_scan(&d, &dl, &snull, &sl, pkt, pktlen);
+ (void)sldns_wire2str_dname_scan(&d, &dl, &snull, &sl, pkt, pktlen, &comprloop);
return pktlen-dl-LDNS_HEADER_SIZE;
}
size_t walk_len = plen, sl=0;
char* snull = NULL;
uint16_t i;
+ int comprloop = 0;
if(walk_len < LDNS_HEADER_SIZE)
return 0;
/* skip other records with wire2str_scan */
for(i=0; i < LDNS_QDCOUNT(p); i++)
(void)sldns_wire2str_rrquestion_scan(&walk, &walk_len,
- &snull, &sl, p, plen);
+ &snull, &sl, p, plen, &comprloop);
for(i=0; i < LDNS_ANCOUNT(p); i++)
(void)sldns_wire2str_rr_scan(&walk, &walk_len, &snull, &sl,
- p, plen);
+ p, plen, &comprloop);
/* walk through authority section */
for(i=0; i < LDNS_NSCOUNT(p); i++) {
uint8_t* dstart = walk;
size_t dlen = walk_len;
(void)sldns_wire2str_dname_scan(&dstart, &dlen, &snull, &sl,
- p, plen);
+ p, plen, &comprloop);
if(dlen >= 2 && sldns_read_uint16(dstart) == LDNS_RR_TYPE_SOA) {
/* skip type, class, TTL, rdatalen */
if(dlen < 10)
dlen -= 10;
/* check third rdf */
(void)sldns_wire2str_dname_scan(&dstart, &dlen, &snull,
- &sl, p, plen);
+ &sl, p, plen, &comprloop);
(void)sldns_wire2str_dname_scan(&dstart, &dlen, &snull,
- &sl, p, plen);
+ &sl, p, plen, &comprloop);
if(dlen < 4)
return 0;
verbose(3, "found serial %u in msg. ",
}
/* move to next RR */
(void)sldns_wire2str_rr_scan(&walk, &walk_len, &snull, &sl,
- p, plen);
+ p, plen, &comprloop);
}
return 0;
}
size_t wlen = *plen, sl=0;
char* snull = NULL;
uint16_t i;
+ int comprloop = 0;
if(wlen < LDNS_HEADER_SIZE)
return 0;
/* skip other records with wire2str_scan */
for(i=0; i < LDNS_QDCOUNT(*p); i++)
(void)sldns_wire2str_rrquestion_scan(&w, &wlen, &snull, &sl,
- *p, *plen);
+ *p, *plen, &comprloop);
for(i=0; i < LDNS_ANCOUNT(*p); i++)
- (void)sldns_wire2str_rr_scan(&w, &wlen, &snull, &sl, *p, *plen);
+ (void)sldns_wire2str_rr_scan(&w, &wlen, &snull, &sl, *p, *plen, &comprloop);
for(i=0; i < LDNS_NSCOUNT(*p); i++)
- (void)sldns_wire2str_rr_scan(&w, &wlen, &snull, &sl, *p, *plen);
+ (void)sldns_wire2str_rr_scan(&w, &wlen, &snull, &sl, *p, *plen, &comprloop);
/* walk through additional section */
for(i=0; i < LDNS_ARCOUNT(*p); i++) {
uint8_t* dstart = w;
size_t dlen = wlen;
(void)sldns_wire2str_dname_scan(&dstart, &dlen, &snull, &sl,
- *p, *plen);
+ *p, *plen, &comprloop);
if(dlen >= 2 && sldns_read_uint16(dstart) == LDNS_RR_TYPE_OPT) {
*p = dstart+2;
*plen = dlen-2;
return 1;
}
/* move to next RR */
- (void)sldns_wire2str_rr_scan(&w, &wlen, &snull, &sl, *p, *plen);
+ (void)sldns_wire2str_rr_scan(&w, &wlen, &snull, &sl, *p, *plen, &comprloop);
}
return 0;
}
char* snull = NULL;
uint16_t i;
uint16_t num = LDNS_ANCOUNT(pkt)+LDNS_NSCOUNT(pkt)+LDNS_ARCOUNT(pkt);
+ int comprloop = 0;
if(walk_len < LDNS_HEADER_SIZE)
return;
walk += LDNS_HEADER_SIZE;
walk_len -= LDNS_HEADER_SIZE;
for(i=0; i < LDNS_QDCOUNT(pkt); i++)
(void)sldns_wire2str_rrquestion_scan(&walk, &walk_len,
- &snull, &sl, pkt, pktlen);
+ &snull, &sl, pkt, pktlen, &comprloop);
for(i=0; i < num; i++) {
/* wipe TTL */
uint8_t* dstart = walk;
size_t dlen = walk_len;
(void)sldns_wire2str_dname_scan(&dstart, &dlen, &snull, &sl,
- pkt, pktlen);
+ pkt, pktlen, &comprloop);
if(dlen < 8)
return;
sldns_write_uint32(dstart+4, 0);
/* go to next RR */
(void)sldns_wire2str_rr_scan(&walk, &walk_len, &snull, &sl,
- pkt, pktlen);
+ pkt, pktlen, &comprloop);
}
}
char qs[512], ps[512];
size_t qslen = sizeof(qs), pslen = sizeof(ps);
char* qss = qs, *pss = ps;
+ int comprloop = 0;
if(!qn || !pn)
return 0;
- (void)sldns_wire2str_dname_scan(&qn, &qlen, &qss, &qslen, q, qlen);
- (void)sldns_wire2str_dname_scan(&pn, &plen, &pss, &pslen, p, plen);
+ (void)sldns_wire2str_dname_scan(&qn, &qlen, &qss, &qslen, q, qlen, &comprloop);
+ (void)sldns_wire2str_dname_scan(&pn, &plen, &pss, &pslen, p, plen, &comprloop);
return (strcmp(qs, ps) == 0);
}
char qs[5120], ps[5120];
size_t qslen = sizeof(qs), pslen = sizeof(ps);
char* qss = qs, *pss = ps;
+ int comprloop = 0;
if(!qn || !pn)
return 0;
/* decompresses domain names */
- (void)sldns_wire2str_dname_scan(&qn, &qlen, &qss, &qslen, q, qlen);
- (void)sldns_wire2str_dname_scan(&pn, &plen, &pss, &pslen, p, plen);
+ (void)sldns_wire2str_dname_scan(&qn, &qlen, &qss, &qslen, q, qlen, &comprloop);
+ (void)sldns_wire2str_dname_scan(&pn, &plen, &pss, &pslen, p, plen, &comprloop);
/* same: false, (strict subdomain check)??? */
if(strcmp(qs, ps) == 0)
return 1;