Free (GPLv2) TCP/IP stack developed by TASS Belgium

Dependents:   lpc1768-picotcp-demo ZeroMQ_PicoTCP_Publisher_demo TCPSocket_HelloWorld_PicoTCP Pico_TCP_UDP_Test ... more

PicoTCP. Copyright (c) 2013 TASS Belgium NV.

Released under the GNU General Public License, version 2.

Different licensing models may exist, at the sole discretion of the Copyright holders.

Official homepage: http://www.picotcp.com

Bug tracker: https://github.com/tass-belgium/picotcp/issues

Development steps:

  • initial integration with mbed RTOS
  • generic mbed Ethernet driver
  • high performance NXP LPC1768 specific Ethernet driver
  • Multi-threading support for mbed RTOS
  • Berkeley sockets and integration with the New Socket API
  • Fork of the apps running on top of the New Socket API
  • Scheduling optimizations
  • Debugging/benchmarking/testing

Demo application (measuring TCP sender performance):

Import programlpc1768-picotcp-demo

A PicoTCP demo app testing the ethernet throughput on the lpc1768 mbed board.

modules/pico_dns_client.c

Committer:
tass
Date:
2015-09-28
Revision:
152:a3d286bf94e5
Parent:
149:5f4cb161cec3

File content as of revision 152:a3d286bf94e5:

/*********************************************************************
   PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
   See LICENSE and COPYING for usage.
   .
   Authors: Kristof Roelants
 *********************************************************************/
#include "pico_config.h"
#include "pico_stack.h"
#include "pico_addressing.h"
#include "pico_socket.h"
#include "pico_ipv4.h"
#include "pico_ipv6.h"
#include "pico_dns_client.h"
#include "pico_dns_common.h"
#include "pico_tree.h"

#ifdef PICO_SUPPORT_DNS_CLIENT

#ifdef PICO_SUPPORT_IPV4

#define dns_dbg(...) do {} while(0)
/* #define dns_dbg dbg */

/* DNS response length */
#define PICO_DNS_MAX_QUERY_LEN 255
#define PICO_DNS_MAX_QUERY_LABEL_LEN 63

/* DNS client retransmission time (msec) + frequency */
#define PICO_DNS_CLIENT_RETRANS 4000
#define PICO_DNS_CLIENT_MAX_RETRANS 3

static void pico_dns_client_callback(uint16_t ev, struct pico_socket *s);
static void pico_dns_client_retransmission(pico_time now, void *arg);
static int pico_dns_client_getaddr_init(const char *url, uint16_t proto, void (*callback)(char *, void *), void *arg);

struct pico_dns_ns
{
    struct pico_ip4 ns; /* nameserver */
};

static int dns_ns_cmp(void *ka, void *kb)
{
    struct pico_dns_ns *a = ka, *b = kb;
    return pico_ipv4_compare(&a->ns, &b->ns);
}
PICO_TREE_DECLARE(NSTable, dns_ns_cmp);

struct pico_dns_query
{
    char *query;
    uint16_t len;
    uint16_t id;
    uint16_t qtype;
    uint16_t qclass;
    uint8_t retrans;
    struct pico_dns_ns q_ns;
    struct pico_socket *s;
    void (*callback)(char *, void *);
    void *arg;
};

static int dns_query_cmp(void *ka, void *kb)
{
    struct pico_dns_query *a = ka, *b = kb;
    if (a->id == b->id)
        return 0;

    return (a->id < b->id) ? (-1) : (1);
}
PICO_TREE_DECLARE(DNSTable, dns_query_cmp);

static int pico_dns_client_del_ns(struct pico_ip4 *ns_addr)
{
    struct pico_dns_ns test = {{0}}, *found = NULL;

    test.ns = *ns_addr;
    found = pico_tree_findKey(&NSTable, &test);
    if (!found)
        return -1;

    pico_tree_delete(&NSTable, found);
    PICO_FREE(found);

    /* no NS left, add default NS */
    if (pico_tree_empty(&NSTable))
        pico_dns_client_init();

    return 0;
}

static struct pico_dns_ns *pico_dns_client_add_ns(struct pico_ip4 *ns_addr)
{
    struct pico_dns_ns *dns = NULL, *found = NULL, test = {{0}};

    dns = PICO_ZALLOC(sizeof(struct pico_dns_ns));
    if (!dns) {
        pico_err = PICO_ERR_ENOMEM;
        return NULL;
    }

    dns->ns = *ns_addr;

    found = pico_tree_insert(&NSTable, dns);
    if (found) { /* nameserver already present */
        PICO_FREE(dns);
        return found;
    }

    /* default NS found, remove it */
    pico_string_to_ipv4(PICO_DNS_NS_DEFAULT, (uint32_t *)&test.ns.addr);
    found = pico_tree_findKey(&NSTable, &test);
    if (found && (found->ns.addr != ns_addr->addr))
        pico_dns_client_del_ns(&found->ns);

    return dns;
}

static struct pico_dns_ns pico_dns_client_next_ns(struct pico_ip4 *ns_addr)
{
    struct pico_dns_ns dns = {{0}}, *nxtdns = NULL;
    struct pico_tree_node *node = NULL, *nxtnode = NULL;

    dns.ns = *ns_addr;
    node = pico_tree_findNode(&NSTable, &dns);
    if (!node)
        return dns; /* keep using current NS */

    nxtnode = pico_tree_next(node);
    nxtdns = nxtnode->keyValue;
    if (!nxtdns)
        nxtdns = (struct pico_dns_ns *)pico_tree_first(&NSTable);

    return *nxtdns;
}

static struct pico_dns_query *pico_dns_client_add_query(struct pico_dns_header *hdr, uint16_t len, struct pico_dns_question_suffix *suffix,
                                                        void (*callback)(char *, void *), void *arg)
{
    struct pico_dns_query *q = NULL, *found = NULL;

    q = PICO_ZALLOC(sizeof(struct pico_dns_query));
    if (!q)
        return NULL;

    q->query = (char *)hdr;
    q->len = len;
    q->id = short_be(hdr->id);
    q->qtype = short_be(suffix->qtype);
    q->qclass = short_be(suffix->qclass);
    q->retrans = 1;
    q->q_ns = *((struct pico_dns_ns *)pico_tree_first(&NSTable));
    q->callback = callback;
    q->arg = arg;
    q->s = pico_socket_open(PICO_PROTO_IPV4, PICO_PROTO_UDP, &pico_dns_client_callback);
    if (!q->s) {
        PICO_FREE(q);
        return NULL;
    }

    found = pico_tree_insert(&DNSTable, q);
    if (found) {
        pico_err = PICO_ERR_EAGAIN;
        pico_socket_close(q->s);
        PICO_FREE(q);
        return NULL;
    }

    return q;
}

static int pico_dns_client_del_query(uint16_t id)
{
    struct pico_dns_query test = {
        0
    }, *found = NULL;

    test.id = id;
    found = pico_tree_findKey(&DNSTable, &test);
    if (!found)
        return -1;

    PICO_FREE(found->query);
    pico_socket_close(found->s);
    pico_tree_delete(&DNSTable, found);
    PICO_FREE(found);
    return 0;
}

static struct pico_dns_query *pico_dns_client_find_query(uint16_t id)
{
    struct pico_dns_query test = {
        0
    }, *found = NULL;

    test.id = id;
    found = pico_tree_findKey(&DNSTable, &test);
    if (found)
        return found;
    else
        return NULL;
}

/* seek end of string */
static char *pico_dns_client_seek(char *ptr)
{
    if (!ptr)
        return NULL;

    while (*ptr != 0)
        ptr++;
    return ptr + 1;
}

static struct pico_dns_query *pico_dns_client_idcheck(uint16_t id)
{
    struct pico_dns_query test = {
        0
    };

    test.id = id;
    return pico_tree_findKey(&DNSTable, &test);
}

static int pico_dns_client_query_header(struct pico_dns_header *hdr)
{
    uint16_t id = 0;
    uint8_t retry = 32;

    do {
        id = (uint16_t)(pico_rand() & 0xFFFFU);
        dns_dbg("DNS: generated id %u\n", id);
    } while (retry-- && pico_dns_client_idcheck(id));
    if (!retry)
        return -1;

    hdr->id = short_be(id);
    pico_dns_fill_packet_header(hdr, 1, 0, 0, 0); /* 1 question, 0 answers */

    return 0;
}

static int pico_dns_client_check_header(struct pico_dns_header *pre)
{
    if (pre->qr != PICO_DNS_QR_RESPONSE || pre->opcode != PICO_DNS_OPCODE_QUERY || pre->rcode != PICO_DNS_RCODE_NO_ERROR) {
        dns_dbg("DNS ERROR: OPCODE %d | TC %d | RCODE %d\n", pre->opcode, pre->tc, pre->rcode);
        return -1;
    }

    if (short_be(pre->ancount) < 1) {
        dns_dbg("DNS ERROR: ancount < 1\n");
        return -1;
    }

    return 0;
}

static int pico_dns_client_check_qsuffix(struct pico_dns_question_suffix *suf, struct pico_dns_query *q)
{
    if (!suf)
        return -1;

    if (short_be(suf->qtype) != q->qtype || short_be(suf->qclass) != q->qclass) {
        dns_dbg("DNS ERROR: received qtype (%u) or qclass (%u) incorrect\n", short_be(suf->qtype), short_be(suf->qclass));
        return -1;
    }

    return 0;
}

static int pico_dns_client_check_url(struct pico_dns_header *resp, struct pico_dns_query *q)
{
    char *recv_name = (char*)(resp) + sizeof(struct pico_dns_header) + PICO_DNS_LABEL_INITIAL;
    char *exp_name = (char *)(q->query) + sizeof(struct pico_dns_header) + PICO_DNS_LABEL_INITIAL;
    if (strcasecmp(recv_name,  exp_name) != 0)
        return -1;

    return 0;
}

static int pico_dns_client_check_asuffix(struct pico_dns_record_suffix *suf, struct pico_dns_query *q)
{
    if (!suf) {
        pico_err = PICO_ERR_EINVAL;
        return -1;
    }

    if (short_be(suf->rtype) != q->qtype || short_be(suf->rclass) != q->qclass) {
        dns_dbg("DNS WARNING: received qtype (%u) or qclass (%u) incorrect\n", short_be(suf->rtype), short_be(suf->rclass));
        return -1;
    }

    if (long_be(suf->rttl) > PICO_DNS_MAX_TTL) {
        dns_dbg("DNS WARNING: received TTL (%u) > MAX (%u)\n", short_be(suf->rttl), PICO_DNS_MAX_TTL);
        return -1;
    }

    return 0;
}

static char *pico_dns_client_seek_suffix(char *suf, struct pico_dns_header *pre, struct pico_dns_query *q)
{
    struct pico_dns_record_suffix *asuffix = NULL;
    uint16_t comp = 0, compression = 0;
    uint16_t i = 0;

    if (!suf)
        return NULL;

    while (i++ < short_be(pre->ancount)) {
        comp = short_from(suf);
        compression = short_be(comp);
        switch (compression >> 14)
        {
        case PICO_DNS_POINTER:
            while (compression >> 14 == PICO_DNS_POINTER) {
                dns_dbg("DNS: pointer\n");
                suf += sizeof(uint16_t);
                comp = short_from(suf);
                compression = short_be(comp);
            }
            break;

        case PICO_DNS_LABEL:
            dns_dbg("DNS: label\n");
            suf = pico_dns_client_seek(suf);
            break;

        default:
            dns_dbg("DNS ERROR: incorrect compression (%u) value\n", compression);
            return NULL;
        }

        asuffix = (struct pico_dns_record_suffix *)suf;
        if (!asuffix)
            break;

        if (pico_dns_client_check_asuffix(asuffix, q) < 0) {
            suf += (sizeof(struct pico_dns_record_suffix) + short_be(asuffix->rdlength));
            continue;
        }

        return suf;
    }
    return NULL;
}

static int pico_dns_client_send(struct pico_dns_query *q)
{
    uint16_t *paramID = PICO_ZALLOC(sizeof(uint16_t));
    if (!paramID) {
        pico_err = PICO_ERR_ENOMEM;
        return -1;
    }

    dns_dbg("DNS: sending query to %08X\n", q->q_ns.ns.addr);
    if (!q->s)
        goto failure;

    if (pico_socket_connect(q->s, &q->q_ns.ns, short_be(PICO_DNS_NS_PORT)) < 0)
        goto failure;

    pico_socket_send(q->s, q->query, q->len);
    *paramID = q->id;
    pico_timer_add(PICO_DNS_CLIENT_RETRANS, pico_dns_client_retransmission, paramID);

    return 0;

failure:
    PICO_FREE(paramID);
    return -1;
}

static void pico_dns_client_retransmission(pico_time now, void *arg)
{
    struct pico_dns_query *q = NULL;
    struct pico_dns_query dummy;
    IGNORE_PARAMETER(now);

    if(!arg)
        return;

    /* search for the dns query and free used space */
    dummy.id = *(uint16_t *)arg;
    q = (struct pico_dns_query *)pico_tree_findKey(&DNSTable, &dummy);
    PICO_FREE(arg);

    /* dns query successful? */
    if (!q) {
        return;
    }

    q->retrans++;
    if (q->retrans <= PICO_DNS_CLIENT_MAX_RETRANS) {
        q->q_ns = pico_dns_client_next_ns(&q->q_ns.ns);
        pico_dns_client_send(q);
    } else {
        pico_err = PICO_ERR_EIO;
        q->callback(NULL, q->arg);
        pico_dns_client_del_query(q->id);
    }
}

static int pico_dns_client_user_callback(struct pico_dns_record_suffix *asuffix, struct pico_dns_query *q)
{
    uint32_t ip = 0;
    char *str = NULL;
    char *rdata = (char *) asuffix + sizeof(struct pico_dns_record_suffix);

    switch (q->qtype)
    {
    case PICO_DNS_TYPE_A:
        ip = long_from(rdata);
        str = PICO_ZALLOC(PICO_DNS_IPV4_ADDR_LEN);
        pico_ipv4_to_string(str, ip);
        break;
#ifdef PICO_SUPPORT_IPV6
    case PICO_DNS_TYPE_AAAA:
    {
        struct pico_ip6 ip6;
        memcpy(&ip6.addr, rdata, sizeof(struct pico_ip6));
        str = PICO_ZALLOC(PICO_DNS_IPV6_ADDR_LEN);
        pico_ipv6_to_string(str, ip6.addr);
        break;
    }
#endif
    case PICO_DNS_TYPE_PTR:
        /* TODO: check for decompression / rdlength vs. decompressed length */
        pico_dns_notation_to_name(rdata, short_be(asuffix->rdlength));
        str = PICO_ZALLOC((size_t)(short_be(asuffix->rdlength) -
                                   PICO_DNS_LABEL_INITIAL));
        if (!str) {
            pico_err = PICO_ERR_ENOMEM;
            return -1;
        }

        memcpy(str, rdata + PICO_DNS_LABEL_INITIAL, short_be(asuffix->rdlength) - PICO_DNS_LABEL_INITIAL);
        break;

    default:
        dns_dbg("DNS ERROR: incorrect qtype (%u)\n", q->qtype);
        break;
    }

    if (q->retrans) {
        q->callback(str, q->arg);
        q->retrans = 0;
        pico_dns_client_del_query(q->id);
    }

    if (str)
        PICO_FREE(str);

    return 0;
}

static char dns_response[PICO_IP_MRU] = {
    0
};

static void pico_dns_try_fallback_cname(struct pico_dns_query *q, struct pico_dns_header *h, struct pico_dns_question_suffix *qsuffix)
{
    uint16_t type = q->qtype;
    uint16_t proto = PICO_PROTO_IPV4;
    struct pico_dns_record_suffix *asuffix = NULL;
    char *p_asuffix = NULL;
    char *cname_orig = NULL;
    char *cname = NULL;
    uint16_t cname_len;

    /* Try to use CNAME only if A or AAAA query is ongoing */
    if (type != PICO_DNS_TYPE_A && type != PICO_DNS_TYPE_AAAA)
        return;

    if (type == PICO_DNS_TYPE_AAAA)
        proto = PICO_PROTO_IPV6;

    q->qtype = PICO_DNS_TYPE_CNAME;
    p_asuffix = (char *)qsuffix + sizeof(struct pico_dns_question_suffix);
    p_asuffix = pico_dns_client_seek_suffix(p_asuffix, h, q);
    if (!p_asuffix) {
        return;
    }

    /* Found CNAME response. Re-initiating query. */
    asuffix = (struct pico_dns_record_suffix *)p_asuffix;
    cname = pico_dns_decompress_name((char *)asuffix + sizeof(struct pico_dns_record_suffix), (pico_dns_packet *)h); /* allocates memory! */
    cname_orig = cname; /* to free later */

    if (cname == NULL)
        return;

    cname_len = (uint16_t)(pico_dns_strlen(cname) + 1);

    pico_dns_notation_to_name(cname, cname_len);
    if (cname[0] == '.')
        cname++;

    dns_dbg("Restarting query for name '%s'\n", cname);
    pico_dns_client_getaddr_init(cname, proto, q->callback, q->arg);
    PICO_FREE(cname_orig);
    pico_dns_client_del_query(q->id);
}

static void pico_dns_client_callback(uint16_t ev, struct pico_socket *s)
{
    struct pico_dns_header *header = NULL;
    char *domain;
    struct pico_dns_question_suffix *qsuffix = NULL;
    struct pico_dns_record_suffix *asuffix = NULL;
    struct pico_dns_query *q = NULL;
    char *p_asuffix = NULL;

    if (ev == PICO_SOCK_EV_ERR) {
        dns_dbg("DNS: socket error received\n");
        return;
    }

    if (ev & PICO_SOCK_EV_RD) {
        if (pico_socket_read(s, dns_response, PICO_IP_MRU) < 0)
            return;
    }

    header = (struct pico_dns_header *)dns_response;
    domain = (char *)header + sizeof(struct pico_dns_header);
    qsuffix = (struct pico_dns_question_suffix *)pico_dns_client_seek(domain);
    /* valid asuffix is determined dynamically later on */

    if (pico_dns_client_check_header(header) < 0)
        return;

    q = pico_dns_client_find_query(short_be(header->id));
    if (!q)
        return;

    if (pico_dns_client_check_qsuffix(qsuffix, q) < 0)
        return;

    if (pico_dns_client_check_url(header, q) < 0)
        return;

    p_asuffix = (char *)qsuffix + sizeof(struct pico_dns_question_suffix);
    p_asuffix = pico_dns_client_seek_suffix(p_asuffix, header, q);
    if (!p_asuffix) {
        pico_dns_try_fallback_cname(q, header, qsuffix);
        return;
    }

    asuffix = (struct pico_dns_record_suffix *)p_asuffix;
    pico_dns_client_user_callback(asuffix, q);

    return;
}

static int pico_dns_create_message(struct pico_dns_header **header, struct pico_dns_question_suffix **qsuffix, enum pico_dns_arpa arpa, const char *url, uint16_t *urlen, uint16_t *hdrlen)
{
    char *domain;
    char inaddr_arpa[14];
    uint16_t strlen = 0, arpalen = 0;

    if (!url) {
        pico_err = PICO_ERR_EINVAL;
        return -1;
    }

    if(arpa == PICO_DNS_ARPA4) {
        strcpy(inaddr_arpa, ".in-addr.arpa");
        strlen = pico_dns_strlen(url);
    }

#ifdef PICO_SUPPORT_IPV6
    else if (arpa == PICO_DNS_ARPA6) {
        strcpy(inaddr_arpa, ".IP6.ARPA");
        strlen = STRLEN_PTR_IP6;
    }
#endif
    else {
        strcpy(inaddr_arpa, "");
        strlen = pico_dns_strlen(url);
    }

    arpalen = pico_dns_strlen(inaddr_arpa);
    *urlen = (uint16_t)(PICO_DNS_LABEL_INITIAL + strlen + arpalen + PICO_DNS_LABEL_ROOT);
    *hdrlen = (uint16_t)(sizeof(struct pico_dns_header) + *urlen + sizeof(struct pico_dns_question_suffix));
    *header = PICO_ZALLOC(*hdrlen);
    if (!*header) {
        pico_err = PICO_ERR_ENOMEM;
        return -1;
    }

    *header = (struct pico_dns_header *)*header;
    domain = (char *) *header + sizeof(struct pico_dns_header);
    *qsuffix = (struct pico_dns_question_suffix *)(domain + *urlen);

    if(arpa == PICO_DNS_ARPA4) {
        memcpy(domain + PICO_DNS_LABEL_INITIAL, url, strlen);
        pico_dns_mirror_addr(domain + PICO_DNS_LABEL_INITIAL);
        memcpy(domain + PICO_DNS_LABEL_INITIAL + strlen, inaddr_arpa, arpalen);
    }

#ifdef PICO_SUPPORT_IPV6
    else if (arpa == PICO_DNS_ARPA6) {
        pico_dns_ipv6_set_ptr(url, domain + PICO_DNS_LABEL_INITIAL);
        memcpy(domain + PICO_DNS_LABEL_INITIAL + STRLEN_PTR_IP6, inaddr_arpa, arpalen);
    }
#endif
    else {
        memcpy(domain + PICO_DNS_LABEL_INITIAL, url, strlen);
    }

    /* assemble dns message */
    pico_dns_client_query_header(*header);
    pico_dns_name_to_dns_notation(domain, strlen);

    return 0;
}

static int pico_dns_client_addr_label_check_len(const char *url)
{
    const char *p, *label;
    int count;
    label = url;
    p = label;

    while(*p != (char) 0) {
        count = 0;
        while((*p != (char)0)) {
            if (*p == '.') {
                label = ++p;
                break;
            }

            count++;
            p++;
            if (count > PICO_DNS_MAX_QUERY_LABEL_LEN)
                return -1;
        }
    }
    return 0;
}

static int pico_dns_client_getaddr_check(const char *url, void (*callback)(char *, void *))
{
    if (!url || !callback) {
        pico_err = PICO_ERR_EINVAL;
        return -1;
    }

    if (strlen(url) > PICO_DNS_MAX_QUERY_LEN) {
        pico_err = PICO_ERR_EINVAL;
        return -1;
    }

    if (pico_dns_client_addr_label_check_len(url) < 0) {
        pico_err = PICO_ERR_EINVAL;
        return -1;
    }

    return 0;
}

static int pico_dns_client_getaddr_init(const char *url, uint16_t proto, void (*callback)(char *, void *), void *arg)
{
    struct pico_dns_header *header = NULL;
    struct pico_dns_question_suffix *qsuffix = NULL;
    struct pico_dns_query *q = NULL;
    uint16_t len = 0, lblen = 0;
    (void)proto;

    if (pico_dns_client_getaddr_check(url, callback) < 0)
        return -1;

    if(pico_dns_create_message(&header, &qsuffix, PICO_DNS_NO_ARPA, url, &lblen, &len) != 0)
        return -1;

#ifdef PICO_SUPPORT_IPV6
    if (proto == PICO_PROTO_IPV6) {
        pico_dns_question_fill_suffix(qsuffix, PICO_DNS_TYPE_AAAA, PICO_DNS_CLASS_IN);
    } else
#endif
    pico_dns_question_fill_suffix(qsuffix, PICO_DNS_TYPE_A, PICO_DNS_CLASS_IN);

    q = pico_dns_client_add_query(header, len, qsuffix, callback, arg);
    if (!q) {
        PICO_FREE(header);
        return -1;
    }

    if (pico_dns_client_send(q) < 0) {
        pico_dns_client_del_query(q->id); /* frees msg */
        return -1;
    }

    return 0;
}

int pico_dns_client_getaddr(const char *url, void (*callback)(char *, void *), void *arg)
{
    return pico_dns_client_getaddr_init(url, PICO_PROTO_IPV4, callback, arg);
}

int pico_dns_client_getaddr6(const char *url, void (*callback)(char *, void *), void *arg)
{
    return pico_dns_client_getaddr_init(url, PICO_PROTO_IPV6, callback, arg);
}

static int pico_dns_getname_univ(const char *ip, void (*callback)(char *, void *), void *arg, enum pico_dns_arpa arpa)
{
    struct pico_dns_header *header = NULL;
    struct pico_dns_question_suffix *qsuffix = NULL;
    struct pico_dns_query *q = NULL;
    uint16_t len = 0, lblen = 0;

    if (!ip || !callback) {
        pico_err = PICO_ERR_EINVAL;
        return -1;
    }

    if(pico_dns_create_message(&header, &qsuffix, arpa, ip, &lblen, &len) != 0)
        return -1;

    pico_dns_question_fill_suffix(qsuffix, PICO_DNS_TYPE_PTR, PICO_DNS_CLASS_IN);
    q = pico_dns_client_add_query(header, len, qsuffix, callback, arg);
    if (!q) {
        PICO_FREE(header);
        return -1;
    }

    if (pico_dns_client_send(q) < 0) {
        pico_dns_client_del_query(q->id); /* frees header */
        return -1;
    }

    return 0;
}

int pico_dns_client_getname(const char *ip, void (*callback)(char *, void *), void *arg)
{
    return pico_dns_getname_univ(ip, callback, arg, PICO_DNS_ARPA4);
}


int pico_dns_client_getname6(const char *ip, void (*callback)(char *, void *), void *arg)
{
    return pico_dns_getname_univ(ip, callback, arg, PICO_DNS_ARPA6);
}

int pico_dns_client_nameserver(struct pico_ip4 *ns, uint8_t flag)
{
    if (!ns) {
        pico_err = PICO_ERR_EINVAL;
        return -1;
    }

    switch (flag)
    {
    case PICO_DNS_NS_ADD:
        if (!pico_dns_client_add_ns(ns))
            return -1;

        break;

    case PICO_DNS_NS_DEL:
        if (pico_dns_client_del_ns(ns) < 0) {
            pico_err = PICO_ERR_EINVAL;
            return -1;
        }

        break;

    default:
        pico_err = PICO_ERR_EINVAL;
        return -1;
    }
    return 0;
}

int pico_dns_client_init(void)
{
    struct pico_ip4 default_ns = {
        0
    };

    if (pico_string_to_ipv4(PICO_DNS_NS_DEFAULT, (uint32_t *)&default_ns.addr) < 0)
        return -1;

    return pico_dns_client_nameserver(&default_ns, PICO_DNS_NS_ADD);
}

#else

int pico_dns_client_init(void)
{
    dbg("ERROR Trying to initialize DNS module: IPv4 not supported in this build.\n");
    return -1;
}
#endif /* PICO_SUPPORT_IPV4 */


#endif /* PICO_SUPPORT_DNS_CLIENT */