diff --git a/include/asterisk/dns_internal.h b/include/asterisk/dns_internal.h index 7ce0c28f1f..3d2d0df7a7 100644 --- a/include/asterisk/dns_internal.h +++ b/include/asterisk/dns_internal.h @@ -26,6 +26,9 @@ #ifndef _ASTERISK_DNS_INTERNAL_H #define _ASTERISK_DNS_INTERNAL_H +/*! \brief For AST_LIST */ +#include "asterisk/linkedlists.h" + /*! \brief For AST_VECTOR */ #include "asterisk/vector.h" @@ -57,6 +60,16 @@ struct ast_dns_record { char data[0]; }; +/*! \brief A TXT record */ +struct ast_dns_txt_record { + /*! \brief Generic DNS record information */ + struct ast_dns_record generic; + /*! \brief The number of character strings in the TXT record */ + size_t count; + /*! \brief The raw DNS record */ + char data[0]; +}; + /*! \brief An SRV record */ struct ast_dns_srv_record { /*! \brief Generic DNS record information */ @@ -199,6 +212,19 @@ struct ast_sched_context; */ struct ast_sched_context *ast_dns_get_sched(void); +/*! + * \brief Allocate and parse a DNS TXT record + * \since 16.10.0, 17.4.0 + * + * \param query The DNS query + * \param data This specific TXT record + * \param size The size of the TXT record + * + * \retval non-NULL success + * \retval NULL failure + */ +struct ast_dns_record *dns_txt_alloc(struct ast_dns_query *query, const char *data, const size_t size); + /*! * \brief Allocate and parse a DNS NAPTR record * diff --git a/include/asterisk/dns_txt.h b/include/asterisk/dns_txt.h new file mode 100644 index 0000000000..368a861085 --- /dev/null +++ b/include/asterisk/dns_txt.h @@ -0,0 +1,64 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2020, Sean Bright + * + * Sean Bright + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file + * \brief DNS TXT Record Parsing API + * \author Sean Bright + */ + +#ifndef ASTERISK_DNS_TXT_H +#define ASTERISK_DNS_TXT_H + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +/*! + * \brief Get the number of character strings in a TXT record + * \since 16.10.0, 17.4.0 + * + * \param record The DNS record + * + * \return the number of character strings in this TXT record + */ +size_t ast_dns_txt_get_count(const struct ast_dns_record *record); + +/*! + * \brief Get the character strings from this TXT record + * \since 16.10.0, 17.4.0 + * + * \param record The DNS record + * + * \retval NULL Unable to allocate memory + * \return Vector of strings. Free with ast_dns_txt_free_strings + */ +struct ast_vector_string *ast_dns_txt_get_strings(const struct ast_dns_record *record); + +/*! + * \brief Free strings returned by ast_dns_txt_get_strings + * \since 16.10.0, 17.4.0 + * + * \param strings The vector to free + */ +void ast_dns_txt_free_strings(struct ast_vector_string *strings); + +#if defined(__cplusplus) || defined(c_plusplus) +} +#endif + +#endif /* ASTERISK_DNS_TXT_H */ diff --git a/main/dns_core.c b/main/dns_core.c index f262d7deba..0fb0731e92 100644 --- a/main/dns_core.c +++ b/main/dns_core.c @@ -516,8 +516,9 @@ static struct ast_dns_record *generic_record_alloc(struct ast_dns_query *query, typedef struct ast_dns_record *(*dns_alloc_fn)(struct ast_dns_query *query, const char *data, const size_t size); static dns_alloc_fn dns_alloc_table [] = { + [T_TXT] = dns_txt_alloc, [T_NAPTR] = dns_naptr_alloc, - [T_SRV] = dns_srv_alloc, + [T_SRV] = dns_srv_alloc, }; static struct ast_dns_record *allocate_dns_record(unsigned int rr_type, struct ast_dns_query *query, const char *data, const size_t size) diff --git a/main/dns_txt.c b/main/dns_txt.c new file mode 100644 index 0000000000..5c1c581785 --- /dev/null +++ b/main/dns_txt.c @@ -0,0 +1,126 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2020, Sean Bright + * + * Sean Bright + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file + * \brief DNS TXT Record Parsing API + * \author Sean Bright + */ + +/*** MODULEINFO + core + ***/ + +#include "asterisk.h" + +#include + +#include "asterisk/dns_core.h" +#include "asterisk/dns_txt.h" +#include "asterisk/dns_internal.h" +#include "asterisk/utils.h" + +struct ast_dns_record *dns_txt_alloc(struct ast_dns_query *query, const char *data, const size_t size) +{ + struct ast_dns_txt_record *txt; + const char *end_of_record = data + size; + size_t count = 0; + + /* Because we can't allocate additional memory, the best we can do here is just + * validate that this conforms to a TXT record. */ + while (data < end_of_record) { + uint8_t byte_count = (uint8_t) *data; + count++; + data += byte_count + 1; + } + + if (data != end_of_record) { + /* This is not a valid TXT record, so we can bail out */ + return NULL; + } + + txt = ast_calloc(1, sizeof(*txt) + size); + if (!txt) { + return NULL; + } + + txt->count = count; + txt->generic.data_ptr = txt->data; + + return (struct ast_dns_record *) txt; +} + +size_t ast_dns_txt_get_count(const struct ast_dns_record *record) +{ + struct ast_dns_txt_record *txt = (struct ast_dns_txt_record *) record; + ast_assert(ast_dns_record_get_rr_type(record) == T_TXT); + return txt->count; +} + +struct ast_vector_string *ast_dns_txt_get_strings(const struct ast_dns_record *record) +{ + struct ast_vector_string *strings; + + const size_t size = ast_dns_record_get_data_size(record); + const char *data = ast_dns_record_get_data(record); + const char *end_of_record = data + size; + + ast_assert(ast_dns_record_get_rr_type(record) == T_TXT); + + strings = ast_malloc(sizeof(struct ast_vector_const_string)); + if (!strings) { + return NULL; + } + + if (AST_VECTOR_INIT(strings, ast_dns_txt_get_count(record))) { + ast_free(strings); + return NULL; + } + + while (data < end_of_record) { + char *s; + uint8_t bytes = (uint8_t) *data; + + s = ast_malloc(bytes + 1); + if (!s) { + ast_dns_txt_free_strings(strings); + return NULL; + } + + memcpy(s, &data[1], bytes); + s[bytes] = 0; + + /* We know the size in advance so this can't fail */ + AST_VECTOR_APPEND(strings, s); + + data += bytes + 1; + } + + /* Sanity check */ + if (data != end_of_record) { + ast_dns_txt_free_strings(strings); + return NULL; + } + + return strings; +} + +void ast_dns_txt_free_strings(struct ast_vector_string *strings) +{ + AST_VECTOR_CALLBACK_VOID(strings, ast_free); + AST_VECTOR_PTR_FREE(strings); +}