/** Add rr (from packet here) to rrset, skips rr */ static int add_rr_to_rrset(struct rrset_parse* rrset, sldns_buffer* pkt, struct msg_parse* msg, struct regional* region, sldns_pkt_section section, uint16_t type) { struct rr_parse* rr; /* check section of rrset. */ if(rrset->section != section && type != LDNS_RR_TYPE_RRSIG && rrset->type != LDNS_RR_TYPE_RRSIG) { /* silently drop it - we drop the last part, since * trust in rr data depends on the section it is in. * the less trustworthy part is discarded. * also the last part is more likely to be incomplete. * RFC 2181: must put RRset only once in response. */ /* verbose(VERB_QUERY, "Packet contains rrset data in " "multiple sections, dropped last part."); log_buf(VERB_QUERY, "packet was", pkt); */ /* forwards */ if(!skip_ttl_rdata(pkt)) return LDNS_RCODE_FORMERR; return 0; } if( (msg->qtype == LDNS_RR_TYPE_RRSIG || msg->qtype == LDNS_RR_TYPE_ANY) && sig_is_double(pkt, rrset, sldns_buffer_current(pkt))) { if(!skip_ttl_rdata(pkt)) return LDNS_RCODE_FORMERR; return 0; } /* create rr */ if(!(rr = (struct rr_parse*)regional_alloc(region, sizeof(*rr)))) return LDNS_RCODE_SERVFAIL; rr->outside_packet = 0; rr->ttl_data = sldns_buffer_current(pkt); rr->next = 0; if(type == LDNS_RR_TYPE_RRSIG && rrset->type != LDNS_RR_TYPE_RRSIG) { if(rrset->rrsig_last) rrset->rrsig_last->next = rr; else rrset->rrsig_first = rr; rrset->rrsig_last = rr; rrset->rrsig_count++; } else { if(rrset->rr_last) rrset->rr_last->next = rr; else rrset->rr_first = rr; rrset->rr_last = rr; rrset->rr_count++; } /* calc decompressed size */ if(!calc_size(pkt, type, rr)) return LDNS_RCODE_FORMERR; rrset->size += rr->size; return 0; }
/** * Inser canonical owner name into buffer. * @param buf: buffer to insert into at current position. * @param k: rrset with its owner name. * @param sig: signature with signer name and label count. * must be length checked, at least 18 bytes long. * @param can_owner: position in buffer returned for future use. * @param can_owner_len: length of canonical owner name. */ static void insert_can_owner(sldns_buffer* buf, struct ub_packed_rrset_key* k, uint8_t* sig, uint8_t** can_owner, size_t* can_owner_len) { int rrsig_labels = (int)sig[3]; int fqdn_labels = dname_signame_label_count(k->rk.dname); *can_owner = sldns_buffer_current(buf); if(rrsig_labels == fqdn_labels) { /* no change */ sldns_buffer_write(buf, k->rk.dname, k->rk.dname_len); query_dname_tolower(*can_owner); *can_owner_len = k->rk.dname_len; return; } log_assert(rrsig_labels < fqdn_labels); /* *. | fqdn(rightmost rrsig_labels) */ if(rrsig_labels < fqdn_labels) { int i; uint8_t* nm = k->rk.dname; size_t len = k->rk.dname_len; /* so skip fqdn_labels-rrsig_labels */ for(i=0; i<fqdn_labels-rrsig_labels; i++) { dname_remove_label(&nm, &len); } *can_owner_len = len+2; sldns_buffer_write(buf, (uint8_t*)"\001*", 2); sldns_buffer_write(buf, nm, len); query_dname_tolower(*can_owner); } }
/** 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; }
/** analyze domain name in packet, possibly compressed */ static void analyze_dname(sldns_buffer* pkt) { size_t oldpos = sldns_buffer_position(pkt); size_t len; printf("[pos %d] dname: ", (int)oldpos); dname_print(stdout, pkt, sldns_buffer_current(pkt)); len = pkt_dname_len(pkt); printf(" len=%d", (int)len); if(sldns_buffer_position(pkt)-oldpos != len) printf(" comprlen=%d\n", (int)(sldns_buffer_position(pkt)-oldpos)); else printf("\n"); }
/** analyze rdata in packet */ static void analyze_rdata(sldns_buffer*pkt, const sldns_rr_descriptor* desc, uint16_t rdlen) { int rdf = 0; int count = (int)desc->_dname_count; size_t len, oldpos; while(rdlen > 0 && count) { switch(desc->_wireformat[rdf]) { case LDNS_RDF_TYPE_DNAME: oldpos = sldns_buffer_position(pkt); analyze_dname(pkt); rdlen -= 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]); } if(len) { printf(" wf[%d]", (int)len); sldns_buffer_skip(pkt, (ssize_t)len); rdlen -= len; } rdf++; } if(rdlen) { size_t i; printf(" remain[%d]\n", (int)rdlen); for(i=0; i<rdlen; i++) printf(" %2.2X", (unsigned)sldns_buffer_current(pkt)[i]); printf("\n"); } else printf("\n"); sldns_buffer_skip(pkt, (ssize_t)rdlen); }
int sldns_buffer_printf(sldns_buffer *buffer, const char *format, ...) { va_list args; int written = 0; size_t remaining; if (sldns_buffer_status_ok(buffer)) { sldns_buffer_invariant(buffer); assert(buffer->_limit == buffer->_capacity); remaining = sldns_buffer_remaining(buffer); va_start(args, format); written = vsnprintf((char *) sldns_buffer_current(buffer), remaining, format, args); va_end(args); if (written == -1) { buffer->_status_err = 1; return -1; } else if ((size_t) written >= remaining) { if (!sldns_buffer_reserve(buffer, (size_t) written + 1)) { buffer->_status_err = 1; return -1; } va_start(args, format); written = vsnprintf((char *) sldns_buffer_current(buffer), sldns_buffer_remaining(buffer), format, args); va_end(args); if (written == -1) { buffer->_status_err = 1; return -1; } } buffer->_position += written; } return written; }
void error_encode(sldns_buffer* buf, int r, struct query_info* qinfo, uint16_t qid, uint16_t qflags, struct edns_data* edns) { uint16_t flags; sldns_buffer_clear(buf); sldns_buffer_write(buf, &qid, sizeof(uint16_t)); flags = (uint16_t)(BIT_QR | BIT_RA | r); /* QR and retcode*/ flags |= (qflags & (BIT_RD|BIT_CD)); /* copy RD and CD bit */ sldns_buffer_write_u16(buf, flags); if(qinfo) flags = 1; else flags = 0; sldns_buffer_write_u16(buf, flags); flags = 0; sldns_buffer_write(buf, &flags, sizeof(uint16_t)); sldns_buffer_write(buf, &flags, sizeof(uint16_t)); sldns_buffer_write(buf, &flags, sizeof(uint16_t)); if(qinfo) { const uint8_t* qname = qinfo->local_alias ? qinfo->local_alias->rrset->rk.dname : qinfo->qname; size_t qname_len = qinfo->local_alias ? qinfo->local_alias->rrset->rk.dname_len : qinfo->qname_len; if(sldns_buffer_current(buf) == qname) sldns_buffer_skip(buf, (ssize_t)qname_len); else sldns_buffer_write(buf, qname, qname_len); sldns_buffer_write_u16(buf, qinfo->qtype); sldns_buffer_write_u16(buf, qinfo->qclass); } sldns_buffer_flip(buf); if(edns) { struct edns_data es = *edns; es.edns_version = EDNS_ADVERTISED_VERSION; es.udp_size = EDNS_ADVERTISED_SIZE; es.ext_rcode = 0; es.bits &= EDNS_DO; if(sldns_buffer_limit(buf) + calc_edns_field_size(&es) > edns->udp_size) return; attach_edns_record(buf, &es); } }
/** * 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; }
/** store query section in wireformat buffer, return RETVAL */ static int insert_query(struct query_info* qinfo, struct compress_tree_node** tree, sldns_buffer* buffer, struct regional* region) { if(sldns_buffer_remaining(buffer) < qinfo->qname_len+sizeof(uint16_t)*2) return RETVAL_TRUNC; /* buffer too small */ /* the query is the first name inserted into the tree */ if(!compress_tree_store(qinfo->qname, dname_count_labels(qinfo->qname), sldns_buffer_position(buffer), region, NULL, tree)) return RETVAL_OUTMEM; if(sldns_buffer_current(buffer) == qinfo->qname) sldns_buffer_skip(buffer, (ssize_t)qinfo->qname_len); else sldns_buffer_write(buffer, qinfo->qname, qinfo->qname_len); sldns_buffer_write_u16(buffer, qinfo->qtype); sldns_buffer_write_u16(buffer, qinfo->qclass); return RETVAL_OK; }
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; }
/** * read contents of trusted-keys{ ... ; clauses and insert keys into storage. * @param anchors: where to store keys * @param buf: buffer to use * @param line: line number in file * @param in: file to read from. * @return 0 on error. */ static int process_bind_contents(struct val_anchors* anchors, sldns_buffer* buf, int* line, FILE* in) { /* loop over contents, collate strings before ; */ /* contents is (numbered): 0 1 2 3 4 5 6 7 8 */ /* name. 257 3 5 base64 base64 */ /* quoted value: 0 "111" 0 0 0 0 0 0 0 */ /* comments value: 1 "000" 1 1 1 "0 0 0 0" 1 */ int contnum = 0; int quoted = 0; int comments = 1; int rdlen; char* str = 0; sldns_buffer_clear(buf); while((rdlen=readkeyword_bindfile(in, buf, line, comments))) { if(rdlen == 1 && sldns_buffer_position(buf) == 1 && isspace((unsigned char)*sldns_buffer_begin(buf))) { /* starting whitespace is removed */ sldns_buffer_clear(buf); continue; } else if(rdlen == 1 && sldns_buffer_current(buf)[-1] == '"') { /* remove " from the string */ if(contnum == 0) { quoted = 1; comments = 0; } sldns_buffer_skip(buf, -1); if(contnum > 0 && quoted) { if(sldns_buffer_remaining(buf) < 8+1) { log_err("line %d, too long", *line); return 0; } sldns_buffer_write(buf, " DNSKEY ", 8); quoted = 0; comments = 1; } else if(contnum > 0) comments = !comments; continue; } else if(rdlen == 1 && sldns_buffer_current(buf)[-1] == ';') { if(contnum < 5) { sldns_buffer_write_u8(buf, 0); log_err("line %d, bad key", *line); return 0; } sldns_buffer_skip(buf, -1); sldns_buffer_write_u8(buf, 0); str = strdup((char*)sldns_buffer_begin(buf)); if(!str) { log_err("line %d, allocation failure", *line); return 0; } if(!anchor_store_str(anchors, buf, str)) { log_err("line %d, bad key", *line); free(str); return 0; } free(str); sldns_buffer_clear(buf); contnum = 0; quoted = 0; comments = 1; continue; } else if(rdlen == 1 && sldns_buffer_current(buf)[-1] == '}') { if(contnum > 0) { sldns_buffer_write_u8(buf, 0); log_err("line %d, bad key before }", *line); return 0; } return 1; } else if(rdlen == 1 && isspace((unsigned char)sldns_buffer_current(buf)[-1])) { /* leave whitespace here */ } else { /* not space or whatnot, so actual content */ contnum ++; if(contnum == 1 && !quoted) { if(sldns_buffer_remaining(buf) < 8+1) { log_err("line %d, too long", *line); return 0; } sldns_buffer_write(buf, " DNSKEY ", 8); } } } log_err("line %d, EOF before }", *line); 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; }
/** Find rrset. If equal to previous it is fast. hash if not so. * @param msg: the message with hash table. * @param pkt: the packet in wireformat (needed for compression ptrs). * @param dname: pointer to start of dname (compressed) in packet. * @param dnamelen: uncompressed wirefmt length of dname. * @param type: type of current rr. * @param dclass: class of current rr. * @param hash: hash value is returned if the rrset could not be found. * @param rrset_flags: is returned if the rrset could not be found. * @param prev_dname_first: dname of last seen RR. First seen dname. * @param prev_dname_last: dname of last seen RR. Last seen dname. * @param prev_dnamelen: dname len of last seen RR. * @param prev_type: type of last seen RR. * @param prev_dclass: class of last seen RR. * @param rrset_prev: last seen RRset. * @param section: the current section in the packet. * @param region: used to allocate temporary parsing data. * @return 0 on out of memory. */ static int find_rrset(struct msg_parse* msg, sldns_buffer* pkt, uint8_t* dname, size_t dnamelen, uint16_t type, uint16_t dclass, hashvalue_t* hash, uint32_t* rrset_flags, uint8_t** prev_dname_first, uint8_t** prev_dname_last, size_t* prev_dnamelen, uint16_t* prev_type, uint16_t* prev_dclass, struct rrset_parse** rrset_prev, sldns_pkt_section section, struct regional* region) { hashvalue_t dname_h = pkt_hash_rrset_first(pkt, dname); uint16_t covtype; if(*rrset_prev) { /* check if equal to previous item */ if(type == *prev_type && dclass == *prev_dclass && dnamelen == *prev_dnamelen && smart_compare(pkt, dname, *prev_dname_first, *prev_dname_last) == 0 && type != LDNS_RR_TYPE_RRSIG) { /* same as previous */ *prev_dname_last = dname; return 1; } /* check if rrsig over previous item */ if(type == LDNS_RR_TYPE_RRSIG && dclass == *prev_dclass && pkt_rrsig_covered_equals(pkt, sldns_buffer_current(pkt), *prev_type) && smart_compare(pkt, dname, *prev_dname_first, *prev_dname_last) == 0) { /* covers previous */ *prev_dname_last = dname; return 1; } } /* find by hashing and lookup in hashtable */ *rrset_flags = pkt_rrset_flags(pkt, type, section); /* if rrsig - try to lookup matching data set first */ if(type == LDNS_RR_TYPE_RRSIG && pkt_rrsig_covered(pkt, sldns_buffer_current(pkt), &covtype)) { *hash = pkt_hash_rrset_rest(dname_h, covtype, dclass, *rrset_flags); *rrset_prev = msgparse_hashtable_lookup(msg, pkt, *hash, *rrset_flags, dname, dnamelen, covtype, dclass); if(!*rrset_prev && covtype == LDNS_RR_TYPE_NSEC) { /* if NSEC try with NSEC apex bit twiddled */ *rrset_flags ^= PACKED_RRSET_NSEC_AT_APEX; *hash = pkt_hash_rrset_rest(dname_h, covtype, dclass, *rrset_flags); *rrset_prev = msgparse_hashtable_lookup(msg, pkt, *hash, *rrset_flags, dname, dnamelen, covtype, dclass); if(!*rrset_prev) /* untwiddle if not found */ *rrset_flags ^= PACKED_RRSET_NSEC_AT_APEX; } if(!*rrset_prev && covtype == LDNS_RR_TYPE_SOA) { /* if SOA try with SOA neg flag twiddled */ *rrset_flags ^= PACKED_RRSET_SOA_NEG; *hash = pkt_hash_rrset_rest(dname_h, covtype, dclass, *rrset_flags); *rrset_prev = msgparse_hashtable_lookup(msg, pkt, *hash, *rrset_flags, dname, dnamelen, covtype, dclass); if(!*rrset_prev) /* untwiddle if not found */ *rrset_flags ^= PACKED_RRSET_SOA_NEG; } if(*rrset_prev) { *prev_dname_first = (*rrset_prev)->dname; *prev_dname_last = dname; *prev_dnamelen = dnamelen; *prev_type = covtype; *prev_dclass = dclass; return 1; } } if(type != LDNS_RR_TYPE_RRSIG) { int hasother = 0; /* find matching rrsig */ *hash = pkt_hash_rrset_rest(dname_h, LDNS_RR_TYPE_RRSIG, dclass, 0); *rrset_prev = msgparse_hashtable_lookup(msg, pkt, *hash, 0, dname, dnamelen, LDNS_RR_TYPE_RRSIG, dclass); if(*rrset_prev && rrset_has_sigover(pkt, *rrset_prev, type, &hasother)) { /* yes! */ *prev_dname_first = (*rrset_prev)->dname; *prev_dname_last = dname; *prev_dnamelen = dnamelen; *prev_type = type; *prev_dclass = dclass; *rrset_prev = change_rrsig_rrset(*rrset_prev, msg, pkt, type, *rrset_flags, hasother, section, region); if(!*rrset_prev) return 0; return 1; } } *hash = pkt_hash_rrset_rest(dname_h, type, dclass, *rrset_flags); *rrset_prev = msgparse_hashtable_lookup(msg, pkt, *hash, *rrset_flags, dname, dnamelen, type, dclass); if(*rrset_prev) *prev_dname_first = (*rrset_prev)->dname; else *prev_dname_first = dname; *prev_dname_last = dname; *prev_dnamelen = dnamelen; *prev_type = type; *prev_dclass = dclass; return 1; }
/** * Canonicalize Rdata in buffer. * @param buf: buffer at position just after the rdata. * @param rrset: rrset with type. * @param len: length of the rdata (including rdatalen uint16). */ static void canonicalize_rdata(sldns_buffer* buf, struct ub_packed_rrset_key* rrset, size_t len) { uint8_t* datstart = sldns_buffer_current(buf)-len+2; switch(ntohs(rrset->rk.type)) { case LDNS_RR_TYPE_NXT: case LDNS_RR_TYPE_NS: case LDNS_RR_TYPE_MD: case LDNS_RR_TYPE_MF: case LDNS_RR_TYPE_CNAME: case LDNS_RR_TYPE_MB: case LDNS_RR_TYPE_MG: case LDNS_RR_TYPE_MR: case LDNS_RR_TYPE_PTR: case LDNS_RR_TYPE_DNAME: /* type only has a single argument, the name */ query_dname_tolower(datstart); return; case LDNS_RR_TYPE_MINFO: case LDNS_RR_TYPE_RP: case LDNS_RR_TYPE_SOA: /* two names after another */ query_dname_tolower(datstart); query_dname_tolower(datstart + dname_valid(datstart, len-2)); return; case LDNS_RR_TYPE_RT: case LDNS_RR_TYPE_AFSDB: case LDNS_RR_TYPE_KX: case LDNS_RR_TYPE_MX: /* skip fixed part */ if(len < 2+2+1) /* rdlen, skiplen, 1byteroot */ return; datstart += 2; query_dname_tolower(datstart); return; case LDNS_RR_TYPE_SIG: /* downcase the RRSIG, compat with BIND (kept it from SIG) */ case LDNS_RR_TYPE_RRSIG: /* skip fixed part */ if(len < 2+18+1) return; datstart += 18; query_dname_tolower(datstart); return; case LDNS_RR_TYPE_PX: /* skip, then two names after another */ if(len < 2+2+1) return; datstart += 2; query_dname_tolower(datstart); query_dname_tolower(datstart + dname_valid(datstart, len-2-2)); return; case LDNS_RR_TYPE_NAPTR: if(len < 2+4) return; len -= 2+4; datstart += 4; if(len < (size_t)datstart[0]+1) /* skip text field */ return; len -= (size_t)datstart[0]+1; datstart += (size_t)datstart[0]+1; if(len < (size_t)datstart[0]+1) /* skip text field */ return; len -= (size_t)datstart[0]+1; datstart += (size_t)datstart[0]+1; if(len < (size_t)datstart[0]+1) /* skip text field */ return; len -= (size_t)datstart[0]+1; datstart += (size_t)datstart[0]+1; if(len < 1) /* check name is at least 1 byte*/ return; query_dname_tolower(datstart); return; case LDNS_RR_TYPE_SRV: /* skip fixed part */ if(len < 2+6+1) return; datstart += 6; query_dname_tolower(datstart); return; /* do not canonicalize NSEC rdata name, compat with * from bind 9.4 signer, where it does not do so */ case LDNS_RR_TYPE_NSEC: /* type starts with the name */ case LDNS_RR_TYPE_HINFO: /* not downcased */ /* A6 not supported */ default: /* nothing to do for unknown types */ return; } }
/** 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; }