/** 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; }
/** compress domain names in rdata, return RETVAL_* */ static int compress_rdata(sldns_buffer* pkt, uint8_t* rdata, size_t todolen, struct regional* region, struct compress_tree_node** tree, const sldns_rr_descriptor* desc) { int labs, r, rdf = 0; size_t dname_len, len, pos = sldns_buffer_position(pkt); uint8_t count = desc->_dname_count; sldns_buffer_skip(pkt, 2); /* rdata len fill in later */ /* space for rdatalen checked for already */ rdata += 2; todolen -= 2; while(todolen > 0 && count) { switch(desc->_wireformat[rdf]) { case LDNS_RDF_TYPE_DNAME: labs = dname_count_size_labels(rdata, &dname_len); if((r=compress_any_dname(rdata, pkt, labs, region, tree)) != RETVAL_OK) return r; rdata += dname_len; todolen -= dname_len; count--; len = 0; break; case LDNS_RDF_TYPE_STR: len = *rdata + 1; break; default: len = get_rdf_size(desc->_wireformat[rdf]); } if(len) { /* copy over */ if(sldns_buffer_remaining(pkt) < len) return RETVAL_TRUNC; sldns_buffer_write(pkt, rdata, len); todolen -= len; rdata += len; } rdf++; } /* copy remainder */ if(todolen > 0) { if(sldns_buffer_remaining(pkt) < todolen) return RETVAL_TRUNC; sldns_buffer_write(pkt, rdata, todolen); } /* set rdata len */ sldns_buffer_write_u16_at(pkt, pos, sldns_buffer_position(pkt)-pos-2); return RETVAL_OK; }
/** analyze rdata in packet */ static void analyze_rdata(ldns_buffer*pkt, const ldns_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 = ldns_buffer_position(pkt); analyze_dname(pkt); rdlen -= ldns_buffer_position(pkt)-oldpos; count --; len = 0; break; case LDNS_RDF_TYPE_STR: len = ldns_buffer_current(pkt)[0] + 1; break; default: len = get_rdf_size(desc->_wireformat[rdf]); } if(len) { printf(" wf[%d]", (int)len); ldns_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)ldns_buffer_current(pkt)[i]); printf("\n"); } else printf("\n"); ldns_buffer_skip(pkt, (ssize_t)rdlen); }
/** * Compare two RR for canonical order, in a field-style sweep. * @param d: rrset data * @param desc: ldns wireformat descriptor. * @param i: first RR to compare * @param j: first RR to compare * @return comparison code. */ static int canonical_compare_byfield(struct packed_rrset_data* d, const sldns_rr_descriptor* desc, size_t i, size_t j) { /* sweep across rdata, keep track of some state: * which rr field, and bytes left in field. * current position in rdata, length left. * are we in a dname, length left in a label. */ int wfi = -1; /* current wireformat rdata field (rdf) */ int wfj = -1; uint8_t* di = d->rr_data[i]+2; /* ptr to current rdata byte */ uint8_t* dj = d->rr_data[j]+2; size_t ilen = d->rr_len[i]-2; /* length left in rdata */ size_t jlen = d->rr_len[j]-2; int dname_i = 0; /* true if these bytes are part of a name */ int dname_j = 0; size_t lablen_i = 0; /* 0 for label length byte,for first byte of rdf*/ size_t lablen_j = 0; /* otherwise remaining length of rdf or label */ int dname_num_i = (int)desc->_dname_count; /* decreased at root label */ int dname_num_j = (int)desc->_dname_count; /* loop while there are rdata bytes available for both rrs, * and still some lowercasing needs to be done; either the dnames * have not been reached yet, or they are currently being processed */ while(ilen > 0 && jlen > 0 && (dname_num_i > 0 || dname_num_j > 0)) { /* compare these two bytes */ /* lowercase if in a dname and not a label length byte */ if( ((dname_i && lablen_i)?(uint8_t)tolower((int)*di):*di) != ((dname_j && lablen_j)?(uint8_t)tolower((int)*dj):*dj) ) { if(((dname_i && lablen_i)?(uint8_t)tolower((int)*di):*di) < ((dname_j && lablen_j)?(uint8_t)tolower((int)*dj):*dj)) return -1; return 1; } ilen--; jlen--; /* bytes are equal */ /* advance field i */ /* lablen 0 means that this byte is the first byte of the * next rdata field; inspect this rdata field and setup * to process the rest of this rdata field. * The reason to first read the byte, then setup the rdf, * is that we are then sure the byte is available and short * rdata is handled gracefully (even if it is a formerr). */ if(lablen_i == 0) { if(dname_i) { /* scan this dname label */ /* capture length to lowercase */ lablen_i = (size_t)*di; if(lablen_i == 0) { /* end root label */ dname_i = 0; dname_num_i--; /* if dname num is 0, then the * remainder is binary only */ if(dname_num_i == 0) lablen_i = ilen; } } else { /* scan this rdata field */ wfi++; if(desc->_wireformat[wfi] == LDNS_RDF_TYPE_DNAME) { dname_i = 1; lablen_i = (size_t)*di; if(lablen_i == 0) { dname_i = 0; dname_num_i--; if(dname_num_i == 0) lablen_i = ilen; } } else if(desc->_wireformat[wfi] == LDNS_RDF_TYPE_STR) lablen_i = (size_t)*di; else lablen_i = get_rdf_size( desc->_wireformat[wfi]) - 1; } } else lablen_i--; /* advance field j; same as for i */ if(lablen_j == 0) { if(dname_j) { lablen_j = (size_t)*dj; if(lablen_j == 0) { dname_j = 0; dname_num_j--; if(dname_num_j == 0) lablen_j = jlen; } } else { wfj++; if(desc->_wireformat[wfj] == LDNS_RDF_TYPE_DNAME) { dname_j = 1; lablen_j = (size_t)*dj; if(lablen_j == 0) { dname_j = 0; dname_num_j--; if(dname_num_j == 0) lablen_j = jlen; } } else if(desc->_wireformat[wfj] == LDNS_RDF_TYPE_STR) lablen_j = (size_t)*dj; else lablen_j = get_rdf_size( desc->_wireformat[wfj]) - 1; } } else lablen_j--; di++; dj++; } /* end of the loop; because we advanced byte by byte; now we have * that the rdata has ended, or that there is a binary remainder */ /* shortest first */ if(ilen == 0 && jlen == 0) return 0; if(ilen == 0) return -1; if(jlen == 0) return 1; /* binary remainder, capture comparison in wfi variable */ if((wfi = memcmp(di, dj, (ilen<jlen)?ilen:jlen)) != 0) return wfi; if(ilen < jlen) return -1; if(jlen < ilen) return 1; 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; }