diff options
Diffstat (limited to 'src/resolv.c')
-rw-r--r-- | src/resolv.c | 1432 |
1 files changed, 1432 insertions, 0 deletions
diff --git a/src/resolv.c b/src/resolv.c new file mode 100644 index 0000000..aa18f76 --- /dev/null +++ b/src/resolv.c @@ -0,0 +1,1432 @@ +#include "resolv.h" +#include "util_rt.h" +#include "util_ct.h" +#include "llist.h" +#include "imap.h" +#include "iset.h" +#include "protocol.h" +#include "mbedtls.h" + +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <time.h> + +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <sys/poll.h> + +#include <pthsem.h> +#include <mbedtls/ssl.h> +#include <mbedtls/ctr_drbg.h> + +_Static_assert(sizeof(uint_fast16_t) <= sizeof(prne_imap_key_type_t), "prne_imap cannot contain uint_fast16_t"); + +#define OK_OR_ERR(v) if (v < 0) { goto ERR; } + +typedef enum { + RESOLV_CTX_STATE_OK, + RESOLV_CTX_STATE_FIN_CALLED, + RESOLV_CTX_STATE_FINALISED, +} resolv_ctx_state_t; + +typedef struct { + prne_net_endpoint_t *arr; + size_t cnt; + bool ownership; +} resolv_dnssrv_pool_t; + +typedef struct { + prne_resolv_t *owner; + prne_llist_entry_t *qlist_ent; + char *qname; + size_t qname_size; + prne_pth_cv_t *cv; + uint_fast16_t qid; // 0 reserved + prne_resolv_fut_t fut; + prne_ipv_t ipv; + prne_resolv_query_type_t type; + struct timespec tp_queued; +} query_entry_t; + +struct prne_resolv { + size_t read_cnt_len; + size_t write_cnt_len; + struct pollfd act_sck_pfd; + size_t ptr_dnssrv4, ptr_dnssrv6; + resolv_dnssrv_pool_t dnssrv_4, dnssrv_6; + pth_mutex_t lock; + pth_cond_t cond; + resolv_ctx_state_t ctx_state; + uint8_t write_buf[514]; + uint8_t read_buf[514]; + prne_llist_t qlist; + prne_imap_t qid_map; // uint16_t:q_ent(could be null) + struct { + mbedtls_ssl_config conf; + mbedtls_ssl_context ctx; + mbedtls_ctr_drbg_context *ctr_drbg; + } ssl; +}; + +#define DECL_CTX_PTR(p) prne_resolv_t *ctx = (prne_resolv_t*)p + +static const struct timespec RESOLV_RSRC_ERR_PAUSE = { 1, 0 }; // 1s +static const struct timespec RESOLV_CONN_ERR_PAUSE = { 0, 100 }; // 100ms +static const struct timespec RESOLV_QUERY_TIMEOUT = { 15, 0 }; // 15s +static const struct timespec RESOLV_SCK_OP_TIMEOUT = { 10, 0 }; // 10s +static const struct timespec RESOLV_SCK_IDLE_TIMEOUT = { 15, 0 }; // 15s +static const struct timespec RESOLV_SCK_CLOSE_TIMEOUT = { 1, 0 }; // 1s +static const size_t RESOLV_PIPELINE_SIZE = 4; + +static prne_net_endpoint_t DEF_IPV4_EP[] = { + // Google + { { { 0x8, 0x8, 0x8, 0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }, PRNE_IPV_4 }, 853 }, + { { { 0x8, 0x8, 0x4, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }, PRNE_IPV_4 }, 853 }, + // Cloudflare + { { { 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }, PRNE_IPV_4 }, 853 }, + { { { 0x1, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }, PRNE_IPV_4 }, 853 }, + // Quad9 + { { { 0x9, 0x9, 0x9, 0xa, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }, PRNE_IPV_4 }, 853 }, + { { { 0x95, 0x70, 0x70, 0xa, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }, PRNE_IPV_4 }, 853 }, + // CleanBrowsing + { { { 0xb9, 0xe4, 0xa8, 0x9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }, PRNE_IPV_4 }, 853 }, + { { { 0xb9, 0xe4, 0xa9, 0x9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }, PRNE_IPV_4 }, 853 } +}; + +static resolv_dnssrv_pool_t RESOLV_DEF_IPV4_POOL = { + DEF_IPV4_EP, + sizeof(DEF_IPV4_EP)/sizeof(prne_net_endpoint_t), + false +}; + +static prne_net_endpoint_t DEF_IPV6_EP[] = { + // Google + { { { 0x20, 0x1, 0x48, 0x60, 0x48, 0x60, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x88, 0x88 }, PRNE_IPV_6 }, 853 }, + { { { 0x20, 0x1, 0x48, 0x60, 0x48, 0x60, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x88, 0x44 }, PRNE_IPV_6 }, 853 }, + // Cloudflare + { { { 0x26, 0x6, 0x47, 0x0, 0x47, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x11, 0x11 }, PRNE_IPV_6 }, 853 }, + { { { 0x26, 0x6, 0x47, 0x0, 0x47, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x1 }, PRNE_IPV_6 }, 853 }, + // Quad9 + { { { 0x26, 0x20, 0x0, 0xfe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xfe }, PRNE_IPV_6 }, 853 }, + { { { 0x26, 0x20, 0x0, 0xfe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9 }, PRNE_IPV_6 }, 853 }, + // CleanBrowsing + { { { 0x2a, 0xd, 0x2a, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2 }, PRNE_IPV_6 }, 853 }, + { { { 0x2a, 0xd, 0x2a, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2 }, PRNE_IPV_6 }, 853 } +}; + +static resolv_dnssrv_pool_t RESOLV_DEF_IPV6_POOL = { + DEF_IPV6_EP, + sizeof(DEF_IPV6_EP)/sizeof(prne_net_endpoint_t), + false +}; + +static int resolv_set_cmn_fd_opt (const int fd) { + // TODO: no FD_CLOEXEC + return fcntl(fd, F_SETFL, O_NONBLOCK) == 0 ? fcntl(fd, F_SETFD, FD_CLOEXEC) : -1; +} + +static void resolv_free_q_ent (query_entry_t *q_ent) { + if (q_ent == NULL) { + return; + } + + prne_free(q_ent->qname); + prne_free_resolv_fut(&q_ent->fut); + + prne_free(q_ent); +} + +static bool resolv_gen_qname (const char *name, char **out, size_t *out_size) { + size_t len = prne_nstrlen(name); + char *ptr = (char*)name, *delim; + char *end = ptr + len; + size_t label_size; + char *ret_ptr; + size_t ret_size; + + if (len == 0) { + errno = EINVAL; + return false; + } + if (name[len - 1] != '.') { + len += 1; + } + if (len > 255) { + errno = EINVAL; + return false; + } + for (; *name != 0; name += 1) { + if (((uint_fast8_t)*name & 0xC0) == 0xC0) { + errno = EINVAL; + return false; + } + } + + ret_ptr = prne_alloc_str(len); + if (ret_ptr == NULL) { + return false; + } + + ret_size = 0; + while (ptr < end) { + delim = strchr(ptr, '.'); + if (delim == NULL) { + delim = strchr(ptr, 0); + } + + label_size = delim - ptr; + if (label_size == 0 || label_size > 63) { + errno = EINVAL; + goto ERR; + } + + ret_ptr[ret_size] = (uint8_t)label_size; + memcpy(ret_ptr + ret_size + 1, ptr, label_size); + ret_size += 1 + label_size; + ptr = delim + 1; + } + ret_ptr[ret_size] = 0; + ret_size += 1; + + *out = prne_realloc(ret_ptr, 1, ret_size); + if (*out == NULL) { + *out = ret_ptr; + } + *out_size = ret_size; + + return true; +ERR: + prne_free(ret_ptr); + return false; +} + +char *resolv_qname_tostr (const char *qname) { + char *ret, *p, *end; + const size_t qname_size = strlen(qname) + 1; + size_t label_len; + + ret = p = (char*)prne_malloc(1, qname_size); + if (p == NULL) { + return NULL; + } + memcpy(p, qname, qname_size); + end = p + qname_size; + + while (p < end) { + label_len = *p; + if (label_len == 0) { + break; + } + else if (p + label_len > end) { + goto ERR; + } + + memmove(p, p + 1, label_len); + p[label_len] = '.'; + p += label_len + 1; + } + + return ret; +ERR: + prne_free(ret); + return NULL; +} + +static bool resolv_qq (prne_resolv_t *ctx, const char *name, prne_pth_cv_t *cv, prne_resolv_prm_t *out, query_entry_t **ny_q_ent) { + query_entry_t *q_ent = NULL; + + if (ctx->ctx_state != RESOLV_CTX_STATE_OK) { + errno = ECANCELED; + return false; + } + + q_ent = (query_entry_t*)prne_malloc(sizeof(query_entry_t), 1); + if (q_ent == NULL) { + goto ERR; + } + q_ent->owner = ctx; + q_ent->qlist_ent = NULL; + q_ent->qname = NULL; + q_ent->qname_size = 0; + q_ent->cv = cv; + q_ent->qid = 0; + q_ent->ipv = PRNE_IPV_NONE; + prne_init_resolv_fut(&q_ent->fut); + + if (!resolv_gen_qname(name, &q_ent->qname, &q_ent->qname_size)) { + goto ERR; + } + + prne_assert(pth_mutex_acquire(&ctx->lock, FALSE, NULL)); + q_ent->qlist_ent = prne_llist_append(&ctx->qlist, q_ent); + if (q_ent->qlist_ent == NULL) { + goto ERR; + } + q_ent->tp_queued = prne_gettime(CLOCK_MONOTONIC); + pth_cond_notify(&ctx->cond, FALSE); + prne_assert(pth_mutex_release(&ctx->lock)); + + out->ctx = q_ent; + out->fut = &q_ent->fut; + *ny_q_ent = q_ent; + + return true; +ERR: + if (q_ent != NULL) { + prne_llist_erase(&ctx->qlist, q_ent->qlist_ent); + prne_free(q_ent->qname); + + prne_free(q_ent); + } + + return false; +} + +static void resolv_disown_qent (query_entry_t *qent) { + qent->owner = NULL; + qent->qlist_ent = NULL; + qent->qid = 0; + if (qent->cv != NULL) { + prne_pth_cv_notify(qent->cv); + } +} + +static size_t resolv_next_pool_ptr (prne_resolv_t *ctx, const size_t cnt) { + size_t ret = 0; + + prne_assert(mbedtls_ctr_drbg_random(ctx->ssl.ctr_drbg, (unsigned char*)&ret, sizeof(size_t)) == 0); + + return ret % cnt; +} + +static uint16_t resolv_next_qid (prne_resolv_t *ctx) { + uint16_t ret; + + for (uint_fast16_t i = 0; i < UINT16_MAX; i += 1) { + prne_assert(mbedtls_ctr_drbg_random(ctx->ssl.ctr_drbg, (unsigned char*)&ret, sizeof(uint16_t)) == 0); + + ret = (ret % UINT16_MAX) + 1; + if (prne_imap_lookup(&ctx->qid_map, ret) == NULL) { + return ret; + } + } + + return 0; +} + +static void resolv_close_sck (prne_resolv_t *ctx, const struct timespec *pause, bool change_srvr) { // TODO: take errno as param + size_t i; + query_entry_t *qent; + prne_llist_entry_t *lent; + + // ctx->qid_map -> ctx->qlist + for (i = 0; i < ctx->qid_map.size; i += 1) { + qent = (query_entry_t*)ctx->qid_map.tbl[i].val; + if (qent == NULL) { + continue; + } + + lent = prne_llist_append(&ctx->qlist, qent); + if (lent == NULL) { + qent->fut.qr = PRNE_RESOLV_QR_ERR; + qent->fut.err = errno; + resolv_disown_qent(qent); + } + else { + qent->qid = 0; + qent->qlist_ent = lent; + } + } + prne_imap_clear(&ctx->qid_map); + + prne_shutdown(ctx->act_sck_pfd.fd, SHUT_RDWR); + prne_close(ctx->act_sck_pfd.fd); + ctx->act_sck_pfd.fd = -1; + ctx->read_cnt_len = 0; + ctx->write_cnt_len = 0; + mbedtls_ssl_free(&ctx->ssl.ctx); + mbedtls_ssl_init(&ctx->ssl.ctx); + + if (pause != NULL) { + prne_unint_pth_nanosleep(*pause); + } + if (change_srvr) { + ctx->ptr_dnssrv4 = resolv_next_pool_ptr(ctx, ctx->dnssrv_4.cnt); + ctx->ptr_dnssrv6 = resolv_next_pool_ptr(ctx, ctx->dnssrv_6.cnt); + } +} + +static bool resolv_ensure_act_dns_fd (prne_resolv_t *ctx) { + static const int ov_nodelay = 1; + static socklen_t optval_len = sizeof(int); + size_t i; + struct pollfd pfs[2]; + int optval, pollret; + const struct timespec *err_sleep = NULL; + bool ret = false; + + pfs[0].fd = socket(AF_INET6, SOCK_STREAM, 0); + pfs[1].fd = socket(AF_INET, SOCK_STREAM, 0); + pfs[0].events = POLLOUT; + pfs[1].events = POLLOUT; + if (pfs[0].fd >= 0) { + setsockopt(pfs[0].fd, SOL_TCP, TCP_NODELAY, &ov_nodelay, sizeof(int)); + if (resolv_set_cmn_fd_opt(pfs[0].fd) < 0) { + prne_close(pfs[0].fd); + pfs[0].fd = -1; + } + else { + struct sockaddr_in6 addr; + + memzero(&addr, sizeof(addr)); + prne_net_ep_tosin6(ctx->dnssrv_6.arr + ctx->ptr_dnssrv4, &addr); + if (connect(pfs[0].fd, (const struct sockaddr*)&addr, sizeof(addr)) < 0 && errno != EINPROGRESS) { + prne_close(pfs[0].fd); + pfs[0].fd = -1; + } + } + } + if (pfs[1].fd >= 0) { + setsockopt(pfs[1].fd, SOL_TCP, TCP_NODELAY, &ov_nodelay, sizeof(int)); + if (resolv_set_cmn_fd_opt(pfs[1].fd) < 0) { + prne_close(pfs[1].fd); + pfs[1].fd = -1; + } + else { + struct sockaddr_in addr; + + memzero(&addr, sizeof(addr)); + prne_net_ep_tosin4(ctx->dnssrv_4.arr + ctx->ptr_dnssrv6, &addr); + if (connect(pfs[1].fd, (const struct sockaddr*)&addr, sizeof(addr)) < 0 && errno != EINPROGRESS) { + prne_close(pfs[1].fd); + pfs[1].fd = -1; + } + } + } + + /* + * Assume that socket creation failed because there's no resource. + * There could be other reasons like no socket() syscall in kernel + * or no IPv4 support. + */ + if (pfs[0].fd < 0 && pfs[1].fd < 0) { + err_sleep = &RESOLV_RSRC_ERR_PAUSE; + goto END; + } + + err_sleep = &RESOLV_CONN_ERR_PAUSE; + while (pfs[0].fd >= 0 || pfs[1].fd >= 0) { + pollret = prne_unint_pth_poll(pfs, 2, &RESOLV_SCK_OP_TIMEOUT); + if (pollret > 0) { + for (i = 0; i < 2; i += 1) { + if (pfs[i].revents & (POLLHUP | POLLERR | POLLNVAL)) { + prne_close(pfs[i].fd); + pfs[i].fd = -1; + } + else if (pfs[i].revents & POLLOUT) { + if (getsockopt(pfs[i].fd, SOL_SOCKET, SO_ERROR, &optval, &optval_len) < 0 || optval != 0) { + prne_close(pfs[i].fd); + pfs[i].fd = -1; + } + else { + if (mbedtls_ssl_setup(&ctx->ssl.ctx, &ctx->ssl.conf) != 0 || mbedtls_ssl_set_hostname(&ctx->ssl.ctx, NULL) != 0) { + err_sleep = &RESOLV_RSRC_ERR_PAUSE; + goto END; + } + ctx->act_sck_pfd.fd = pfs[i].fd; + pfs[i].fd = -1; + + mbedtls_ssl_set_bio(&ctx->ssl.ctx, &ctx->act_sck_pfd.fd, prne_mbedtls_ssl_send_cb, prne_mbedtls_ssl_recv_cb, NULL); + ret = true; + break; + } + } + } + } + else if (pollret < 0) { + err_sleep = &RESOLV_RSRC_ERR_PAUSE; + break; + } + else { + err_sleep = NULL; + break; + } + } + +END: + prne_close(pfs[0].fd); + prne_close(pfs[1].fd); + if (!ret && err_sleep != NULL) { + prne_unint_pth_nanosleep(*err_sleep); + } + return ret; +} + +static bool resolv_tls_handshake (prne_resolv_t *ctx) { + int pollret; + bool ret = false; + const struct timespec *err_sleep = NULL; + + while (true) { + switch (mbedtls_ssl_handshake(&ctx->ssl.ctx)) { + case MBEDTLS_ERR_SSL_WANT_READ: + ctx->act_sck_pfd.events = POLLIN; + break; + case MBEDTLS_ERR_SSL_WANT_WRITE: + ctx->act_sck_pfd.events = POLLOUT; + break; + case 0: + ret = true; + goto END; + default: + err_sleep = &RESOLV_CONN_ERR_PAUSE; + goto END; + } + + pollret = prne_unint_pth_poll(&ctx->act_sck_pfd, 1, &RESOLV_SCK_OP_TIMEOUT); + if (pollret < 0) { + err_sleep = &RESOLV_RSRC_ERR_PAUSE; + goto END; + } + else if (pollret == 0) { + goto END; + } + else if (ctx->act_sck_pfd.revents & (POLLERR | POLLNVAL | POLLHUP)) { + err_sleep = &RESOLV_CONN_ERR_PAUSE; + goto END; + } + } + +END: + if (!ret) { + resolv_close_sck(ctx, err_sleep, true); + } + return ret; +} + +static bool resolv_ensure_conn (prne_resolv_t *ctx) { + if (ctx->act_sck_pfd.fd < 0) { + if (!(resolv_ensure_act_dns_fd(ctx) && + resolv_tls_handshake(ctx))) + return false; + } + + return true; +} + +static const uint8_t* resolv_index_labels (prne_imap_t *map, const uint8_t *start, const uint8_t *end, const uint8_t *p, prne_resolv_qr_t *qr, int *err) { + uint16_t ptr; + const prne_imap_tuple_t *tpl; + + assert(qr != NULL); + assert(err != NULL); + if (p >= end) { + *qr = PRNE_RESOLV_QR_PRO_ERR; + return NULL; + } + + while (*p != 0 && p < end) { + if ((p[0] & 0xC0) == 0xC0) { + // met pointer. don't go further. + ptr = ((uint16_t)p[0] << 8) | (uint16_t)p[1]; + tpl = prne_imap_lookup(map, ptr); + if (tpl == NULL) { + *qr = PRNE_RESOLV_QR_ERR; + *err = errno; + return NULL; + } + return p + 2; + } + else if (*p > 63) { + *qr = PRNE_RESOLV_QR_PRO_ERR; + return NULL; + } + else { + // index the label + ptr = (uint16_t)(p - start) | 0xC000; + if (prne_imap_insert(map, ptr, (void*)p) == NULL) { + *qr = PRNE_RESOLV_QR_ERR; + *err = errno; + return NULL; + } + p += *p + 1; + } + } + + return p + 1; +} + +static int resolv_mapped_qname_cmp (prne_imap_t *map, const uint8_t *a, const uint8_t *b, prne_resolv_qr_t *qr) { + const uint8_t *p[2] = { a, b }; + size_t i; + uint16_t ptr; + const prne_imap_tuple_t *tpl; + int ret; + + + assert(qr != NULL); + + do { + // deref the pointers + for (i = 0; i < 2; i += 1) { + if ((p[i][0] & 0xC0) == 0xC0) { + ptr = ((uint16_t)p[i][0] << 8) | (uint16_t)p[i][1]; + tpl = prne_imap_lookup(map, ptr); + if (tpl == NULL) { + ret = -1; + *qr = PRNE_RESOLV_QR_PRO_ERR; + break; + } + p[i] = (const uint8_t*)tpl->val; + } + } + + if (*p[0] != *p[1]) { + ret = 0; + break; + } + if (*p[0] == 0 || *p[1] == 0) { + ret = 1; + break; + } + + p[0] += 1; + p[1] += 1; + } while (true); + + return ret; +} + +static bool resolv_proc_dns_msg (prne_resolv_t *ctx, const uint8_t *data, const size_t len, bool *err_flag) { + typedef struct { + const uint8_t *name; + const uint8_t *data; + uint32_t ttl; + uint16_t rtype; + uint16_t rclass; + uint16_t data_len; + } rr_tuple_t; + rr_tuple_t *tpl; + prne_resolv_qr_t qr; + int err = 0, cmp_ret; + uint_fast16_t qid, status, ancount, ttype; + prne_imap_t ptr_map; // val in msg(uint8_t):(uint8_t*)real addr + prne_llist_t rr_list, ret_list; + prne_iset_t alias_set; + prne_llist_entry_t *cur; + query_entry_t *qent; + const uint8_t *qname, *alias, *end = data + len, *p, *rname; + size_t i, j, loop_cnt; + bool ret; + + if (len < 12) { + *err_flag = true; + return false; + } + *err_flag = false; + + qr = PRNE_RESOLV_QR_NONE; + status = 0; + ttype = 0; + prne_init_imap(&ptr_map); + prne_init_llist(&rr_list); + prne_init_llist(&ret_list); + prne_init_iset(&alias_set); + + // ID + { + const prne_imap_tuple_t *tpl; + + qid = ((uint16_t)data[0] << 8) | (uint16_t)data[1]; + tpl = prne_imap_lookup(&ctx->qid_map, qid); + ret = tpl != NULL; + if (ret) { + qent = (query_entry_t*)tpl->val; + if (qent != NULL) { + switch (qent->type) { + case PRNE_RESOLV_QT_A: ttype = 1; break; + case PRNE_RESOLV_QT_AAAA: ttype = 28; break; + case PRNE_RESOLV_QT_TXT: ttype = 16; break; + default: abort(); + } + } + } + else { + qent = NULL; + } + prne_imap_erase(&ctx->qid_map, qid); + } + // QR + if ((data[2] & 0x80) == 0) { + qr = PRNE_RESOLV_QR_PRO_ERR; + *err_flag = true; + goto END; + } + // Opcode + if ((data[2] & 0x78) != 0) { + qr = PRNE_RESOLV_QR_PRO_ERR; + *err_flag = true; + goto END; + } + // AA - don't care + // RCODE + status = data[3] & 0x0F; + if (status != 0) { + qr = PRNE_RESOLV_QR_STATUS; + goto END; + } + // TC + if ((data[2] & 0x02) != 0) { + qr = PRNE_RESOLV_QR_IMPL; + goto END; + } + // QDCOUNT + if ((((uint_fast16_t)data[4] << 8) | (uint_fast16_t)data[5]) != 1) { + qr = PRNE_RESOLV_QR_PRO_ERR; + *err_flag = true; + goto END; + } + // ANCOUNT + ancount = ((uint_fast16_t)data[6] << 8) | (uint_fast16_t)data[7]; + + // decode question + if (len < 12 + 1 + 4) { // min msg with 1 QDCOUNT length + qr = PRNE_RESOLV_QR_PRO_ERR; + *err_flag = true; + goto END; + } + qname = data + 12; + p = resolv_index_labels(&ptr_map, data, end, (const uint8_t*)qname, &qr, &err); + if (p == NULL) { + goto END; + } + if ((size_t)(p - data + 4) > len) { + qr = PRNE_RESOLV_QR_PRO_ERR; + *err_flag = true; + goto END; + } + if (qent != NULL && strcmp((const char*)qname, qent->qname) != 0) { + qr = PRNE_RESOLV_QR_PRO_ERR; + *err_flag = true; + goto END; + } + if ((ttype != 0 && ttype != (((uint_fast16_t)p[0] << 8) | (uint_fast16_t)p[1])) || + (((uint_fast16_t)p[2] << 8) | (uint_fast16_t)p[3]) != 1) { + qr = PRNE_RESOLV_QR_PRO_ERR; + *err_flag = true; + goto END; + } + + p += 4; + // decode answer RRs + for (i = 0; i < ancount; i += 1) { + tpl = prne_malloc(sizeof(rr_tuple_t), 1); + if (tpl == NULL) { + err = errno; + qr = PRNE_RESOLV_QR_ERR; + goto END; + } + if (prne_llist_append(&rr_list, tpl) == NULL) { + prne_free(tpl); + err = errno; + qr = PRNE_RESOLV_QR_ERR; + goto END; + } + + tpl->name = p; + p = resolv_index_labels(&ptr_map, data, end, p, &qr, &err); + if (p == NULL) { + goto END; + } + if (p >= end || end - p < 10) { + qr = PRNE_RESOLV_QR_PRO_ERR; + *err_flag = true; + goto END; + } + tpl->rtype = ((uint_fast16_t)p[0] << 8) | (uint_fast16_t)p[1]; + tpl->rclass = ((uint_fast16_t)p[2] << 8) | (uint_fast16_t)p[3]; + tpl->ttl = ((uint_fast32_t)p[4]) | ((uint_fast32_t)p[5]) | ((uint_fast32_t)p[6]) | ((uint_fast32_t)p[7]); + tpl->data_len = ((uint_fast16_t)p[8] << 8) | (uint_fast16_t)p[9]; + rname = tpl->data = p + 10; + + switch (tpl->rtype) { + case PRNE_RESOLV_RTYPE_SOA: + loop_cnt = 2; + break; + case PRNE_RESOLV_RTYPE_CNAME: + case PRNE_RESOLV_RTYPE_MX: + case PRNE_RESOLV_RTYPE_NS: + case PRNE_RESOLV_RTYPE_PTR: + loop_cnt = 1; + break; + default: + loop_cnt = 0; + } + for (j = 0; j < loop_cnt; j += 1) { + rname = resolv_index_labels(&ptr_map, data, tpl->data + tpl->data_len, rname, &qr, &err); + if (rname == NULL) { + goto END; + } + } + + p += 10 + tpl->data_len; + } + + // resolve cname + alias = qname; + if (!prne_iset_insert(&alias_set, (prne_iset_val_t)alias)) { + qr = PRNE_RESOLV_QR_ERR; + err = errno; + goto END; + } +QNAME_START: + cur = rr_list.head; + while (cur != NULL) { + tpl = (rr_tuple_t*)cur->element; + + if (tpl->rtype == PRNE_RESOLV_RTYPE_CNAME) { + cmp_ret = resolv_mapped_qname_cmp(&ptr_map, tpl->name, alias, &qr); + if (cmp_ret < 0) { + goto END; + } + if (cmp_ret) { + if (prne_iset_lookup(&alias_set, (prne_iset_val_t)tpl->data)) { + qr = PRNE_RESOLV_QR_PRO_ERR; + goto END; + } + if (!prne_iset_insert(&alias_set, (prne_iset_val_t)tpl->data)) { + qr = PRNE_RESOLV_QR_ERR; + err = errno; + goto END; + } + alias = tpl->data; + goto QNAME_START; + } + } + + cur = cur->next; + } + + // index the selected(alias) resources + cur = rr_list.head; + while (cur != NULL) { + tpl = (rr_tuple_t*)cur->element; + + cmp_ret = resolv_mapped_qname_cmp(&ptr_map, tpl->name, alias, &qr); + if (cmp_ret < 0) { + goto END; + } + if (cmp_ret && ttype == tpl->rtype) { + if (prne_llist_append(&ret_list, tpl) == NULL) { + qr = PRNE_RESOLV_QR_ERR; + err = errno; + goto END; + } + } + + cur = cur->next; + } + + // return data + if (ret_list.size > 0 && qent != NULL) { + prne_llist_entry_t *cur; + rr_tuple_t *tpl; + + qent->fut.rr = (prne_resolv_rr_t*)prne_malloc(sizeof(prne_resolv_rr_t), ret_list.size); + if (qent->fut.rr == NULL) { + qr = PRNE_RESOLV_QR_ERR; + err = errno; + goto END; + } + qent->fut.rr_cnt = ret_list.size; + for (i = 0; i < qent->fut.rr_cnt; i += 1) { + prne_init_resolv_rr(qent->fut.rr + i); + } + + i = 0; + cur = ret_list.head; + while (cur != NULL) { + tpl = (rr_tuple_t*)cur->element; + + qent->fut.rr[i].rr_class = tpl->rclass; + qent->fut.rr[i].rr_type = tpl->rtype; + qent->fut.rr[i].rr_ttl = tpl->ttl; + if (tpl->data_len > 0) { + if ((qent->fut.rr[i].name = resolv_qname_tostr(qent->qname)) == NULL || + (qent->fut.rr[i].rd_data = (uint8_t*)prne_malloc(1, tpl->data_len)) == NULL) { + qr = PRNE_RESOLV_QR_ERR; + err = errno; + goto END; + } + qent->fut.rr[i].rd_len = tpl->data_len; + memcpy(qent->fut.rr[i].rd_data, tpl->data, tpl->data_len); + } + else { + qent->fut.rr[i].rd_data = NULL; + qent->fut.rr[i].rd_len = 0; + } + + i += 1; + cur = cur->next; + } + } + + qr = PRNE_RESOLV_QR_OK; + +END: + cur = rr_list.head; + while (cur != NULL) { + prne_free(cur->element); + cur = cur->next; + } + prne_free_llist(&rr_list); + prne_free_llist(&ret_list); + prne_free_imap(&ptr_map); + prne_free_iset(&alias_set); + if (qent != NULL) { + if (qr != PRNE_RESOLV_QR_OK) { + for (i = 0; i < qent->fut.rr_cnt; i += 1) { + prne_free_resolv_rr(qent->fut.rr + i); + } + prne_free(qent->fut.rr); + qent->fut.rr = NULL; + qent->fut.rr_cnt = 0; + } + qent->fut.qr = qr; + qent->fut.err = err; + qent->fut.status = status; + resolv_disown_qent(qent); + } + + return ret; +} + +static size_t resolv_calc_dot_msg_len (query_entry_t *qent) { + return 2/*DoT head*/ + 12/*msg head*/ + qent->qname_size + 4/*QCLASS, QTYPE*/; +} + +static void resolv_write_dns_msg (query_entry_t *qent, uint8_t *mem) { + // ID + mem[0] = (uint8_t)((qent->qid & 0xFF00) >> 8); + mem[1] = (uint8_t)(qent->qid & 0x00FF); + // QR: 0, Opcode: 0, AA:0, TC: 0, RD: 1, RA: 0, Z: 0, RCODE: 0 + mem[2] = 0x01; + mem[3] = 0x00; + // QDCOUNT: 1 + mem[4] = 0x00; + mem[5] = 0x01; + // ANCOUNT, NSCOUNT, ARCOUNT: 0 + mem[6] = mem[7] = mem[8] = mem[9] = mem[10] = mem[11] = 0x00; + + // QNAME + memcpy(mem + 12, qent->qname, qent->qname_size); + // QTYPE + switch (qent->type) { + case PRNE_RESOLV_QT_A: + mem[qent->qname_size + 12] = 0x00; + mem[qent->qname_size + 13] = 0x01; + break; + case PRNE_RESOLV_QT_AAAA: + mem[qent->qname_size + 12] = 0x00; + mem[qent->qname_size + 13] = 0x1C; + break; + case PRNE_RESOLV_QT_TXT: + mem[qent->qname_size + 12] = 0x00; + mem[qent->qname_size + 13] = 0x10; + break; + default: abort(); + } + // QCLASS: IN + mem[qent->qname_size + 14] = 0x00; + mem[qent->qname_size + 15] = 0x01; +} + +static bool resolv_send_dns_msgs (prne_resolv_t *ctx) { + prne_llist_entry_t *cur; + query_entry_t *qent; + size_t dot_msg_len, dns_msg_len; + uint16_t qid; + bool ret = false; + + cur = ctx->qlist.head; + while (cur != NULL && ctx->qid_map.size < RESOLV_PIPELINE_SIZE) { + qent = (query_entry_t*)cur->element; + + dot_msg_len = resolv_calc_dot_msg_len(qent); + dns_msg_len = dot_msg_len - 2; + if (dot_msg_len + ctx->write_cnt_len <= sizeof(ctx->write_buf)) { + qid = resolv_next_qid(ctx); + if (qid == 0) { + qent->fut.qr = PRNE_RESOLV_QR_ERR; + qent->fut.err = 0; + prne_llist_erase(&ctx->qlist, qent->qlist_ent); + resolv_disown_qent(qent); + + return ret; + } + + if (prne_imap_insert(&ctx->qid_map, qid, qent) == NULL) { + qent->fut.err = errno; + qent->fut.qr = PRNE_RESOLV_QR_ERR; + prne_llist_erase(&ctx->qlist, cur); + resolv_disown_qent(qent); + + return ret; + } + else { + cur = prne_llist_erase(&ctx->qlist, cur); + + ctx->write_buf[ctx->write_cnt_len + 0] = (uint8_t)((dns_msg_len & 0xFF00) >> 8); + ctx->write_buf[ctx->write_cnt_len + 1] = (uint8_t)(dns_msg_len & 0x00FF); + qent->qid = qid; + resolv_write_dns_msg(qent, ctx->write_buf + ctx->write_cnt_len + 2); + + ctx->write_cnt_len += dot_msg_len; + ret |= true; + } + } + else { + break; + } + } + + return ret; +} + +static void resolv_proc_expired (prne_resolv_t *ctx) { + const struct timespec now = prne_gettime(CLOCK_MONOTONIC); + prne_llist_entry_t *cur; + query_entry_t *qent; + + cur = ctx->qlist.head; + while (cur != NULL) { + qent = (query_entry_t*)cur->element; + + if (prne_cmp_timespec(RESOLV_QUERY_TIMEOUT, prne_sub_timespec(now, qent->tp_queued)) < 0) { + qent->fut.qr = PRNE_RESOLV_QR_TIMEOUT; + cur = prne_llist_erase(&ctx->qlist, cur); + resolv_disown_qent(qent); + } + else { + cur = cur->next; + } + } +} + +static void resolv_proc_q (prne_resolv_t *ctx) { + int pollret, ret; + short pfd_events; + bool proc; + /* + * The server could be sending gibberish response messages that look legit. + * Timeout the loop when we don't receive response we recognise in time. + */ + struct timespec last_proc, now, delta, op_rem; + +LOOP: + last_proc = prne_gettime(CLOCK_MONOTONIC); + proc = false; + while (ctx->qlist.size > 0 || ctx->qid_map.size > 0) { + resolv_proc_expired(ctx); + + if (ctx->write_cnt_len > 0 || ctx->qid_map.size < RESOLV_PIPELINE_SIZE) { + pfd_events = POLLIN | POLLOUT; + } + else { + pfd_events = POLLIN; + } + + if (!resolv_ensure_conn(ctx)) { + goto LOOP; + } + + now = prne_gettime(CLOCK_MONOTONIC); + if (proc) { + last_proc = now; + } + proc = false; + delta = prne_sub_timespec(now, last_proc); + if (prne_cmp_timespec(delta, RESOLV_SCK_OP_TIMEOUT) >= 0) { + resolv_close_sck(ctx, NULL, true); + goto LOOP; + } + + op_rem = prne_sub_timespec(RESOLV_SCK_OP_TIMEOUT, delta); + ctx->act_sck_pfd.events = pfd_events; + pollret = prne_unint_pth_poll(&ctx->act_sck_pfd, 1, &op_rem); + + if (pollret > 0) { + if (ctx->act_sck_pfd.revents & (POLLERR | POLLHUP | POLLNVAL)) { + resolv_close_sck(ctx, &RESOLV_CONN_ERR_PAUSE, true); + goto LOOP; + } + if (ctx->act_sck_pfd.revents & POLLIN) { + size_t pos, msg_len; + bool err_flag = false; + + ret = mbedtls_ssl_read(&ctx->ssl.ctx, ctx->read_buf + ctx->read_cnt_len, sizeof(ctx->read_buf) - ctx->read_cnt_len); + if (ret <= 0) { + // we don't renegotiate with terrorists. + resolv_close_sck(ctx, &RESOLV_CONN_ERR_PAUSE, true); + goto LOOP; + } + ctx->read_cnt_len += (size_t)ret; + + pos = 0; + while (true) { + if (pos + 1 >= ctx->read_cnt_len) { + break; + } + msg_len = ((size_t)ctx->read_buf[pos] << 8) | (size_t)ctx->read_buf[pos + 1]; + if (msg_len > 512) { // unimplemented. + prne_dbgpf("* [resolv_wkr] Protocol error: received %zu bytes long msg. Dropping connection!\n", msg_len); + // try to get qid + if (ctx->read_cnt_len > pos + 4) { + const uint16_t qid = ((uint_fast16_t)ctx->read_buf[pos + 2] << 8) | (uint_fast16_t)ctx->read_buf[pos + 3]; + const prne_imap_tuple_t *tpl = prne_imap_lookup(&ctx->qid_map, qid); + + if (tpl->val != NULL) { + query_entry_t *qent = (query_entry_t*)tpl->val; + qent->fut.qr = PRNE_RESOLV_QR_IMPL; + resolv_disown_qent(qent); + } + prne_imap_erase(&ctx->qid_map, qid); + } + resolv_close_sck(ctx, &RESOLV_CONN_ERR_PAUSE, true); + goto LOOP; + } + if (pos + 1 + msg_len >= ctx->read_cnt_len) { + break; + } + + proc |= resolv_proc_dns_msg(ctx, ctx->read_buf + pos + 2, msg_len, &err_flag); + if (err_flag) { + resolv_close_sck(ctx, &RESOLV_CONN_ERR_PAUSE, true); + goto LOOP; + } + pos += 2 + msg_len; + } + if (pos > 0) { + memmove(ctx->read_buf, ctx->read_buf + pos, ctx->read_cnt_len - pos); + ctx->read_cnt_len -= pos; + } + } + + if ((ctx->act_sck_pfd.revents & POLLOUT) && ctx->write_cnt_len > 0) { + ret = mbedtls_ssl_write(&ctx->ssl.ctx, ctx->write_buf, ctx->write_cnt_len); + if (ret <= 0) { + // we don't renegotiate with terrorists. + resolv_close_sck(ctx, &RESOLV_CONN_ERR_PAUSE, true); + goto LOOP; + } + + memmove(ctx->write_buf, ctx->write_buf + (size_t)ret, ctx->write_cnt_len - (size_t)ret); + ctx->write_cnt_len -= (size_t)ret; + } + if (ctx->write_cnt_len == 0) { + proc |= resolv_send_dns_msgs(ctx); + } + } + else if (pollret == 0) { + resolv_close_sck(ctx, NULL, true); + } + else { + resolv_close_sck(ctx, &RESOLV_RSRC_ERR_PAUSE, true); + } + } +} + +static void resolv_proc_close (prne_resolv_t *ctx) { + int pollret; + + do { + switch (mbedtls_ssl_close_notify(&ctx->ssl.ctx)) { + case MBEDTLS_ERR_SSL_WANT_READ: + ctx->act_sck_pfd.events = POLLIN; + break; + case MBEDTLS_ERR_SSL_WANT_WRITE: + ctx->act_sck_pfd.events = POLLOUT; + break; + case 0: + resolv_close_sck(ctx, NULL, false); + return; + default: + resolv_close_sck(ctx, &RESOLV_CONN_ERR_PAUSE, true); + return; + } + + pollret = prne_unint_pth_poll(&ctx->act_sck_pfd, 1, &RESOLV_SCK_CLOSE_TIMEOUT); + if (pollret < 0) { + resolv_close_sck(ctx, &RESOLV_CONN_ERR_PAUSE, true); + return; + } + else if (pollret == 0) { + resolv_close_sck(ctx, NULL, true); + return; + } + else if (ctx->act_sck_pfd.revents & (POLLERR | POLLHUP | POLLNVAL)) { + resolv_close_sck(ctx, &RESOLV_CONN_ERR_PAUSE, true); + return; + } + } while (true); +} + +static void resolv_wkr_free (void *p) { + DECL_CTX_PTR(p); + + if (p == NULL) { + return; + } + + if (ctx->dnssrv_4.ownership) { + prne_free(ctx->dnssrv_4.arr); + } + if (ctx->dnssrv_6.ownership) { + prne_free(ctx->dnssrv_6.arr); + } + prne_free_llist(&ctx->qlist); + prne_free_imap(&ctx->qid_map); + mbedtls_ssl_config_free(&ctx->ssl.conf); + mbedtls_ssl_free(&ctx->ssl.ctx); + + prne_close(ctx->act_sck_pfd.fd); + prne_free(ctx); +} + +static void resolv_wkr_fin (void *p) { + DECL_CTX_PTR(p); + prne_assert(pth_mutex_acquire(&ctx->lock, FALSE, NULL)); + ctx->ctx_state = RESOLV_CTX_STATE_FIN_CALLED; + pth_cond_notify(&ctx->cond, FALSE); + prne_assert(pth_mutex_release(&ctx->lock)); +} + +static void *resolv_wkr_entry (void *p) { + DECL_CTX_PTR(p); + bool sck_close; + + while (ctx->ctx_state == RESOLV_CTX_STATE_OK) { + sck_close = false; + + prne_assert(pth_mutex_acquire(&ctx->lock, FALSE, NULL)); + if (ctx->qlist.size == 0) { + pth_event_t ev; + + if (ctx->act_sck_pfd.fd >= 0) { + ev = pth_event(PTH_EVENT_TIME, pth_timeout(RESOLV_SCK_IDLE_TIMEOUT.tv_sec, 0)); + } + else { + ev = NULL; + } + + pth_cond_await(&ctx->cond, &ctx->lock, ev); + + if (ev != NULL) { + sck_close = pth_event_occurred(ev) != 0; + pth_event_free(ev, PTH_FREE_ALL); + } + } + prne_assert(pth_mutex_release(&ctx->lock)); + + if (sck_close) { + resolv_proc_close(ctx); + } + resolv_proc_q(ctx); + } + + if (ctx->act_sck_pfd.fd >= 0) { + resolv_proc_close(ctx); + } + + ctx->ctx_state = RESOLV_CTX_STATE_FINALISED; + return NULL; +} + +prne_resolv_t *prne_alloc_resolv (prne_worker_t *wkr, mbedtls_ctr_drbg_context *ctr_drbg) { + prne_resolv_t *ctx = NULL; + + if (wkr == NULL || ctr_drbg == NULL) { + errno = EINVAL; + return NULL; + } + + ctx = (prne_resolv_t*)prne_malloc(sizeof(prne_resolv_t), 1); + if (ctx == NULL) { + return NULL; + } + ctx->read_cnt_len = 0; + ctx->write_cnt_len = 0; + ctx->act_sck_pfd.fd = -1; + ctx->ctx_state = RESOLV_CTX_STATE_OK; + ctx->ssl.ctr_drbg = ctr_drbg; + prne_init_llist(&ctx->qlist); + prne_init_imap(&ctx->qid_map); + mbedtls_ssl_config_init(&ctx->ssl.conf); + mbedtls_ssl_init(&ctx->ssl.ctx); + pth_mutex_init(&ctx->lock); + pth_cond_init(&ctx->cond); + + ctx->dnssrv_4 = RESOLV_DEF_IPV4_POOL; + ctx->dnssrv_6 = RESOLV_DEF_IPV6_POOL; + ctx->ptr_dnssrv4 = resolv_next_pool_ptr(ctx, ctx->dnssrv_4.cnt); + ctx->ptr_dnssrv6 = resolv_next_pool_ptr(ctx, ctx->dnssrv_6.cnt); + if (mbedtls_ssl_config_defaults(&ctx->ssl.conf, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT) != 0) { + goto ERR; + } + mbedtls_ssl_conf_rng(&ctx->ssl.conf, mbedtls_ctr_drbg_random, ctx->ssl.ctr_drbg); + mbedtls_ssl_conf_authmode(&ctx->ssl.conf, MBEDTLS_SSL_VERIFY_NONE); + + wkr->ctx = ctx; + wkr->free_ctx = resolv_wkr_free; + wkr->fin = resolv_wkr_fin; + wkr->entry = resolv_wkr_entry; + return ctx; +ERR: + if (ctx != NULL) { + prne_free_llist(&ctx->qlist); + prne_free_imap(&ctx->qid_map); + mbedtls_ssl_config_free(&ctx->ssl.conf); + mbedtls_ssl_free(&ctx->ssl.ctx); + + prne_free(ctx); + } + + return NULL; +} + +bool prne_resolv_prm_gethostbyname (prne_resolv_t *wkr, const char *name, const prne_ipv_t ipv, prne_pth_cv_t *cv, prne_resolv_prm_t *out) { + bool ret; + query_entry_t *q_ent; + prne_resolv_query_type_t qt; + + switch (ipv) { + case PRNE_IPV_4: qt = PRNE_RESOLV_QT_A; break; + case PRNE_IPV_6: qt = PRNE_RESOLV_QT_AAAA; break; + default: + errno = EINVAL; + return false; + } + + ret = resolv_qq(wkr, name, cv, out, &q_ent); + if (ret) { + q_ent->ipv = ipv; + q_ent->type = qt; + } + + return ret; +} + +bool prne_resolv_prm_gettxtrec (prne_resolv_t *wkr, const char *name, prne_pth_cv_t *cv, prne_resolv_prm_t *out) { + bool ret; + query_entry_t *q_ent; + + ret = resolv_qq(wkr, name, cv, out, &q_ent); + if (ret) { + q_ent->type = PRNE_RESOLV_QT_TXT; + } + + return ret; +} + +void prne_resolv_free_prm (prne_resolv_prm_t *prm) { + if (prm->ctx != NULL) { + query_entry_t *ent = (query_entry_t*)prm->ctx; + + if (ent->owner != NULL) { + prne_llist_erase(&ent->owner->qlist, ent->qlist_ent); + + if (prne_imap_lookup(&ent->owner->qid_map, ent->qid) != NULL) { + prne_imap_insert(&ent->owner->qid_map, ent->qid, 0); + } + } + resolv_free_q_ent(ent); + } + + prm->ctx = NULL; + prm->fut = NULL; +} + +void prne_resolv_init_prm (prne_resolv_prm_t *prm) { + prm->ctx = NULL; + prm->fut = NULL; +} + +void prne_init_resolv_fut (prne_resolv_fut_t *fut) { + fut->rr_cnt = 0; + fut->rr = NULL; + fut->qr = PRNE_RESOLV_QR_NONE; + fut->err = 0; + fut->status = 0; +} + +void prne_free_resolv_fut (prne_resolv_fut_t *fut) { + size_t i; + + for (i = 0; i < fut->rr_cnt; i += 1) { + prne_free_resolv_rr(fut->rr + i); + } + prne_free(fut->rr); + fut->rr = NULL; + fut->rr_cnt = 0; +} + +void prne_init_resolv_rr (prne_resolv_rr_t *rr) { + rr->name = NULL; + rr->rr_class = 0; + rr->rr_type = 0; + rr->rr_ttl = 0; + rr->rd_data = NULL; + rr->rd_len = 0; +} + +void prne_free_resolv_rr (prne_resolv_rr_t *rr) { + prne_free(rr->name); + prne_free(rr->rd_data); + rr->rd_data = NULL; + rr->rd_len = 0; +} + +const char *prne_resolv_qr_tostr (const prne_resolv_qr_t qr) { + switch (qr) { + case PRNE_RESOLV_QR_OK: return "OK"; + case PRNE_RESOLV_QR_ERR: return "ERR"; + case PRNE_RESOLV_QR_PRO_ERR: return "PRO_ERR"; + case PRNE_RESOLV_QR_FIN: return "FIN"; + case PRNE_RESOLV_QR_IMPL: return "IMPL"; + case PRNE_RESOLV_QR_TIMEOUT: return "TIMEOUT"; + case PRNE_RESOLV_QR_STATUS: return "STATUS"; + } + return NULL; +} + +const char *prne_resolv_rcode_tostr (const prne_resolv_rcode_t rc) { + switch (rc) { + case PRNE_RESOLV_RCODE_NOERROR: return "NOERROR"; + case PRNE_RESOLV_RCODE_FORMERR: return "FORMERR"; + case PRNE_RESOLV_RCODE_SERVFAIL: return "SERVFAIL"; + case PRNE_RESOLV_RCODE_NXDOMAIN: return "NXDOMAIN"; + case PRNE_RESOLV_RCODE_NOTIMP: return "NOTIMP"; + case PRNE_RESOLV_RCODE_REFUSED: return "REFUSED"; + } + return NULL; +} + +const char *prne_resolv_rrtype_tostr (const uint16_t rrt) { + switch (rrt) { + case PRNE_RESOLV_RTYPE_A: return "A"; + case PRNE_RESOLV_RTYPE_NS: return "NS"; + case PRNE_RESOLV_RTYPE_CNAME: return "CNAME"; + case PRNE_RESOLV_RTYPE_SOA: return "SOA"; + case PRNE_RESOLV_RTYPE_PTR: return "PTR"; + case PRNE_RESOLV_RTYPE_MX: return "MX"; + case PRNE_RESOLV_RTYPE_TXT: return "TXT"; + case PRNE_RESOLV_RTYPE_AAAA: return "AAAA"; + } + return NULL; +} |