diff --git a/CuteDNS.files b/CuteDNS.files index a09c50bc5a56b7bf38ae3f6baca7189d68c31d4c..c1a6b4428ada4bd5fd0c28d0e8b113fbf030feff 100644 --- a/CuteDNS.files +++ b/CuteDNS.files @@ -66,3 +66,5 @@ src/dnslib/consts.h src/tests/dnslib/dnslib_dname_tests.c src/tests/dnslib/dnslib_rdata_tests.c src/tests/dnslib/dnslib_rrset_tests.c +src/dnslib/descriptor.h +src/dnslib/descriptor.c diff --git a/src/dnslib/descriptor.c b/src/dnslib/descriptor.c index 070bc284117d7017f8caaaf524b91c7ebd7dfc11..91e2cd16aa6323cbfa60d5dc91f7e75a9ca7ccea 100644 --- a/src/dnslib/descriptor.c +++ b/src/dnslib/descriptor.c @@ -61,7 +61,7 @@ static dnslib_rrtype_descriptor_t dnslib_rrtype_descriptors[DNSLIB_RRTYPE_DESCRI { DNSLIB_RDATA_WF_BINARY }, true }, /* 11 */ { DNSLIB_RRTYPE_WKS, "WKS", 2, - { DNSLIB_RDATA_WF_A, DNSLIB_RDATA_WF_BINARY }, true }, + { DNSLIB_RDATA_WF_A, DNSLIB_RDATA_WF_BINARY }, true }, /* 12 */ { DNSLIB_RRTYPE_PTR, "PTR", 1, { DNSLIB_RDATA_WF_COMPRESSED_DNAME }, true }, @@ -75,7 +75,7 @@ static dnslib_rrtype_descriptor_t dnslib_rrtype_descriptors[DNSLIB_RRTYPE_DESCRI { DNSLIB_RRTYPE_MX, "MX", 2, { DNSLIB_RDATA_WF_SHORT, DNSLIB_RDATA_WF_COMPRESSED_DNAME }, true }, /* 16 */ /* This is obscure, but I guess there's no other way */ - { DNSLIB_RRTYPE_TXT, "TXT", MAXRDATALEN, + { DNSLIB_RRTYPE_TXT, "TXT", MAX_RDATA_ITEMS, { DNSLIB_RDATA_WF_TEXT, DNSLIB_RDATA_WF_TEXT, DNSLIB_RDATA_WF_TEXT, DNSLIB_RDATA_WF_TEXT, DNSLIB_RDATA_WF_TEXT, DNSLIB_RDATA_WF_TEXT, DNSLIB_RDATA_WF_TEXT, DNSLIB_RDATA_WF_TEXT, DNSLIB_RDATA_WF_TEXT, DNSLIB_RDATA_WF_TEXT, DNSLIB_RDATA_WF_TEXT, DNSLIB_RDATA_WF_TEXT, @@ -166,7 +166,7 @@ static dnslib_rrtype_descriptor_t dnslib_rrtype_descriptors[DNSLIB_RRTYPE_DESCRI { DNSLIB_RRTYPE_OPT, "OPT", 1, { DNSLIB_RDATA_WF_BINARY }, true }, /* 42 */ - { DNSLIB_RRTYPE_APL, "APL", MAXRDATALEN, + { DNSLIB_RRTYPE_APL, "APL", MAX_RDATA_ITEMS, { DNSLIB_RDATA_WF_APL, DNSLIB_RDATA_WF_APL, DNSLIB_RDATA_WF_APL, DNSLIB_RDATA_WF_APL, DNSLIB_RDATA_WF_APL, DNSLIB_RDATA_WF_APL, DNSLIB_RDATA_WF_APL, DNSLIB_RDATA_WF_APL, DNSLIB_RDATA_WF_APL, DNSLIB_RDATA_WF_APL, DNSLIB_RDATA_WF_APL, DNSLIB_RDATA_WF_APL, @@ -229,7 +229,7 @@ static dnslib_rrtype_descriptor_t dnslib_rrtype_descriptors[DNSLIB_RRTYPE_DESCRI /* There's a GNU extension that works like this: [first ... last] = value */ /* 99 */ - [99] = { DNSLIB_RRTYPE_SPF, "SPF", MAXRDATALEN, + [99] = { DNSLIB_RRTYPE_SPF, "SPF", MAX_RDATA_ITEMS, { DNSLIB_RDATA_WF_TEXT, DNSLIB_RDATA_WF_TEXT, DNSLIB_RDATA_WF_TEXT, DNSLIB_RDATA_WF_TEXT, DNSLIB_RDATA_WF_TEXT, DNSLIB_RDATA_WF_TEXT, DNSLIB_RDATA_WF_TEXT, DNSLIB_RDATA_WF_TEXT, DNSLIB_RDATA_WF_TEXT, DNSLIB_RDATA_WF_TEXT, DNSLIB_RDATA_WF_TEXT, DNSLIB_RDATA_WF_TEXT, diff --git a/src/dnslib/descriptor.h b/src/dnslib/descriptor.h index 95b00350f4dfcda17e9ed3779b13ad683f6e7511..465a9a3592e65b1f6b4825e7fb849495ecff123b 100644 --- a/src/dnslib/descriptor.h +++ b/src/dnslib/descriptor.h @@ -15,7 +15,12 @@ #include "common.h" -enum mxrdtln { MAXRDATALEN = 64 }; +enum mxrdtln +{ + MAX_RDATA_ITEMS = 64, + MAX_RDATA_ITEM_SIZE = 255, + MAX_RDATA_WIRE_SIZE = MAX_RDATA_ITEMS * MAX_RDATA_ITEM_SIZE +}; //#define MAXRDATALEN 64 /* 64 is in NSD. Seems a little too much, but I'd say it's not a real issue. */ @@ -154,7 +159,7 @@ struct dnslib_rrtype_descriptor uint16_t type; /* RR type */ const char *name; /* Textual name. */ uint8_t length; /* Maximum number of RDATA items. */ - uint8_t wireformat[MAXRDATALEN]; /* rdata_wireformat_type */ + uint8_t wireformat[MAX_RDATA_ITEMS]; /* rdata_wireformat_type */ bool fixed_items; /* Has fixed number of RDATA items? */ }; diff --git a/src/dnslib/rdata.c b/src/dnslib/rdata.c index bea66afc860a75b5523d0ebd93ae8727b7106b38..51ac0b1cdc96aa0352368b6e8868321d8265510d 100644 --- a/src/dnslib/rdata.c +++ b/src/dnslib/rdata.c @@ -4,12 +4,55 @@ #include <string.h> #include "common.h" #include "rdata.h" +#include "descriptor.h" +#include "dname.h" + +/*----------------------------------------------------------------------------*/ +/* Non-API functions */ +/*----------------------------------------------------------------------------*/ + +static int dnslib_rdata_compare_binary( const uint8_t *d1, const uint8_t *d2, + int count1, int count2 ) +{ + int i1 = 0, i2 = 0; + + // length stored in the first octet + if (count1 < 0) { + // take count from the first byte + count1 = (int)d1[0]; + // and start from the second byte + i1 = 1; + } + if (count2 < 0) { // dtto + count2 = (int)d2[0]; + i2 = 1; + } + + + while (i1 < count1 && i2 < count2 && d1[i1] == d2[i2]) { + ++i1; + ++i2; + } + + if (i1 == count1 && i2 == count2) { + return 0; + } + + if (i1 == count1 && i2 < count2) { + return -1; + } else if (i2 == count2 && i1 < count1) { + return 1; + } else { + assert(i1 < count1 && i2 < count2); + return (d1[i1] < d2[i2]) ? -1 : 1; + } +} /*----------------------------------------------------------------------------*/ /* API functions */ /*----------------------------------------------------------------------------*/ -dnslib_rdata_t *dnslib_rdata_new( uint count ) +dnslib_rdata_t *dnslib_rdata_new() { dnslib_rdata_t *rdata = (dnslib_rdata_t *)malloc(sizeof(dnslib_rdata_t)); if (rdata == NULL) { @@ -18,15 +61,7 @@ dnslib_rdata_t *dnslib_rdata_new( uint count ) } rdata->items = NULL; - - if (count > 0 && (rdata->items = (dnslib_rdata_item_t *)calloc( - count, sizeof(dnslib_rdata_item_t))) == NULL) { - ERR_ALLOC_FAILED; - free(rdata); - return NULL; - } - - rdata->count = count; + rdata->count = 0; return rdata; } @@ -52,14 +87,14 @@ int dnslib_rdata_set_items( dnslib_rdata_t *rdata, return 1; } - if (rdata->count == 0) { // empty so far, allocate new space - assert(rdata->items == NULL); - if ((rdata->items = (dnslib_rdata_item_t *)malloc( - count * sizeof(dnslib_rdata_item_t))) == NULL) { - ERR_ALLOC_FAILED; - return -1; - } - } else if (rdata->count != count) { + if (rdata->items != NULL) { // not empty + return -1; + } + + assert(rdata->count == 0); + if ((rdata->items = (dnslib_rdata_item_t *)malloc( + count * sizeof(dnslib_rdata_item_t))) == NULL) { + ERR_ALLOC_FAILED; return -2; } @@ -92,3 +127,209 @@ void dnslib_rdata_free( dnslib_rdata_t **rdata ) free(*rdata); *rdata = NULL; } + +/*----------------------------------------------------------------------------*/ + +uint dnslib_rdata_wire_size( const dnslib_rdata_t *rdata, + const uint8_t *format ) +{ + uint size = 0; + + for (int i = 0; i < rdata->count; ++i) { + switch (format[i]) { + case DNSLIB_RDATA_WF_COMPRESSED_DNAME: + case DNSLIB_RDATA_WF_UNCOMPRESSED_DNAME: + case DNSLIB_RDATA_WF_LITERAL_DNAME: + size += dnslib_dname_size(rdata->items[i].dname); + break; + case DNSLIB_RDATA_WF_BYTE: + size += 1; + break; + case DNSLIB_RDATA_WF_SHORT: + size += 2; + break; + case DNSLIB_RDATA_WF_LONG: + size += 4; + break; + case DNSLIB_RDATA_WF_A: + size += 4; + break; + case DNSLIB_RDATA_WF_AAAA: + size += 16; + break; + case DNSLIB_RDATA_WF_TEXT: + case DNSLIB_RDATA_WF_BINARYWITHLENGTH: + // size stored in the first byte, but the first byte also counts + size += rdata->items[i].raw_data[0] + 1; + break; + case DNSLIB_RDATA_WF_BINARY: + case DNSLIB_RDATA_WF_APL: // saved as binary + case DNSLIB_RDATA_WF_IPSECGATEWAY: // saved as binary + // size stored in the first byte, first byte doesn't count + size += rdata->items[i].raw_data[0]; + break; + default: + assert(0); + } + } + return size; +} + +/*----------------------------------------------------------------------------*/ + +int dnslib_rdata_to_wire( const dnslib_rdata_t *rdata, const uint8_t *format, + uint8_t *buffer, uint buf_size ) +{ + uint copied = 0; + uint8_t tmp[MAX_RDATA_WIRE_SIZE]; + uint8_t *to = tmp; + + for (int i = 0; i < rdata->count; ++i) { + assert(copied < MAX_RDATA_WIRE_SIZE); + + switch (format[i]) { + case DNSLIB_RDATA_WF_COMPRESSED_DNAME: + case DNSLIB_RDATA_WF_UNCOMPRESSED_DNAME: + case DNSLIB_RDATA_WF_LITERAL_DNAME: { + uint size = dnslib_dname_size(rdata->items[i].dname); + memcpy(to, dnslib_dname_name(rdata->items[i].dname), size); + to += size; + copied += size; + } break; + case DNSLIB_RDATA_WF_BYTE: + *(to++) = rdata->items[i].int8; + ++copied; + break; + case DNSLIB_RDATA_WF_SHORT: { + const uint8_t *from = (uint8_t *)(&rdata->items[i].int16); + // copy from last byte to first (little to big endian) + // TODO: check endianness of the machine + from += 1; + for (int i = 0; i < 2; ++i) { + *(to++) = *(from--); + ++copied; + } + } break; + case DNSLIB_RDATA_WF_LONG: { + const uint8_t *from = (uint8_t *)(&rdata->items[i].int32); + // copy from last byte to first (little to big endian) + // TODO: check endianness of the machine + from += 3; + for (int i = 0; i < 4; ++i) { + *(to++) = *(from--); + ++copied; + } + } break; + case DNSLIB_RDATA_WF_A: { + const uint8_t *from = rdata->items[i].a; + for (int i = 0; i < 4; ++i) { + *(to++) = *(from++); + ++copied; + } + } break; + case DNSLIB_RDATA_WF_AAAA: { + const uint8_t *from = rdata->items[i].raw_data; + for (int i = 0; i < 16; ++i) { + *(to++) = *(from++); + ++copied; + } + } break; + case DNSLIB_RDATA_WF_TEXT: + case DNSLIB_RDATA_WF_BINARYWITHLENGTH: + // size stored in the first byte, but the first byte also needs to + // be copied + memcpy(to, rdata->items[i].raw_data, + rdata->items[i].raw_data[0] + 1); + copied += rdata->items[i].raw_data[0] + 1; + to += rdata->items[i].raw_data[0] + 1; + break; + case DNSLIB_RDATA_WF_BINARY: + case DNSLIB_RDATA_WF_APL: // saved as binary + case DNSLIB_RDATA_WF_IPSECGATEWAY: // saved as binary + // size stored in the first byte, first byte must not be copied + memcpy(to, &(rdata->items[i].raw_data[1]), + rdata->items[i].raw_data[0]); + copied += rdata->items[i].raw_data[0]; + to += rdata->items[i].raw_data[0]; + break; + default: + assert(0); + } + } + + if (copied > buf_size) { + log_warning("Not enough place allocated for function " + "dnslib_rdata_to_wire(). Allocated %u, need %u\n", + buf_size, copied); + return -1; + } + + memcpy(buffer, tmp, copied); + return 0; +} + +/*----------------------------------------------------------------------------*/ + +int dnslib_rdata_compare( const dnslib_rdata_t *r1, const dnslib_rdata_t *r2, + const uint8_t *format ) +{ + uint count = (r1->count < r2->count) ? r1->count : r2->count; + + int cmp = 0; + + for (int i = 0; i < count; ++i) { + dnslib_rdata_item_t *item1 = &r2->items[i]; + dnslib_rdata_item_t *item2 = &r2->items[i]; + + switch (format[i]) { + case DNSLIB_RDATA_WF_COMPRESSED_DNAME: + case DNSLIB_RDATA_WF_UNCOMPRESSED_DNAME: + case DNSLIB_RDATA_WF_LITERAL_DNAME: + cmp = dnslib_rdata_compare_binary( + dnslib_dname_name(item1->dname), + dnslib_dname_name(item2->dname), + dnslib_dname_size(item1->dname), + dnslib_dname_size(item2->dname)); + break; + case DNSLIB_RDATA_WF_BYTE: + cmp = (item1->int8 == item2->int8) ? 0 : ( + (r1->items[i].int8 < item2->int8) ? -1 : 1); + break; + case DNSLIB_RDATA_WF_SHORT: + cmp = (item1->int16 == item2->int16) ? 0 : ( + (item1->int16 < item2->int16) ? -1 : 1); + break; + case DNSLIB_RDATA_WF_LONG: + cmp = (item1->int32 == item2->int32) ? 0 : ( + (item1->int32 < item2->int32) ? -1 : 1); + break; + case DNSLIB_RDATA_WF_A: + cmp = dnslib_rdata_compare_binary(item1->a, item2->a, 4, 4); + break; + case DNSLIB_RDATA_WF_AAAA: + cmp = dnslib_rdata_compare_binary(item1->raw_data, item2->raw_data, + 16, 16); + break; + case DNSLIB_RDATA_WF_TEXT: + case DNSLIB_RDATA_WF_BINARYWITHLENGTH: + cmp = dnslib_rdata_compare_binary(&item1->raw_data[1], + &item2->raw_data[1], (int)item1->raw_data[0], + (int)item2->raw_data[0]); + break; + case DNSLIB_RDATA_WF_BINARY: + case DNSLIB_RDATA_WF_APL: // saved as binary + case DNSLIB_RDATA_WF_IPSECGATEWAY: // saved as binary + cmp = dnslib_rdata_compare_binary(item1->a, item2->a, -1, -1); + break; + default: + assert(0); + } + + if (cmp != 0) { + return cmp; + } + } + + assert(cmp == 0); + return 0; +} diff --git a/src/dnslib/rdata.h b/src/dnslib/rdata.h index 21cc0e553e3c2e5335f532d72bdaaf61831ee55f..4d7ad9744e99a349fd514bd16a7f4a4b21c53622 100644 --- a/src/dnslib/rdata.h +++ b/src/dnslib/rdata.h @@ -26,12 +26,19 @@ * require special treatment within some RDATA (e.g. compressing in packets). */ union dnslib_rdata_item { + dnslib_dname_t *dname; /*!< RDATA item represented as a domain name. */ + uint8_t int8; /*!< 8bit unsigned integer */ + uint16_t int16; /*!< 16bit unsigned integer */ + uint32_t int32; /*!< 32bit unsigned integer */ + uint8_t a[4]; /*!< A address - four 8bit integers */ /*! * \brief RDATA item represented as raw array of octets. - * \note Will this be convenient enough? What about parsing? (in runtime?) + * + * In most cases the first octet will be the length of the array. The only + * now known exception will be when storing AAAA address which has known + * length (sixteen 8bit integers), so there is no need to store the lenght. */ uint8_t *raw_data; - dnslib_dname_t *dname; /*!< RDATA item represented as a domain name. */ }; typedef union dnslib_rdata_item dnslib_rdata_item_t; @@ -43,11 +50,19 @@ typedef union dnslib_rdata_item dnslib_rdata_item_t; * Each RDATA may be logically divided into items, each of possible different * type (see dnslib_rdata_item). This structure stores an array of such items. * It is not dynamic, so any RDATA structure may hold either 0 or one specified - * number of items which cannot be changed later. + * number of items which cannot be changed later. However, the number of items + * may be different for each RDATA structure. The number of items should be + * given by descriptors for each RR type. Some types may have variable number + * of items. In such cases, the last item in the array will be set tu NULL + * to distinguish the actual count of items. + * + * \todo Find out whether NULL is appropriate value. If it is a possible + * value for some of the items, we must find some other way to deal with + * this. * * This structure does not hold information about the RDATA items, such as * what type is which item or how long are they. This information should be - * stored elsewhere (probably globally) as it is RR-specific and given for each + * stored elsewhere (in descriptors) as it is RR-specific and given for each * RR type. */ struct dnslib_rdata { @@ -60,15 +75,11 @@ typedef struct dnslib_rdata dnslib_rdata_t; /*----------------------------------------------------------------------------*/ /*! - * \brief Creates an empty RDATA structure and possibly reserves space for data. - * - * \param count Number of RDATA items to be later stored in this structure. - * - * If \a count > 0, all RDATA items are initialized to 0. + * \brief Creates an empty RDATA structure. * * \return Pointer to the new RDATA structure or NULL if an error occured. */ -dnslib_rdata_t *dnslib_rdata_new( uint count ); +dnslib_rdata_t *dnslib_rdata_new(); /*! * \brief Sets the RDATA item on position \a pos. @@ -122,6 +133,36 @@ int dnslib_rdata_set_items( dnslib_rdata_t *rdata, const dnslib_rdata_item_t *dnslib_rdata_get_item( const dnslib_rdata_t *rdata, uint pos ); +/*! + * \brief Returns the size of the RDATA in wire format. + * + * \param rdata RDATA structure to get the wire format size of. + * \param format RDATA format descriptor. + * + * \return Size of the RDATA in wire format. + * + * \todo Consider adding the size to the structure for faster retrieval. + */ +uint dnslib_rdata_wire_size( const dnslib_rdata_t *rdata, + const uint8_t *format ); + +/*! + * \brief Converts the RDATA to wire format. + * + * \param rdata RDATA structure to convert to wire format. + * \param format RDATA format descriptor. + * \param buffer Place to put the wire format into. + * \param buf_size Size of the buffer. + * + * \return 0 on success. + * \return <> 0 otherwise. + * + * \todo Do we really need all those different kinds of RDATA items? It makes + * converting more complicated. + */ +int dnslib_rdata_to_wire( const dnslib_rdata_t *rdata, const uint8_t *format, + uint8_t *buffer, uint buf_size ); + /*! * \brief Destroys the RDATA structure. * @@ -136,6 +177,12 @@ const dnslib_rdata_item_t *dnslib_rdata_get_item( const dnslib_rdata_t *rdata, */ void dnslib_rdata_free( dnslib_rdata_t **rdata ); +/*! + * + */ +int dnslib_rdata_compare( const dnslib_rdata_t *r1, const dnslib_rdata_t *r2, + const uint8_t *format ); + #endif /* _CUTEDNS_RDATA_H */ /*! @} */