diff --git a/src/const-info.c b/src/const-info.c index 1e55a99a..3cfae20e 100644 --- a/src/const-info.c +++ b/src/const-info.c @@ -23,6 +23,7 @@ static struct const_info consts_info[] = { { 310, "GETDNS_RETURN_MEMORY_ERROR", GETDNS_RETURN_MEMORY_ERROR_TEXT }, { 311, "GETDNS_RETURN_INVALID_PARAMETER", GETDNS_RETURN_INVALID_PARAMETER_TEXT }, { 312, "GETDNS_RETURN_NOT_IMPLEMENTED", GETDNS_RETURN_NOT_IMPLEMENTED_TEXT }, + { 399, "GETDNS_RETURN_NEED_MORE_SPACE", GETDNS_RETURN_NEED_MORE_SPACE_TEXT }, { 400, "GETDNS_DNSSEC_SECURE", GETDNS_DNSSEC_SECURE_TEXT }, { 401, "GETDNS_DNSSEC_BOGUS", GETDNS_DNSSEC_BOGUS_TEXT }, { 402, "GETDNS_DNSSEC_INDETERMINATE", GETDNS_DNSSEC_INDETERMINATE_TEXT }, diff --git a/src/convert.c b/src/convert.c index 15e07a1c..c7344875 100644 --- a/src/convert.c +++ b/src/convert.c @@ -220,62 +220,192 @@ getdns_strerror(getdns_return_t err, char *buf, size_t buflen) return GETDNS_RETURN_GOOD; } /* getdns_strerror */ + +/* --------------------- rr_dict, wire, str conversions --------------------- */ + + getdns_return_t getdns_rr_dict2wire( + const getdns_dict *rr_dict, uint8_t **wire, size_t *wire_sz) +{ + uint8_t buf_spc[4096], *buf; + size_t buf_len = sizeof(buf_spc); + getdns_return_t r = getdns_rr_dict2wire_buf( + rr_dict, buf_spc, &buf_len); + + if (r != GETDNS_RETURN_GOOD && r != GETDNS_RETURN_NEED_MORE_SPACE) + return r; + + if (!(buf = malloc(buf_len))) + return GETDNS_RETURN_MEMORY_ERROR; + + if (!r) + memcpy(buf, buf_spc, buf_len); + + else if ((r = getdns_rr_dict2wire_buf(rr_dict, buf, &buf_len))) { + free(buf); + return r; + } + *wire = buf; + *wire_sz = buf_len; + return GETDNS_RETURN_GOOD; +} + +getdns_return_t +getdns_rr_dict2wire_buf( const getdns_dict *rr_dict, uint8_t *wire, size_t *wire_sz) +{ + ssize_t my_wire_sz; + getdns_return_t r; + + if (!wire_sz) + return GETDNS_RETURN_INVALID_PARAMETER; + else + my_wire_sz = *wire_sz; + + r = getdns_rr_dict2wire_scan(rr_dict, &wire, &my_wire_sz); + if (r == GETDNS_RETURN_GOOD || r == GETDNS_RETURN_NEED_MORE_SPACE) + *wire_sz -= my_wire_sz; + return r; +} + +getdns_return_t +getdns_rr_dict2wire_scan( + const getdns_dict *rr_dict, uint8_t **wire, ssize_t *wire_sz) { getdns_return_t r; gldns_buffer gbuf; - if (!rr_dict || !wire || !wire_sz) + if (!rr_dict || !wire || !*wire || !wire_sz) return GETDNS_RETURN_INVALID_PARAMETER; - gldns_buffer_init_frm_data(&gbuf, wire, *wire_sz); - r = _getdns_rr_dict2wire(rr_dict, &gbuf); - *wire_sz = gldns_buffer_position(&gbuf); - if (r) + gldns_buffer_init_frm_data(&gbuf, *wire, *wire_sz); + if ((r = _getdns_rr_dict2wire(rr_dict, &gbuf))) return r; - if (gldns_buffer_position(&gbuf) == 0 || - gldns_buffer_position(&gbuf) > gldns_buffer_capacity(&gbuf)) + if (gldns_buffer_position(&gbuf) == 0) return GETDNS_RETURN_GENERIC_ERROR; - return GETDNS_RETURN_GOOD; + *wire += gldns_buffer_position(&gbuf); + *wire_sz -= gldns_buffer_position(&gbuf); + if (gldns_buffer_position(&gbuf) > gldns_buffer_limit(&gbuf)) + return GETDNS_RETURN_NEED_MORE_SPACE; + else + return GETDNS_RETURN_GOOD; } getdns_return_t getdns_wire2rr_dict( const uint8_t *wire, size_t wire_len, getdns_dict **rr_dict) +{ + return getdns_wire2rr_dict_scan(&wire, &wire_len, rr_dict); +} + +getdns_return_t +getdns_wire2rr_dict_buf( + const uint8_t *wire, size_t *wire_len, getdns_dict **rr_dict) +{ + size_t my_wire_len; + getdns_return_t r; + + if (!wire_len) + return GETDNS_RETURN_INVALID_PARAMETER; + else + my_wire_len = *wire_len; + + if ((r = getdns_wire2rr_dict_scan(&wire, &my_wire_len, rr_dict))) + return r; + + *wire_len -= my_wire_len; + return GETDNS_RETURN_GOOD; +} + +getdns_return_t +getdns_wire2rr_dict_scan( + const uint8_t **wire, size_t *wire_len, getdns_dict **rr_dict) { static struct mem_funcs plain_mem_funcs = { MF_PLAIN, .mf.pln = { malloc, realloc, free } }; _getdns_rr_iter rr_iter_spc, *rr_iter; - if (!wire || !rr_dict) + if (!wire || !*wire || !wire_len || !rr_dict) return GETDNS_RETURN_INVALID_PARAMETER; if (!(rr_iter = _getdns_single_rr_iter_init( - &rr_iter_spc, wire, wire_len))) + &rr_iter_spc, *wire, *wire_len))) return GETDNS_RETURN_GENERIC_ERROR; if (!(*rr_dict = _getdns_rr_iter2rr_dict(&plain_mem_funcs, rr_iter))) return GETDNS_RETURN_MEMORY_ERROR; + *wire_len -= (rr_iter->nxt - rr_iter->pos); + *wire = rr_iter->pos; + return GETDNS_RETURN_GOOD; } + getdns_return_t getdns_rr_dict2str( const getdns_dict *rr_dict, char **str) +{ + char buf_spc[4096], *buf; + size_t buf_len = sizeof(buf_spc) - 1; + getdns_return_t r = getdns_rr_dict2str_buf( + rr_dict, buf_spc, &buf_len); + + if (r != GETDNS_RETURN_GOOD && r != GETDNS_RETURN_NEED_MORE_SPACE) + return r; + + if (!(buf = malloc(buf_len + 1))) + return GETDNS_RETURN_MEMORY_ERROR; + + if (!r) + memcpy(buf, buf_spc, buf_len); + + else if ((r = getdns_rr_dict2str_buf(rr_dict, buf, &buf_len))) { + free(buf); + return r; + } + buf[buf_len] = 0; + *str = buf; + return GETDNS_RETURN_GOOD; +} + +getdns_return_t +getdns_rr_dict2str_buf( + const getdns_dict *rr_dict, char *str, size_t *str_len) +{ + ssize_t my_str_len; + getdns_return_t r; + + if (!str_len) + return GETDNS_RETURN_INVALID_PARAMETER; + else + my_str_len = *str_len; + + r = getdns_rr_dict2str_scan(rr_dict, &str, &my_str_len); + if (r == GETDNS_RETURN_GOOD || r == GETDNS_RETURN_NEED_MORE_SPACE) + *str_len -= my_str_len; + return r; + +} + +getdns_return_t +getdns_rr_dict2str_scan( + const getdns_dict *rr_dict, char **str, ssize_t *str_len) { getdns_return_t r; gldns_buffer gbuf; - uint8_t buf_spc[4096], *buf = buf_spc; - size_t sz; + uint8_t buf_spc[4096], *buf = buf_spc, *scan_buf; + size_t sz, scan_sz; + ssize_t prev_str_len; + char *prev_str; + int sz_needed; - if (!rr_dict || !str) + if (!rr_dict || !str || !*str || !str_len) return GETDNS_RETURN_INVALID_PARAMETER; gldns_buffer_init_frm_data(&gbuf, buf, sizeof(buf_spc)); @@ -293,15 +423,27 @@ getdns_rr_dict2str( GETDNS_FREE(rr_dict->mf, buf); return r; } - if (!(*str = gldns_wire2str_rr(gldns_buffer_begin(&gbuf), - gldns_buffer_position(&gbuf)))) - r = GETDNS_RETURN_MEMORY_ERROR; + scan_buf = gldns_buffer_begin(&gbuf); + scan_sz = gldns_buffer_position(&gbuf); + prev_str = *str; + prev_str_len = *str_len; + sz = (size_t)*str_len; + sz_needed = gldns_wire2str_rr_scan( + &scan_buf, &scan_sz, str, &sz, NULL, 0); + if (sz_needed > prev_str_len) { + *str = prev_str + sz_needed; + *str_len = prev_str_len - sz_needed; + r = GETDNS_RETURN_NEED_MORE_SPACE; + } else + *str_len = sz; + if (buf != buf_spc) GETDNS_FREE(rr_dict->mf, buf); return r; } + getdns_return_t getdns_str2rr_dict( const char *str, getdns_dict **rr_dict, const char *origin, uint32_t default_ttl) diff --git a/src/getdns/getdns_extra.h.in b/src/getdns/getdns_extra.h.in index 2cb130a7..a66ee2f9 100644 --- a/src/getdns/getdns_extra.h.in +++ b/src/getdns/getdns_extra.h.in @@ -400,21 +400,166 @@ getdns_context_get_tls_authentication(getdns_context *context, #define GETDNS_TRANSPORT_TLS_FIRST_AND_FALL_BACK_TO_TCP_KEEP_CONNECTIONS_OPEN 545 #define GETDNS_TRANSPORT_TLS_FIRST_AND_FALL_BACK_TO_TCP_KEEP_CONNECTIONS_OPEN_TEXT "See getdns_context_set_dns_transport()" +#define GETDNS_RETURN_NEED_MORE_SPACE ((getdns_return_t) 399 ) +#define GETDNS_RETURN_NEED_MORE_SPACE_TEXT "The buffer was too small" + +/** + * Convert rr_dict to wireformat representation of the resource record. + * + * @param rr_dict The getdns dict representation of the resource record + * @return wire A newly allocated buffer which will contain the wireformat. + * @return wire_sz The size of the allocated buffer and the wireformat. + * @return GETDNS_RETURN_GOOD on success or an error code on failure. + */ getdns_return_t getdns_rr_dict2wire( + const getdns_dict *rr_dict, uint8_t **wire, size_t *wire_sz); + +/** + * Convert rr_dict to wireformat representation of the resource record. + * + * @param rr_dict The getdns dict representation of the resource record + * @param wire The buffer in which the wireformat will be written + * @param wire_sz On input the size of the wire buffer, + * On output the amount of wireformat needed for the + * wireformat representation of the resource record; + * even if it did not fit. + * @return GETDNS_RETURN_GOOD on success or an error code on failure. + * GETDNS_RETURN_NEED_MORE_SPACE will be returned when the buffer was too + * small. wire_sz will be set to the needed buffer space then. + */ +getdns_return_t +getdns_rr_dict2wire_buf( const getdns_dict *rr_dict, uint8_t *wire, size_t *wire_sz); +/** + * Convert rr_dict to wireformat representation of the resource record. + * + * @param rr_dict The getdns dict representation of the resource record + * @param wire A pointer to the buffer pointer in which the wireformat + * will be written. + * On output the buffer pointer will have moved along + * the buffer and point right after the just written RR. + * @param wire_sz On input the size of the wire buffer, + * On output the amount of wireformat needed for the + * wireformat will have been substracted from wire_sz. + * @return GETDNS_RETURN_GOOD on success or an error code on failure. + * GETDNS_RETURN_NEED_MORE_SPACE will be returned when the buffer was too + * small. The function will pretend that it had written beyond the end + * of the buffer, and wire will point past the buffer and wire_sz will + * contain a negative value. + */ +getdns_return_t +getdns_rr_dict2wire_scan( + const getdns_dict *rr_dict, uint8_t **wire, ssize_t *wire_sz); + + +/** + * Convert wireformat resource record in a getdns rr_dict representation. + * + * @param wire Buffer containing the wireformat rr + * @param wire_sz Size of the wire buffer + * @return rr_dict The returned rr_dict + * @return GETDNS_RETURN_GOOD on success or an error code on failure. + */ getdns_return_t getdns_wire2rr_dict( const uint8_t *wire, size_t wire_sz, getdns_dict **rr_dict); +/** + * Convert wireformat resource record in a getdns rr_dict representation. + * + * @param wire Buffer containing the wireformat rr + * @param wire_sz On input the size of the wire buffer + * On output the length of the wireformat rr. + * @return rr_dict The returned rr_dict + * @return GETDNS_RETURN_GOOD on success or an error code on failure. + */ +getdns_return_t +getdns_wire2rr_dict_buf( + const uint8_t *wire, size_t *wire_sz, getdns_dict **rr_dict); + +/** + * Convert wireformat resource record in a getdns rr_dict representation. + * + * @param wire A pointer to the pointer of the wireformat buffer. + * On return this pointer is moved to after first read + * in resource record. + * @param wire_sz On input the size of the wire buffer + * On output the size is decreased with the length + * of the wireformat resource record. + * @return rr_dict The returned rr_dict + * @return GETDNS_RETURN_GOOD on success or an error code on failure. + */ +getdns_return_t +getdns_wire2rr_dict_scan( + const uint8_t **wire, size_t *wire_sz, getdns_dict **rr_dict); + + +/** + * Convert rr_dict to the string representation of the resource record. + * + * @param rr_dict The getdns dict representation of the resource record + * @return str A newly allocated string representation of the rr + * @return GETDNS_RETURN_GOOD on success or an error code on failure. + */ getdns_return_t getdns_rr_dict2str( const getdns_dict *rr_dict, char **str); +/** + * Convert rr_dict to the string representation of the resource record. + * + * @param rr_dict The getdns dict representation of the resource record + * @param str The buffer in which the string will be written + * @param str_len On input the size of the text buffer, + * On output the amount of characters needed to write + * the string representation of the rr. Even if it does + * not fit. + * @return GETDNS_RETURN_GOOD on success or an error code on failure. + * GETDNS_RETURN_NEED_MORE_SPACE will be returned when the buffer was too + * small. str_len will be set to the needed buffer space then. + */ +getdns_return_t +getdns_rr_dict2str_buf( + const getdns_dict *rr_dict, char *str, size_t *str_len); + +/** + * Convert rr_dict to the string representation of the resource record. + * + * @param rr_dict The getdns dict representation of the resource record + * @param str A pointer to the buffer pointer in which the string + * will be written. + * On output the buffer pointer will have moved along + * the buffer and point right after the just written RR. + * @param str_len On input the size of the str buffer, + * On output the number of characters needed for the + * string will have been substracted from strlen. + * @return GETDNS_RETURN_GOOD on success or an error code on failure. + * GETDNS_RETURN_NEED_MORE_SPACE will be returned when the buffer was too + * small. The function will pretend that it had written beyond the end + * of the buffer, and str will point past the buffer and str_len will + * contain a negative value. + */ +getdns_return_t +getdns_rr_dict2str_scan( + const getdns_dict *rr_dict, char **str, ssize_t *str_len); + + +/** + * Convert the string representation of the resource record to rr_dict format. + * + * @param str String representation of the resource record. + * @return rr_dict The result getdns dict representation of the resource record + * @param origin Default suffix for not fully qualified domain names + * @param default_ttl Default ttl + * @return GETDNS_RETURN_GOOD on success or an error code on failure. + */ getdns_return_t getdns_str2rr_dict( - const char *str, getdns_dict **rr_dict, const char *origin, uint32_t default_ttl); + const char *str, getdns_dict **rr_dict, + const char *origin, uint32_t default_ttl); + #ifdef __cplusplus } diff --git a/src/mk-const-info.c.sh b/src/mk-const-info.c.sh index f343e6fc..1bf7927f 100755 --- a/src/mk-const-info.c.sh +++ b/src/mk-const-info.c.sh @@ -12,7 +12,7 @@ cat > const-info.c << END_OF_HEAD static struct const_info consts_info[] = { { -1, NULL, "/* */" }, END_OF_HEAD -gawk '/^[ ]+GETDNS_[A-Z_]+[ ]+=[ ]+[0-9]+/{ key = sprintf("%4d", $3); consts[key] = $1; }/^#define GETDNS_[A-Z_]+[ ]+[0-9]+/ && !/^#define GETDNS_RRTYPE/ && !/^#define GETDNS_RRCLASS/ && !/^#define GETDNS_OPCODE/ && !/^#define GETDNS_RCODE/ && !/_TEXT/{ key = sprintf("%4d", $3); consts[key] = $2; }END{ n = asorti(consts, const_vals); for ( i = 1; i <= n; i++) { val = const_vals[i]; name = consts[val]; print "\t{ "val", \""name"\", "name"_TEXT },"}}' getdns/getdns.h.in getdns/getdns_extra.h.in | sed 's/,,/,/g' >> const-info.c +gawk '/^[ ]+GETDNS_[A-Z_]+[ ]+=[ ]+[0-9]+/{ key = sprintf("%4d", $3); consts[key] = $1; }/^#define GETDNS_[A-Z_]+[ ]+[0-9]+/ && !/^#define GETDNS_RRTYPE/ && !/^#define GETDNS_RRCLASS/ && !/^#define GETDNS_OPCODE/ && !/^#define GETDNS_RCODE/ && !/_TEXT/{ key = sprintf("%4d", $3); consts[key] = $2; }/^#define GETDNS_[A-Z_]+[ ]+\(\(getdns_return_t) [0-9]+ \)/{ key = sprintf("%4d", $4); consts[key] = $2; }END{ n = asorti(consts, const_vals); for ( i = 1; i <= n; i++) { val = const_vals[i]; name = consts[val]; print "\t{ "val", \""name"\", "name"_TEXT },"}}' getdns/getdns.h.in getdns/getdns_extra.h.in | sed 's/,,/,/g' >> const-info.c cat >> const-info.c << END_OF_TAIL }; diff --git a/src/rr-dict.h b/src/rr-dict.h index c605be32..6ab52b88 100644 --- a/src/rr-dict.h +++ b/src/rr-dict.h @@ -36,8 +36,6 @@ #include "getdns/getdns.h" #include "gldns/gbuffer.h" -#define GETDNS_RETURN_NEED_MORE_SPACE ((getdns_return_t)399) - /* rdf_end returns a pointer to the end of this rdf's data, * i.e. where the next rdata field will start. */ diff --git a/src/rr-iter.c b/src/rr-iter.c index 31913b20..1615d761 100644 --- a/src/rr-iter.c +++ b/src/rr-iter.c @@ -39,11 +39,7 @@ rr_iter_find_nxt(_getdns_rr_iter *i) assert(i); assert(i->rr_type); - if (!i->pkt) { - i->nxt = i->pkt_end; - return; - } - i->nxt = i->n < GLDNS_QDCOUNT(i->pkt) + i->nxt = i->pkt && i->n < GLDNS_QDCOUNT(i->pkt) ? i->rr_type + 4 : i->rr_type + 10 > i->pkt_end ? i->pkt_end diff --git a/src/test/tpkg/260-conversion-functions.tpkg/260-conversion-functions.c b/src/test/tpkg/260-conversion-functions.tpkg/260-conversion-functions.c index f3f5a4b6..614284b4 100644 --- a/src/test/tpkg/260-conversion-functions.tpkg/260-conversion-functions.c +++ b/src/test/tpkg/260-conversion-functions.tpkg/260-conversion-functions.c @@ -58,7 +58,7 @@ int main() size_t length; getdns_list *list; char *str; - uint8_t wire_buf[4096], *wire = wire_buf, *end_of_wire; + uint8_t *wire; size_t wire_len; @@ -81,11 +81,12 @@ int main() /* Convert to wireformat from rdata_raw. * Added fourth list element should NOT show. */ - wire_len = sizeof(wire_buf); - if ((r = getdns_rr_dict2wire(rr_dict, wire, &wire_len))) + wire = NULL; + if ((r = getdns_rr_dict2wire(rr_dict, &wire, &wire_len))) FAIL_r("getdns_rr_dict2wire"); print_wire(wire, wire_len); + free(wire); /* Convert to wireformat from parsing rdata fields. @@ -96,11 +97,11 @@ int main() printf("\nremoved \"/rdata/rdata_raw\":\n\n"); - wire_len = sizeof(wire_buf); - if ((r = getdns_rr_dict2wire(rr_dict, wire, &wire_len))) + if ((r = getdns_rr_dict2wire(rr_dict, &wire, &wire_len))) FAIL_r("getdns_rr_dict2wire"); print_wire(wire, wire_len); + free(wire); /* Remove second and third string elements and show text format. @@ -132,11 +133,6 @@ int main() getdns_dict_destroy(rr_dict); - /* Forward wireformat to test scanning from wireformat later on - */ - wire += wire_len; - wire_len = sizeof(wire_buf) - (wire - wire_buf); - /* Construct rr_dict and convert to string */ if (!(rr_dict = getdns_dict_create())) @@ -163,14 +159,12 @@ int main() printf("\n%s\n", str); free(str); - if ((r = getdns_rr_dict2wire(rr_dict, wire, &wire_len))) + if ((r = getdns_rr_dict2wire(rr_dict, &wire, &wire_len))) FAIL_r("getdns_rr_dict2wire"); getdns_dict_destroy(rr_dict); print_wire(wire, wire_len); - - wire += wire_len; - wire_len = sizeof(wire_buf) - (wire - wire_buf); + free(wire); /* Convert RR with special rdata fields and repeating last element @@ -239,10 +233,5 @@ int main() getdns_dict_destroy(rr_dict); - /* Parse over wire data, convert to string via dict, and print */ - end_of_wire = wire; - wire = wire_buf; - wire_len = end_of_wire - wire; - exit(EXIT_SUCCESS); }