int parse_packet(sldns_buffer* pkt, struct msg_parse* msg, struct regional* region) { int ret; if(sldns_buffer_remaining(pkt) < LDNS_HEADER_SIZE) return LDNS_RCODE_FORMERR; /* read the header */ sldns_buffer_read(pkt, &msg->id, sizeof(uint16_t)); msg->flags = sldns_buffer_read_u16(pkt); msg->qdcount = sldns_buffer_read_u16(pkt); msg->ancount = sldns_buffer_read_u16(pkt); msg->nscount = sldns_buffer_read_u16(pkt); msg->arcount = sldns_buffer_read_u16(pkt); if(msg->qdcount > 1) return LDNS_RCODE_FORMERR; if((ret = parse_query_section(pkt, msg)) != 0) return ret; if((ret = parse_section(pkt, msg, region, LDNS_SECTION_ANSWER, msg->ancount, &msg->an_rrsets)) != 0) return ret; if((ret = parse_section(pkt, msg, region, LDNS_SECTION_AUTHORITY, msg->nscount, &msg->ns_rrsets)) != 0) return ret; if(sldns_buffer_remaining(pkt) == 0 && msg->arcount == 1) { /* BIND accepts leniently that an EDNS record is missing. * so, we do too. */ } else if((ret = parse_section(pkt, msg, region, LDNS_SECTION_ADDITIONAL, msg->arcount, &msg->ar_rrsets)) != 0) return ret; /* if(sldns_buffer_remaining(pkt) > 0) { */ /* there is spurious data at end of packet. ignore */ /* } */ msg->rrset_count = msg->an_rrsets + msg->ns_rrsets + msg->ar_rrsets; return 0; }
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; }
/** return type networkformat that rrsig in packet covers */ static int pkt_rrsig_covered(sldns_buffer* pkt, uint8_t* here, uint16_t* type) { size_t pos = sldns_buffer_position(pkt); sldns_buffer_set_position(pkt, (size_t)(here-sldns_buffer_begin(pkt))); /* ttl + len + size of small rrsig(rootlabel, no signature) */ if(sldns_buffer_remaining(pkt) < 4+2+19) return 0; sldns_buffer_skip(pkt, 4); /* ttl */ if(sldns_buffer_read_u16(pkt) < 19) /* too short */ { sldns_buffer_set_position(pkt, pos); return 0; } *type = sldns_buffer_read_u16(pkt); sldns_buffer_set_position(pkt, pos); return 1; }
/** calculate the size of one rr */ static int calc_size(sldns_buffer* pkt, uint16_t type, struct rr_parse* rr) { const sldns_rr_descriptor* desc; uint16_t pkt_len; /* length of rr inside the packet */ rr->size = sizeof(uint16_t); /* the rdatalen */ sldns_buffer_skip(pkt, 4); /* skip ttl */ pkt_len = sldns_buffer_read_u16(pkt); if(sldns_buffer_remaining(pkt) < pkt_len) return 0; desc = sldns_rr_descript(type); if(pkt_len > 0 && desc && desc->_dname_count > 0) { int count = (int)desc->_dname_count; int rdf = 0; size_t len; size_t oldpos; /* skip first part. */ while(pkt_len > 0 && count) { switch(desc->_wireformat[rdf]) { case LDNS_RDF_TYPE_DNAME: /* decompress every domain name */ oldpos = sldns_buffer_position(pkt); if((len = pkt_dname_len(pkt)) == 0) return 0; /* malformed dname */ if(sldns_buffer_position(pkt)-oldpos > pkt_len) return 0; /* dname exceeds rdata */ pkt_len -= sldns_buffer_position(pkt)-oldpos; rr->size += len; count--; len = 0; break; case LDNS_RDF_TYPE_STR: if(pkt_len < 1) { /* NOTREACHED, due to 'while(>0)' */ return 0; /* len byte exceeds rdata */ } len = sldns_buffer_current(pkt)[0] + 1; break; default: len = get_rdf_size(desc->_wireformat[rdf]); } if(len) { if(pkt_len < len) return 0; /* exceeds rdata */ pkt_len -= len; sldns_buffer_skip(pkt, (ssize_t)len); rr->size += len; } rdf++; } } /* remaining rdata */ rr->size += pkt_len; sldns_buffer_skip(pkt, (ssize_t)pkt_len); return 1; }
/** * Parse query section. * @param pkt: packet, position at call must be at start of query section. * at end position is after query section. * @param msg: store results here. * @return: 0 if OK, or rcode on error. */ static int parse_query_section(sldns_buffer* pkt, struct msg_parse* msg) { if(msg->qdcount == 0) return 0; if(msg->qdcount > 1) return LDNS_RCODE_FORMERR; log_assert(msg->qdcount == 1); if(sldns_buffer_remaining(pkt) <= 0) return LDNS_RCODE_FORMERR; msg->qname = sldns_buffer_current(pkt); if((msg->qname_len = pkt_dname_len(pkt)) == 0) return LDNS_RCODE_FORMERR; if(sldns_buffer_remaining(pkt) < sizeof(uint16_t)*2) return LDNS_RCODE_FORMERR; msg->qtype = sldns_buffer_read_u16(pkt); msg->qclass = sldns_buffer_read_u16(pkt); return 0; }
int query_info_parse(struct query_info* m, sldns_buffer* query) { uint8_t* q = sldns_buffer_begin(query); /* minimum size: header + \0 + qtype + qclass */ if(sldns_buffer_limit(query) < LDNS_HEADER_SIZE + 5) return 0; if(LDNS_OPCODE_WIRE(q) != LDNS_PACKET_QUERY || LDNS_QDCOUNT(q) != 1 || sldns_buffer_position(query) != 0) return 0; sldns_buffer_skip(query, LDNS_HEADER_SIZE); m->qname = sldns_buffer_current(query); if((m->qname_len = query_dname_len(query)) == 0) return 0; /* parse error */ if(sldns_buffer_remaining(query) < 4) return 0; /* need qtype, qclass */ m->qtype = sldns_buffer_read_u16(query); m->qclass = sldns_buffer_read_u16(query); return 1; }
/** analyse pkt */ static void analyze(sldns_buffer* pkt) { uint16_t i, f, qd, an, ns, ar; int rrnum = 0; printf("packet length %d\n", (int)sldns_buffer_limit(pkt)); if(sldns_buffer_limit(pkt) < 12) return; i = sldns_buffer_read_u16(pkt); printf("id (hostorder): %d (0x%x)\n", (int)i, (unsigned)i); f = sldns_buffer_read_u16(pkt); printf("flags: 0x%x\n", (unsigned)f); qd = sldns_buffer_read_u16(pkt); printf("qdcount: %d\n", (int)qd); an = sldns_buffer_read_u16(pkt); printf("ancount: %d\n", (int)an); ns = sldns_buffer_read_u16(pkt); printf("nscount: %d\n", (int)ns); ar = sldns_buffer_read_u16(pkt); printf("arcount: %d\n", (int)ar); printf(";-- query section\n"); while(sldns_buffer_remaining(pkt) > 0) { if(rrnum == (int)qd) printf(";-- answer section\n"); if(rrnum == (int)qd+(int)an) printf(";-- authority section\n"); if(rrnum == (int)qd+(int)an+(int)ns) printf(";-- additional section\n"); printf("rr %d ", rrnum); analyze_rr(pkt, rrnum < (int)qd); rrnum++; } }
/** skip rr ttl and rdata */ static int skip_ttl_rdata(sldns_buffer* pkt) { uint16_t rdatalen; if(sldns_buffer_remaining(pkt) < 6) /* ttl + rdatalen */ return 0; sldns_buffer_skip(pkt, 4); /* ttl */ rdatalen = sldns_buffer_read_u16(pkt); if(sldns_buffer_remaining(pkt) < rdatalen) return 0; sldns_buffer_skip(pkt, (ssize_t)rdatalen); return 1; }
/** analyze rr in packet */ static void analyze_rr(sldns_buffer* pkt, int q) { uint16_t type, dclass, len; uint32_t ttl; analyze_dname(pkt); type = sldns_buffer_read_u16(pkt); dclass = sldns_buffer_read_u16(pkt); printf("type %s(%d)", sldns_rr_descript(type)? sldns_rr_descript(type)->_name: "??" , (int)type); printf(" class %s(%d) ", sldns_lookup_by_id(sldns_rr_classes, (int)dclass)?sldns_lookup_by_id(sldns_rr_classes, (int)dclass)->name:"??", (int)dclass); if(q) { printf("\n"); } else { ttl = sldns_buffer_read_u32(pkt); printf(" ttl %d (0x%x)", (int)ttl, (unsigned)ttl); len = sldns_buffer_read_u16(pkt); printf(" rdata len %d:\n", (int)len); if(sldns_rr_descript(type)) analyze_rdata(pkt, sldns_rr_descript(type), len); else sldns_buffer_skip(pkt, (ssize_t)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; }
/** see if RRSIG is a duplicate of another */ static int sig_is_double(sldns_buffer* pkt, struct rrset_parse* rrset, uint8_t* ttldata) { uint16_t rlen, siglen; size_t pos = sldns_buffer_position(pkt); struct rr_parse* sig; if(sldns_buffer_remaining(pkt) < 6) return 0; sldns_buffer_skip(pkt, 4); /* ttl */ rlen = sldns_buffer_read_u16(pkt); if(sldns_buffer_remaining(pkt) < rlen) { sldns_buffer_set_position(pkt, pos); return 0; } sldns_buffer_set_position(pkt, pos); sig = rrset->rrsig_first; while(sig) { /* check if rdatalen is same */ memmove(&siglen, sig->ttl_data+4, sizeof(siglen)); siglen = ntohs(siglen); /* checks if data in packet is exactly the same, this means * also dname in rdata is the same, but rrsig is not allowed * to have compressed dnames anyway. If it is compressed anyway * it will lead to duplicate rrs for qtype=RRSIG. (or ANY). * * Cannot use sig->size because size of the other one is not * calculated yet. */ if(siglen == rlen) { if(siglen>0 && memcmp(sig->ttl_data+6, ttldata+6, siglen) == 0) { /* same! */ return 1; } } sig = sig->next; } return 0; }
/** * Parse packet RR section, for answer, authority and additional sections. * @param pkt: packet, position at call must be at start of section. * at end position is after section. * @param msg: store results here. * @param region: how to alloc results. * @param section: section enum. * @param num_rrs: how many rrs are in the section. * @param num_rrsets: returns number of rrsets in the section. * @return: 0 if OK, or rcode on error. */ static int parse_section(sldns_buffer* pkt, struct msg_parse* msg, struct regional* region, sldns_pkt_section section, uint16_t num_rrs, size_t* num_rrsets) { uint16_t i; uint8_t* dname, *prev_dname_f = NULL, *prev_dname_l = NULL; size_t dnamelen, prev_dnamelen = 0; uint16_t type, prev_type = 0; uint16_t dclass, prev_dclass = 0; uint32_t rrset_flags = 0; hashvalue_t hash = 0; struct rrset_parse* rrset = NULL; int r; if(num_rrs == 0) return 0; if(sldns_buffer_remaining(pkt) <= 0) return LDNS_RCODE_FORMERR; for(i=0; i<num_rrs; i++) { /* parse this RR. */ dname = sldns_buffer_current(pkt); if((dnamelen = pkt_dname_len(pkt)) == 0) return LDNS_RCODE_FORMERR; if(sldns_buffer_remaining(pkt) < 10) /* type, class, ttl, len */ return LDNS_RCODE_FORMERR; type = sldns_buffer_read_u16(pkt); sldns_buffer_read(pkt, &dclass, sizeof(dclass)); if(0) { /* debug show what is being parsed. */ if(type == LDNS_RR_TYPE_RRSIG) { uint16_t t; if(pkt_rrsig_covered(pkt, sldns_buffer_current(pkt), &t)) fprintf(stderr, "parse of %s(%d) [%s(%d)]", sldns_rr_descript(type)? sldns_rr_descript(type)->_name: "??", (int)type, sldns_rr_descript(t)? sldns_rr_descript(t)->_name: "??", (int)t); } else fprintf(stderr, "parse of %s(%d)", sldns_rr_descript(type)? sldns_rr_descript(type)->_name: "??", (int)type); fprintf(stderr, " %s(%d) ", sldns_lookup_by_id(sldns_rr_classes, (int)ntohs(dclass))?sldns_lookup_by_id( sldns_rr_classes, (int)ntohs(dclass))->name: "??", (int)ntohs(dclass)); dname_print(stderr, pkt, dname); fprintf(stderr, "\n"); } /* see if it is part of an existing RR set */ if(!find_rrset(msg, pkt, dname, dnamelen, type, dclass, &hash, &rrset_flags, &prev_dname_f, &prev_dname_l, &prev_dnamelen, &prev_type, &prev_dclass, &rrset, section, region)) return LDNS_RCODE_SERVFAIL; if(!rrset) { /* it is a new RR set. hash&flags already calculated.*/ (*num_rrsets)++; rrset = new_rrset(msg, dname, dnamelen, type, dclass, hash, rrset_flags, section, region); if(!rrset) return LDNS_RCODE_SERVFAIL; } else if(0) { fprintf(stderr, "is part of existing: "); dname_print(stderr, pkt, rrset->dname); fprintf(stderr, " type %s(%d)\n", sldns_rr_descript(rrset->type)? sldns_rr_descript(rrset->type)->_name: "??", (int)rrset->type); } /* add to rrset. */ if((r=add_rr_to_rrset(rrset, pkt, msg, region, section, type)) != 0) return r; } return 0; }
/** do the rdata copy */ static int rdata_copy(sldns_buffer* pkt, struct packed_rrset_data* data, uint8_t* to, struct rr_parse* rr, time_t* rr_ttl, uint16_t type, sldns_pkt_section section) { uint16_t pkt_len; const sldns_rr_descriptor* desc; *rr_ttl = sldns_read_uint32(rr->ttl_data); /* RFC 2181 Section 8. if msb of ttl is set treat as if zero. */ if(*rr_ttl & 0x80000000U) *rr_ttl = 0; if(type == LDNS_RR_TYPE_SOA && section == LDNS_SECTION_AUTHORITY) { /* negative response. see if TTL of SOA record larger than the * minimum-ttl in the rdata of the SOA record */ if(*rr_ttl > soa_find_minttl(rr)) *rr_ttl = soa_find_minttl(rr); if(*rr_ttl > MAX_NEG_TTL) *rr_ttl = MAX_NEG_TTL; } if(*rr_ttl < MIN_TTL) *rr_ttl = MIN_TTL; if(*rr_ttl < data->ttl) data->ttl = *rr_ttl; if(rr->outside_packet) { /* uncompressed already, only needs copy */ memmove(to, rr->ttl_data+sizeof(uint32_t), rr->size); return 1; } sldns_buffer_set_position(pkt, (size_t) (rr->ttl_data - sldns_buffer_begin(pkt) + sizeof(uint32_t))); /* insert decompressed size into rdata len stored in memory */ /* -2 because rdatalen bytes are not included. */ pkt_len = htons(rr->size - 2); memmove(to, &pkt_len, sizeof(uint16_t)); to += 2; /* read packet rdata len */ pkt_len = sldns_buffer_read_u16(pkt); if(sldns_buffer_remaining(pkt) < pkt_len) return 0; desc = sldns_rr_descript(type); if(pkt_len > 0 && desc && desc->_dname_count > 0) { int count = (int)desc->_dname_count; int rdf = 0; size_t len; size_t oldpos; /* decompress dnames. */ while(pkt_len > 0 && count) { switch(desc->_wireformat[rdf]) { case LDNS_RDF_TYPE_DNAME: oldpos = sldns_buffer_position(pkt); dname_pkt_copy(pkt, to, sldns_buffer_current(pkt)); to += pkt_dname_len(pkt); pkt_len -= sldns_buffer_position(pkt)-oldpos; count--; len = 0; break; case LDNS_RDF_TYPE_STR: len = sldns_buffer_current(pkt)[0] + 1; break; default: len = get_rdf_size(desc->_wireformat[rdf]); break; } if(len) { memmove(to, sldns_buffer_current(pkt), len); to += len; sldns_buffer_skip(pkt, (ssize_t)len); log_assert(len <= pkt_len); pkt_len -= len; } rdf++; } } /* copy remaining rdata */ if(pkt_len > 0) memmove(to, sldns_buffer_current(pkt), pkt_len); return 1; }