From: Calle Dybedahl Date: Wed, 25 Jun 2014 10:52:51 +0000 (+0200) Subject: Straightforward implementation of name compression when converting packets to wireformat. X-Git-Tag: release-1.7.0-rc1~160 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b1d178bc6067a74fde3d67ca8fadee73327434c0;p=thirdparty%2Fldns.git Straightforward implementation of name compression when converting packets to wireformat. --- diff --git a/host2wire.c b/host2wire.c index 8fb5c3a2..a461a2e3 100644 --- a/host2wire.c +++ b/host2wire.c @@ -16,25 +16,96 @@ #include -/* TODO Jelte - add a pointer to a 'possiblecompression' structure - to all the needed functions? - something like an array of name, pointer values? - every dname part could be added to it -*/ - ldns_status ldns_dname2buffer_wire(ldns_buffer *buffer, const ldns_rdf *name) { - if (ldns_buffer_reserve(buffer, ldns_rdf_size(name))) { - ldns_buffer_write(buffer, ldns_rdf_data(name), ldns_rdf_size(name)); + return ldns_dname2buffer_wire_compress(buffer, name, NULL); +} + +ldns_status +ldns_dname2buffer_wire_compress(ldns_buffer *buffer, const ldns_rdf *name, ldns_rbtree_t *compression_data) +{ + ldns_rbnode_t *node; + uint8_t *data; + size_t size; + + /* If no tree, just add the data */ + if(!compression_data) + { + if (ldns_buffer_reserve(buffer, ldns_rdf_size(name))) + { + ldns_buffer_write(buffer, ldns_rdf_data(name), ldns_rdf_size(name)); + } + return ldns_buffer_status(buffer); + } + + /* No labels left, write final zero */ + if(ldns_dname_label_count(name)==0) + { + if(ldns_buffer_reserve(buffer,1)) + { + ldns_buffer_write_u8(buffer, 0); + } + return ldns_buffer_status(buffer); + } + + /* Can we find the name in the tree? */ + if((node = ldns_rbtree_search(compression_data, ldns_rdf_data(name))) != NULL) + { + /* Found */ + uint16_t position = (49152 | (uint16_t)node->data); + if (ldns_buffer_reserve(buffer, 2)) + { + ldns_buffer_write_u16(buffer, position); + } + return ldns_buffer_status(buffer); + } + else + { + /* Not found. Write cache entry, take off first label, write it, */ + /* try again with the rest of the name. */ + ldns_rbnode_t *node = LDNS_MALLOC(ldns_rbnode_t); + if(!node) + { + return LDNS_STATUS_MEM_ERR; + } + node->key = strdup((const char *)ldns_rdf_data(name)); + node->data = (void *)ldns_buffer_position(buffer); + if(!ldns_rbtree_insert(compression_data,node)) + { + /* fprintf(stderr,"Name not found but now it's there?\n"); */ + } + + ldns_rdf *label = ldns_dname_label(name,0); + ldns_rdf *rest = ldns_dname_left_chop(name); + size = ldns_rdf_size(label) - 1; /* Don't want the final zero */ + data = ldns_rdf_data(label); + if(ldns_buffer_reserve(buffer, size)) + { + ldns_buffer_write(buffer, data, size); + } + ldns_rdf_free(label); + ldns_status s = ldns_dname2buffer_wire_compress(buffer, rest, compression_data); + ldns_rdf_free(rest); + return s; } - return ldns_buffer_status(buffer); } ldns_status ldns_rdf2buffer_wire(ldns_buffer *buffer, const ldns_rdf *rdf) { + return ldns_rdf2buffer_wire_compress(buffer, rdf, NULL); +} + +ldns_status +ldns_rdf2buffer_wire_compress(ldns_buffer *buffer, const ldns_rdf *rdf, ldns_rbtree_t *compression_data) +{ + /* If it's a DNAME, call that function to get compression */ + if(compression_data && ldns_rdf_get_type(rdf) == LDNS_RDF_TYPE_DNAME) + { + return ldns_dname2buffer_wire_compress(buffer,rdf,compression_data); + } + if (ldns_buffer_reserve(buffer, ldns_rdf_size(rdf))) { ldns_buffer_write(buffer, ldns_rdf_data(rdf), ldns_rdf_size(rdf)); } @@ -157,12 +228,18 @@ ldns_rr2buffer_wire_canonical(ldns_buffer *buffer, ldns_status ldns_rr2buffer_wire(ldns_buffer *buffer, const ldns_rr *rr, int section) +{ + return ldns_rr2buffer_wire_compress(buffer,rr,section,NULL); +} + +ldns_status +ldns_rr2buffer_wire_compress(ldns_buffer *buffer, const ldns_rr *rr, int section, ldns_rbtree_t *compression_data) { uint16_t i; uint16_t rdl_pos = 0; - + if (ldns_rr_owner(rr)) { - (void) ldns_dname2buffer_wire(buffer, ldns_rr_owner(rr)); + (void) ldns_dname2buffer_wire_compress(buffer, ldns_rr_owner(rr), compression_data); } if (ldns_buffer_reserve(buffer, 4)) { @@ -178,8 +255,8 @@ ldns_rr2buffer_wire(ldns_buffer *buffer, const ldns_rr *rr, int section) ldns_buffer_write_u16(buffer, 0); } for (i = 0; i < ldns_rr_rd_count(rr); i++) { - (void) ldns_rdf2buffer_wire( - buffer, ldns_rr_rdf(rr, i)); + (void) ldns_rdf2buffer_wire_compress( + buffer, ldns_rr_rdf(rr, i), compression_data); } if (rdl_pos != 0) { ldns_buffer_write_u16_at(buffer, rdl_pos, @@ -214,6 +291,7 @@ ldns_status ldns_rr_rdata2buffer_wire(ldns_buffer *buffer, const ldns_rr *rr) { uint16_t i; + /* convert all the rdf's */ for (i = 0; i < ldns_rr_rd_count(rr); i++) { (void) ldns_rdf2buffer_wire(buffer, ldns_rr_rdf(rr,i)); @@ -229,7 +307,7 @@ ldns_hdr2buffer_wire(ldns_buffer *buffer, const ldns_pkt *packet) { uint8_t flags; uint16_t arcount; - + if (ldns_buffer_reserve(buffer, 12)) { ldns_buffer_write_u16(buffer, ldns_pkt_id(packet)); @@ -263,44 +341,54 @@ ldns_hdr2buffer_wire(ldns_buffer *buffer, const ldns_pkt *packet) return ldns_buffer_status(buffer); } +void +compression_node_free(ldns_rbnode_t *node, void *arg) +{ + (void)arg; /* Yes, dear compiler, it is used */ + free((void *)node->key); + LDNS_FREE(node); +} + ldns_status ldns_pkt2buffer_wire(ldns_buffer *buffer, const ldns_pkt *packet) { ldns_rr_list *rr_list; uint16_t i; - + /* edns tmp vars */ ldns_rr *edns_rr; uint8_t edata[4]; + + ldns_rbtree_t *compression_data = ldns_rbtree_create((int (*)(const void *, const void *))strcasecmp); (void) ldns_hdr2buffer_wire(buffer, packet); rr_list = ldns_pkt_question(packet); if (rr_list) { for (i = 0; i < ldns_rr_list_rr_count(rr_list); i++) { - (void) ldns_rr2buffer_wire(buffer, - ldns_rr_list_rr(rr_list, i), LDNS_SECTION_QUESTION); + (void) ldns_rr2buffer_wire_compress(buffer, + ldns_rr_list_rr(rr_list, i), LDNS_SECTION_QUESTION, compression_data); } } rr_list = ldns_pkt_answer(packet); if (rr_list) { for (i = 0; i < ldns_rr_list_rr_count(rr_list); i++) { - (void) ldns_rr2buffer_wire(buffer, - ldns_rr_list_rr(rr_list, i), LDNS_SECTION_ANSWER); + (void) ldns_rr2buffer_wire_compress(buffer, + ldns_rr_list_rr(rr_list, i), LDNS_SECTION_ANSWER, compression_data); } } rr_list = ldns_pkt_authority(packet); if (rr_list) { for (i = 0; i < ldns_rr_list_rr_count(rr_list); i++) { - (void) ldns_rr2buffer_wire(buffer, - ldns_rr_list_rr(rr_list, i), LDNS_SECTION_AUTHORITY); + (void) ldns_rr2buffer_wire_compress(buffer, + ldns_rr_list_rr(rr_list, i), LDNS_SECTION_AUTHORITY, compression_data); } } rr_list = ldns_pkt_additional(packet); if (rr_list) { for (i = 0; i < ldns_rr_list_rr_count(rr_list); i++) { - (void) ldns_rr2buffer_wire(buffer, - ldns_rr_list_rr(rr_list, i), LDNS_SECTION_ADDITIONAL); + (void) ldns_rr2buffer_wire_compress(buffer, + ldns_rr_list_rr(rr_list, i), LDNS_SECTION_ADDITIONAL, compression_data); } } @@ -319,7 +407,7 @@ ldns_pkt2buffer_wire(ldns_buffer *buffer, const ldns_pkt *packet) /* don't forget to add the edns rdata (if any) */ if (packet->_edns_data) ldns_rr_push_rdf (edns_rr, packet->_edns_data); - (void)ldns_rr2buffer_wire(buffer, edns_rr, LDNS_SECTION_ADDITIONAL); + (void)ldns_rr2buffer_wire_compress(buffer, edns_rr, LDNS_SECTION_ADDITIONAL, compression_data); /* take the edns rdata back out of the rr before we free rr */ if (packet->_edns_data) (void)ldns_rr_pop_rdf (edns_rr); @@ -328,10 +416,13 @@ ldns_pkt2buffer_wire(ldns_buffer *buffer, const ldns_pkt *packet) /* add TSIG to additional if it is there */ if (ldns_pkt_tsig(packet)) { - (void) ldns_rr2buffer_wire(buffer, - ldns_pkt_tsig(packet), LDNS_SECTION_ADDITIONAL); + (void) ldns_rr2buffer_wire_compress(buffer, + ldns_pkt_tsig(packet), LDNS_SECTION_ADDITIONAL, compression_data); } - + + ldns_traverse_postorder(compression_data,compression_node_free,NULL); + ldns_rbtree_free(compression_data); + return LDNS_STATUS_OK; } diff --git a/ldns/host2wire.h b/ldns/host2wire.h index f3e3d43f..94693cda 100644 --- a/ldns/host2wire.h +++ b/ldns/host2wire.h @@ -39,6 +39,15 @@ extern "C" { */ ldns_status ldns_dname2buffer_wire(ldns_buffer *buffer, const ldns_rdf *name); +/** + * Copies the dname data to the buffer in wire format + * \param[out] *buffer buffer to append the result to + * \param[in] *name rdata dname to convert + * \param[out] *compression_data data structure holding state for compression + * \return ldns_status + */ +ldns_status ldns_dname2buffer_wire_compress(ldns_buffer *buffer, const ldns_rdf *name, ldns_rbtree_t *compression_data); + /** * Copies the rdata data to the buffer in wire format * \param[out] *output buffer to append the result to @@ -47,6 +56,15 @@ ldns_status ldns_dname2buffer_wire(ldns_buffer *buffer, const ldns_rdf *name); */ ldns_status ldns_rdf2buffer_wire(ldns_buffer *output, const ldns_rdf *rdf); +/** + * Copies the rdata data to the buffer in wire format + * \param[out] *output buffer to append the result to + * \param[in] *rdf rdata to convert + * \param[out] *compression_data data structure holding state for compression + * \return ldns_status + */ +ldns_status ldns_rdf2buffer_wire_compress(ldns_buffer *output, const ldns_rdf *rdf, ldns_rbtree_t *compression_data); + /** * Copies the rdata data to the buffer in wire format * If the rdata is a dname, the letters will be lowercased @@ -70,6 +88,20 @@ ldns_status ldns_rr2buffer_wire(ldns_buffer *output, const ldns_rr *rr, int section); +/** + * Copies the rr data to the buffer in wire format while doing DNAME compression + * \param[out] *output buffer to append the result to + * \param[in] *rr resource record to convert + * \param[in] section the section in the packet this rr is supposed to be in + * (to determine whether to add rdata or not) + * \param[out] *compression_data data structure holding state information for compression + * \return ldns_status + */ +ldns_status ldns_rr2buffer_wire_compress(ldns_buffer *output, + const ldns_rr *rr, + int section, + ldns_rbtree_t *compression_data); + /** * Copies the rr data to the buffer in wire format, in canonical format * according to RFC3597 (every dname in rdata fields of RR's mentioned in