int parse_edns_from_pkt(sldns_buffer* pkt, struct edns_data* edns) { log_assert(LDNS_QDCOUNT(sldns_buffer_begin(pkt)) == 1); log_assert(LDNS_ANCOUNT(sldns_buffer_begin(pkt)) == 0); log_assert(LDNS_NSCOUNT(sldns_buffer_begin(pkt)) == 0); /* check edns section is present */ if(LDNS_ARCOUNT(sldns_buffer_begin(pkt)) > 1) { return LDNS_RCODE_FORMERR; } if(LDNS_ARCOUNT(sldns_buffer_begin(pkt)) == 0) { memset(edns, 0, sizeof(*edns)); edns->udp_size = 512; return 0; } /* domain name must be the root of length 1. */ if(pkt_dname_len(pkt) != 1) return LDNS_RCODE_FORMERR; if(sldns_buffer_remaining(pkt) < 10) /* type, class, ttl, rdatalen */ return LDNS_RCODE_FORMERR; if(sldns_buffer_read_u16(pkt) != LDNS_RR_TYPE_OPT) return LDNS_RCODE_FORMERR; edns->edns_present = 1; edns->udp_size = sldns_buffer_read_u16(pkt); /* class is udp size */ edns->ext_rcode = sldns_buffer_read_u8(pkt); /* ttl used for bits */ edns->edns_version = sldns_buffer_read_u8(pkt); edns->bits = sldns_buffer_read_u16(pkt); /* ignore rdata and rrsigs */ return 0; }
size_t pkt_dname_len(sldns_buffer* pkt) { size_t len = 0; int ptrcount = 0; uint8_t labellen; size_t endpos = 0; /* read dname and determine length */ /* check compression pointers, loops, out of bounds */ while(1) { /* read next label */ if(sldns_buffer_remaining(pkt) < 1) return 0; labellen = sldns_buffer_read_u8(pkt); if(LABEL_IS_PTR(labellen)) { /* compression ptr */ uint16_t ptr; if(sldns_buffer_remaining(pkt) < 1) return 0; ptr = PTR_OFFSET(labellen, sldns_buffer_read_u8(pkt)); if(ptrcount++ > MAX_COMPRESS_PTRS) return 0; /* loop! */ if(sldns_buffer_limit(pkt) <= ptr) return 0; /* out of bounds! */ if(!endpos) endpos = sldns_buffer_position(pkt); sldns_buffer_set_position(pkt, ptr); } else { /* label contents */ if(labellen > 0x3f) return 0; /* label too long */ len += 1 + labellen; if(len > LDNS_MAX_DOMAINLEN) return 0; if(labellen == 0) { /* end of dname */ break; } if(sldns_buffer_remaining(pkt) < labellen) return 0; sldns_buffer_skip(pkt, (ssize_t)labellen); } } if(endpos) sldns_buffer_set_position(pkt, endpos); return len; }
/** See if next rrset is nsec at zone apex */ static int nsec_at_apex(sldns_buffer* pkt) { /* we are at ttl position in packet. */ size_t pos = sldns_buffer_position(pkt); uint16_t rdatalen; if(sldns_buffer_remaining(pkt) < 7) /* ttl+len+root */ return 0; /* eek! */ sldns_buffer_skip(pkt, 4); /* ttl */; rdatalen = sldns_buffer_read_u16(pkt); if(sldns_buffer_remaining(pkt) < rdatalen) { sldns_buffer_set_position(pkt, pos); return 0; /* parse error happens later */ } /* must validate the nsec next domain name format */ if(pkt_dname_len(pkt) == 0) { sldns_buffer_set_position(pkt, pos); return 0; /* parse error */ } /* see if SOA bit is set. */ if(sldns_buffer_position(pkt) < pos+4+rdatalen) { /* nsec type bitmap contains items */ uint8_t win, blen, bits; /* need: windownum, bitmap len, firstbyte */ if(sldns_buffer_position(pkt)+3 > pos+4+rdatalen) { sldns_buffer_set_position(pkt, pos); return 0; /* malformed nsec */ } win = sldns_buffer_read_u8(pkt); blen = sldns_buffer_read_u8(pkt); bits = sldns_buffer_read_u8(pkt); /* 0window always first window. bitlen >=1 or parse error really. bit 0x2 is SOA. */ if(win == 0 && blen >= 1 && (bits & 0x02)) { sldns_buffer_set_position(pkt, pos); return 1; } } sldns_buffer_set_position(pkt, pos); return 0; }
/* determine length of a dname in buffer, no compression pointers allowed */ size_t query_dname_len(sldns_buffer* query) { size_t len = 0; size_t labellen; while(1) { if(sldns_buffer_remaining(query) < 1) return 0; /* parse error, need label len */ labellen = sldns_buffer_read_u8(query); if(labellen&0xc0) return 0; /* no compression allowed in queries */ len += labellen + 1; if(len > LDNS_MAX_DOMAINLEN) return 0; /* too long */ if(labellen == 0) return len; if(sldns_buffer_remaining(query) < labellen) return 0; /* parse error, need content */ sldns_buffer_skip(query, (ssize_t)labellen); } }