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.

Revision:
152:a3d286bf94e5
Parent:
149:5f4cb161cec3
--- a/modules/pico_dns_client.c	Wed Apr 09 17:32:25 2014 +0200
+++ b/modules/pico_dns_client.c	Mon Sep 28 13:16:18 2015 +0200
@@ -1,9 +1,7 @@
 /*********************************************************************
    PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
    See LICENSE and COPYING for usage.
-
    .
-
    Authors: Kristof Roelants
  *********************************************************************/
 #include "pico_config.h"
@@ -13,111 +11,27 @@
 #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_RESPONSE_LEN 256
+#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
 
-/* Default nameservers + port */
-#define PICO_DNS_NS_DEFAULT "208.67.222.222"
-#define PICO_DNS_NS_PORT 53
-
-/* FLAG values */
-#define PICO_DNS_QR_QUERY 0
-#define PICO_DNS_QR_RESPONSE 1
-#define PICO_DNS_OPCODE_QUERY 0
-#define PICO_DNS_OPCODE_IQUERY 1
-#define PICO_DNS_OPCODE_STATUS 2
-#define PICO_DNS_AA_NO_AUTHORITY 0
-#define PICO_DNS_AA_IS_AUTHORITY 1
-#define PICO_DNS_TC_NO_TRUNCATION 0
-#define PICO_DNS_TC_IS_TRUNCATED 1
-#define PICO_DNS_RD_NO_DESIRE 0
-#define PICO_DNS_RD_IS_DESIRED 1
-#define PICO_DNS_RA_NO_SUPPORT 0
-#define PICO_DNS_RA_IS_SUPPORTED 1
-#define PICO_DNS_RCODE_NO_ERROR 0
-#define PICO_DNS_RCODE_EFORMAT 1
-#define PICO_DNS_RCODE_ESERVER 2
-#define PICO_DNS_RCODE_ENAME 3
-#define PICO_DNS_RCODE_ENOIMP 4
-#define PICO_DNS_RCODE_EREFUSED 5
-
-/* QTYPE values */
-#define PICO_DNS_TYPE_A 1
-#define PICO_DNS_TYPE_AAAA 28
-#define PICO_DNS_TYPE_PTR 12
-
-/* QCLASS values */
-#define PICO_DNS_CLASS_IN 1
-
-/* Compression values */
-#define PICO_DNS_LABEL 0
-#define PICO_DNS_POINTER 3
-
-/* Label len */
-#define PICO_DNS_LABEL_INITIAL 1u
-#define PICO_DNS_LABEL_ROOT 1
-
-/* TTL values */
-#define PICO_DNS_MAX_TTL 604800 /* one week */
-
-/* Len of an IPv4 address string */
-#define PICO_DNS_IPV4_ADDR_LEN 16
-#define PICO_DNS_IPV6_ADDR_LEN 54
-
 static void pico_dns_client_callback(uint16_t ev, struct pico_socket *s);
 static void pico_dns_client_retransmission(pico_time now, void *arg);
-
-/* RFC 1035 section 4. MESSAGES */
-PACKED_STRUCT_DEF pico_dns_name
-{
-    char name[1];
-};
-
-/* prefix = header + name pointer
- * flags splitted in 2x uint8 due to endianness */
-PACKED_STRUCT_DEF pico_dns_prefix
-{
-    uint16_t id;
-    uint8_t rd : 1; /* recursion desired  */
-    uint8_t tc : 1; /* truncation  */
-    uint8_t aa : 1; /* authoritative answer  */
-    uint8_t opcode : 4; /* opcode  */
-    uint8_t qr : 1; /* query  */
-    uint8_t rcode : 4; /* response code */
-    uint8_t z : 3; /* zero */
-    uint8_t ra : 1; /* recursion available  */
-    uint16_t qdcount;
-    uint16_t ancount;
-    uint16_t nscount;
-    uint16_t arcount;
-    struct pico_dns_name domain;
-};
-
-PACKED_STRUCT_DEF pico_dns_query_suffix
-{
-    uint16_t qtype;
-    uint16_t qclass;
-};
-
-PACKED_STRUCT_DEF pico_dns_answer_suffix
-{
-    uint16_t qtype;
-    uint16_t qclass;
-    uint32_t ttl;
-    uint16_t rdlength;
-    uint8_t rdata[];
-};
+static int pico_dns_client_getaddr_init(const char *url, uint16_t proto, void (*callback)(char *, void *), void *arg);
 
 struct pico_dns_ns
 {
@@ -127,10 +41,7 @@
 static int dns_ns_cmp(void *ka, void *kb)
 {
     struct pico_dns_ns *a = ka, *b = kb;
-    if (a->ns.addr == b->ns.addr)
-        return 0;
-
-    return (a->ns.addr < b->ns.addr) ? (-1) : (1);
+    return pico_ipv4_compare(&a->ns, &b->ns);
 }
 PICO_TREE_DECLARE(NSTable, dns_ns_cmp);
 
@@ -222,7 +133,7 @@
     return *nxtdns;
 }
 
-static struct pico_dns_query *pico_dns_client_add_query(struct pico_dns_prefix *hdr, uint16_t len, struct pico_dns_query_suffix *suffix,
+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;
@@ -289,21 +200,6 @@
         return NULL;
 }
 
-/* determine len of string */
-static uint16_t pico_dns_client_strlen(const char *url)
-{
-    uint16_t len;
-
-    if (!url)
-        return 0;
-
-    for (len = 0; len < 0xFFFF; len++) {
-        if (url[len] == 0)
-            break;
-    }
-    return len;
-}
-
 /* seek end of string */
 static char *pico_dns_client_seek(char *ptr)
 {
@@ -315,42 +211,6 @@
     return ptr + 1;
 }
 
-/* mirror ip6 */
-
-/* mirror ip address numbers
- * f.e. 192.168.0.1 => 1.0.168.192 */
-static int8_t pico_dns_client_mirror(char *ptr)
-{
-    const unsigned char *addr = NULL;
-    char *m = ptr;
-    uint32_t ip = 0;
-    int8_t i = 0;
-
-    if (pico_string_to_ipv4(ptr, &ip) < 0)
-        return -1;
-
-    ptr = m;
-    addr = (unsigned char *)&ip;
-    for (i = 3; i >= 0; i--) {
-        if (addr[i] > 99) {
-            *ptr++ = (char)('0' + (addr[i] / 100));
-            *ptr++ = (char)('0' + ((addr[i] % 100) / 10));
-            *ptr++ = (char)('0' + ((addr[i] % 100) % 10));
-        } else if(addr[i] > 9) {
-            *ptr++ = (char)('0' + (addr[i] / 10));
-            *ptr++ = (char)('0' + (addr[i] % 10));
-        } else {
-            *ptr++ = (char)('0' + addr[i]);
-        }
-
-        if(i > 0)
-            *ptr++ = '.';
-    }
-    *ptr = '\0';
-
-    return 0;
-}
-
 static struct pico_dns_query *pico_dns_client_idcheck(uint16_t id)
 {
     struct pico_dns_query test = {
@@ -361,7 +221,7 @@
     return pico_tree_findKey(&DNSTable, &test);
 }
 
-static int pico_dns_client_query_prefix(struct pico_dns_prefix *pre)
+static int pico_dns_client_query_header(struct pico_dns_header *hdr)
 {
     uint16_t id = 0;
     uint8_t retry = 32;
@@ -373,73 +233,13 @@
     if (!retry)
         return -1;
 
-    pre->id = short_be(id);
-    pre->qr = PICO_DNS_QR_QUERY;
-    pre->opcode = PICO_DNS_OPCODE_QUERY;
-    pre->aa = PICO_DNS_AA_NO_AUTHORITY;
-    pre->tc = PICO_DNS_TC_NO_TRUNCATION;
-    pre->rd = PICO_DNS_RD_IS_DESIRED;
-    pre->ra = PICO_DNS_RA_NO_SUPPORT;
-    pre->z = 0;
-    pre->rcode = PICO_DNS_RCODE_NO_ERROR;
-    pre->qdcount = short_be(1);
-    pre->ancount = short_be(0);
-    pre->nscount = short_be(0);
-    pre->arcount = short_be(0);
+    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_query_suffix(struct pico_dns_query_suffix *suf, uint16_t type, uint16_t class)
-{
-    suf->qtype = short_be(type);
-    suf->qclass = short_be(class);
-    return 0;
-}
-
-/* replace '.' in the domain name by the label length
- * f.e. www.google.be => 3www6google2be0 */
-static int pico_dns_client_query_domain(char *ptr)
-{
-    char p = 0, *label = NULL;
-    uint8_t len = 0;
-
-    if (!ptr)
-        return -1;
-
-    label = ptr++;
-    while ((p = *ptr++) != 0) {
-        if (p == '.') {
-            *label = (char)len;
-            label = ptr - 1;
-            len = 0;
-        } else {
-            len++;
-        }
-    }
-    *label = (char)len;
-    return 0;
-}
-
-/* replace the label length in the domain name by '.'
- * f.e. 3www6google2be0 => .www.google.be */
-static int pico_dns_client_answer_domain(char *ptr)
-{
-    char p = 0, *label = NULL;
-
-    if (!ptr)
-        return -1;
-
-    label = ptr;
-    while ((p = *ptr++) != 0) {
-        ptr += p;
-        *label = '.';
-        label = ptr;
-    }
-    return 0;
-}
-
-static int pico_dns_client_check_prefix(struct pico_dns_prefix *pre)
+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);
@@ -454,8 +254,11 @@
     return 0;
 }
 
-static int pico_dns_client_check_qsuffix(struct pico_dns_query_suffix *suf, struct pico_dns_query *q)
+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;
@@ -464,52 +267,62 @@
     return 0;
 }
 
-static int pico_dns_client_check_asuffix(struct pico_dns_answer_suffix *suf, struct pico_dns_query *q)
+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->qtype) != q->qtype || short_be(suf->qclass) != q->qclass) {
-        dns_dbg("DNS WARNING: received qtype (%u) or qclass (%u) incorrect\n", short_be(suf->qtype), short_be(suf->qclass));
+    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->ttl) > PICO_DNS_MAX_TTL) {
-        dns_dbg("DNS WARNING: received TTL (%u) > MAX (%u)\n", short_be(suf->ttl), PICO_DNS_MAX_TTL);
+    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_prefix *pre, struct pico_dns_query *q)
+static char *pico_dns_client_seek_suffix(char *suf, struct pico_dns_header *pre, struct pico_dns_query *q)
 {
-    struct pico_dns_answer_suffix *asuffix = NULL;
+    struct pico_dns_record_suffix *asuffix = NULL;
     uint16_t comp = 0, compression = 0;
     uint16_t i = 0;
-    char *psuffix = suf;
+
     if (!suf)
         return NULL;
 
     while (i++ < short_be(pre->ancount)) {
-        comp = short_from(psuffix);
+        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");
-                psuffix += sizeof(uint16_t);
-                comp = short_from(psuffix);
+                suf += sizeof(uint16_t);
+                comp = short_from(suf);
                 compression = short_be(comp);
             }
             break;
 
         case PICO_DNS_LABEL:
             dns_dbg("DNS: label\n");
-            psuffix = pico_dns_client_seek(psuffix);
+            suf = pico_dns_client_seek(suf);
             break;
 
         default:
@@ -517,16 +330,16 @@
             return NULL;
         }
 
-        asuffix = (struct pico_dns_answer_suffix *)psuffix;
+        asuffix = (struct pico_dns_record_suffix *)suf;
         if (!asuffix)
             break;
 
         if (pico_dns_client_check_asuffix(asuffix, q) < 0) {
-            psuffix += (sizeof(struct pico_dns_answer_suffix) + short_be(asuffix->rdlength));
+            suf += (sizeof(struct pico_dns_record_suffix) + short_be(asuffix->rdlength));
             continue;
         }
 
-        return psuffix;
+        return suf;
     }
     return NULL;
 }
@@ -576,7 +389,8 @@
         return;
     }
 
-    if (q->retrans++ <= PICO_DNS_CLIENT_MAX_RETRANS) {
+    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 {
@@ -586,15 +400,16 @@
     }
 }
 
-static int pico_dns_client_user_callback(struct pico_dns_answer_suffix *asuffix, struct pico_dns_query *q)
+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(asuffix->rdata);
+        ip = long_from(rdata);
         str = PICO_ZALLOC(PICO_DNS_IPV4_ADDR_LEN);
         pico_ipv4_to_string(str, ip);
         break;
@@ -602,21 +417,23 @@
     case PICO_DNS_TYPE_AAAA:
     {
         struct pico_ip6 ip6;
-        memcpy(&ip6.addr, asuffix->rdata, sizeof(struct pico_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:
-        pico_dns_client_answer_domain((char *)asuffix->rdata);
-        str = PICO_ZALLOC((size_t)(asuffix->rdlength - PICO_DNS_LABEL_INITIAL));
+        /* 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, asuffix->rdata + PICO_DNS_LABEL_INITIAL, short_be(asuffix->rdlength) - PICO_DNS_LABEL_INITIAL);
+        memcpy(str, rdata + PICO_DNS_LABEL_INITIAL, short_be(asuffix->rdlength) - PICO_DNS_LABEL_INITIAL);
         break;
 
     default:
@@ -627,24 +444,71 @@
     if (q->retrans) {
         q->callback(str, q->arg);
         q->retrans = 0;
-        PICO_FREE(str);
         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_prefix *prefix = NULL;
-    struct pico_dns_name *domain = NULL;
-    struct pico_dns_query_suffix *qsuffix = NULL;
-    struct pico_dns_answer_suffix *asuffix = NULL;
+    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;
-    char msg[PICO_DNS_MAX_RESPONSE_LEN] = {
-        0
-    };
 
     if (ev == PICO_SOCK_EV_ERR) {
         dns_dbg("DNS: socket error received\n");
@@ -652,79 +516,172 @@
     }
 
     if (ev & PICO_SOCK_EV_RD) {
-        if (pico_socket_read(s, msg, PICO_DNS_MAX_RESPONSE_LEN) <= 0)
+        if (pico_socket_read(s, dns_response, PICO_IP_MRU) < 0)
             return;
     }
 
-    prefix = (struct pico_dns_prefix *)msg;
-    domain = &prefix->domain;
-    qsuffix = (struct pico_dns_query_suffix *)pico_dns_client_seek(domain->name);
+    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_prefix(prefix) < 0)
+    if (pico_dns_client_check_header(header) < 0)
         return;
 
-    q = pico_dns_client_find_query(short_be(prefix->id));
+    q = pico_dns_client_find_query(short_be(header->id));
     if (!q)
         return;
 
     if (pico_dns_client_check_qsuffix(qsuffix, q) < 0)
         return;
 
-    p_asuffix = (char *)qsuffix + sizeof(struct pico_dns_query_suffix);
-    p_asuffix = pico_dns_client_seek_suffix(p_asuffix, prefix, q);
-    if (!p_asuffix)
+    if (pico_dns_client_check_url(header, q) < 0)
         return;
 
-    asuffix = (struct pico_dns_answer_suffix *)p_asuffix;
+    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_client_getaddr_init(const char *url, uint16_t proto, void (*callback)(char *, void *), void *arg)
+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 *msg = NULL;
-    struct pico_dns_prefix *prefix = NULL;
-    struct pico_dns_name *domain = NULL;
-    struct pico_dns_query_suffix *qsuffix = NULL;
-    struct pico_dns_query *q = NULL;
-    uint16_t len = 0, lblen = 0, strlen = 0;
-    (void)proto;
+    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;
     }
 
-    strlen = pico_dns_client_strlen(url);
-    lblen = (uint16_t)(PICO_DNS_LABEL_INITIAL + strlen + PICO_DNS_LABEL_ROOT);
-    len = (uint16_t)(sizeof(struct pico_dns_prefix) + lblen + sizeof(struct pico_dns_query_suffix));
-    msg = PICO_ZALLOC(len);
-    if (!msg) {
-        pico_err = PICO_ERR_ENOMEM;
+    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;
     }
 
-    prefix = (struct pico_dns_prefix *)msg;
-    domain = &prefix->domain;
-    qsuffix = (struct pico_dns_query_suffix *)(domain->name + lblen);
-    memcpy(domain->name + PICO_DNS_LABEL_INITIAL, url, strlen);
+    return 0;
+}
 
-    /* assemble dns message */
-    pico_dns_client_query_prefix(prefix);
-    pico_dns_client_query_domain(domain->name);
+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_client_query_suffix(qsuffix, PICO_DNS_TYPE_AAAA, PICO_DNS_CLASS_IN);
+        pico_dns_question_fill_suffix(qsuffix, PICO_DNS_TYPE_AAAA, PICO_DNS_CLASS_IN);
     } else
 #endif
-    pico_dns_client_query_suffix(qsuffix, PICO_DNS_TYPE_A, PICO_DNS_CLASS_IN);
+    pico_dns_question_fill_suffix(qsuffix, PICO_DNS_TYPE_A, PICO_DNS_CLASS_IN);
 
-    q = pico_dns_client_add_query(prefix, len, qsuffix, callback, arg);
+    q = pico_dns_client_add_query(header, len, qsuffix, callback, arg);
     if (!q) {
-        PICO_FREE(msg);
+        PICO_FREE(header);
         return -1;
     }
 
@@ -746,137 +703,46 @@
     return pico_dns_client_getaddr_init(url, PICO_PROTO_IPV6, callback, arg);
 }
 
-int pico_dns_client_getname(const char *ip, void (*callback)(char *, void *), void *arg)
+static int pico_dns_getname_univ(const char *ip, void (*callback)(char *, void *), void *arg, enum pico_dns_arpa arpa)
 {
-    const char *inaddr_arpa = ".in-addr.arpa";
-    char *msg = NULL;
-    struct pico_dns_prefix *prefix = NULL;
-    struct pico_dns_name *domain = NULL;
-    struct pico_dns_query_suffix *qsuffix = NULL;
+    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, strlen = 0, arpalen = 0;
+    uint16_t len = 0, lblen = 0;
 
     if (!ip || !callback) {
         pico_err = PICO_ERR_EINVAL;
         return -1;
     }
 
-    strlen = pico_dns_client_strlen(ip);
-    arpalen = pico_dns_client_strlen(inaddr_arpa);
-    lblen = (uint16_t)(PICO_DNS_LABEL_INITIAL + strlen + arpalen + PICO_DNS_LABEL_ROOT);
-    len = (uint16_t)(sizeof(struct pico_dns_prefix) + lblen + sizeof(struct pico_dns_query_suffix));
-    msg = PICO_ZALLOC(len);
-    if (!msg) {
-        pico_err = PICO_ERR_ENOMEM;
+    if(pico_dns_create_message(&header, &qsuffix, arpa, ip, &lblen, &len) != 0)
         return -1;
-    }
 
-    prefix = (struct pico_dns_prefix *)msg;
-    domain = &prefix->domain;
-    qsuffix = (struct pico_dns_query_suffix *)(prefix->domain.name + lblen);
-    memcpy(domain->name + PICO_DNS_LABEL_INITIAL, ip, strlen);
-    pico_dns_client_mirror(domain->name + PICO_DNS_LABEL_INITIAL);
-    memcpy(domain->name + PICO_DNS_LABEL_INITIAL + strlen, inaddr_arpa, arpalen);
-    /* assemble dns message */
-    pico_dns_client_query_prefix(prefix);
-    pico_dns_client_query_domain(domain->name);
-    pico_dns_client_query_suffix(qsuffix, PICO_DNS_TYPE_PTR, PICO_DNS_CLASS_IN);
-    q = pico_dns_client_add_query(prefix, len, qsuffix, callback, arg);
+    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(msg);
+        PICO_FREE(header);
         return -1;
     }
 
     if (pico_dns_client_send(q) < 0) {
-        pico_dns_client_del_query(q->id); /* frees msg */
+        pico_dns_client_del_query(q->id); /* frees header */
         return -1;
     }
 
     return 0;
 }
 
-
-#ifdef PICO_SUPPORT_IPV6
-#define STRLEN_PTR_IP6 63
-
-static inline char dns_ptr_ip6_nibble_lo(uint8_t byte)
+int pico_dns_client_getname(const char *ip, void (*callback)(char *, void *), void *arg)
 {
-    uint8_t nibble = byte & 0x0f;
-    if (nibble < 10)
-        return (char)(nibble + '0');
-    else
-        return (char)(nibble - 0xa + 'a');
+    return pico_dns_getname_univ(ip, callback, arg, PICO_DNS_ARPA4);
 }
 
-static inline char dns_ptr_ip6_nibble_hi(uint8_t byte)
-{
-    uint8_t nibble = (byte & 0xf0u) >> 4u;
-    if (nibble < 10u)
-        return (char)(nibble + '0');
-    else
-        return (char)(nibble - 0xa + 'a');
-}
-
-static void pico_dns_ipv6_set_ptr(const char *ip, char *dst)
-{
-    struct pico_ip6 ip6;
-    int i, j = 0;
-    pico_string_to_ipv6(ip, ip6.addr);
-    for (i = 15; i >= 0; i--) {
-        dst[j++] = dns_ptr_ip6_nibble_lo(ip6.addr[i]);
-        dst[j++] = '.';
-        dst[j++] = dns_ptr_ip6_nibble_hi(ip6.addr[i]);
-        dst[j++] = '.';
-    }
-}
 
 int pico_dns_client_getname6(const char *ip, void (*callback)(char *, void *), void *arg)
 {
-    const char *inaddr6_arpa = ".IP6.ARPA";
-    char *msg = NULL;
-    struct pico_dns_prefix *prefix = NULL;
-    struct pico_dns_name *domain = NULL;
-    struct pico_dns_query_suffix *qsuffix = NULL;
-    struct pico_dns_query *q = NULL;
-    uint16_t len = 0, lblen = 0, arpalen = 0;
-
-    if (!ip || !callback) {
-        pico_err = PICO_ERR_EINVAL;
-        return -1;
-    }
-
-    arpalen = pico_dns_client_strlen(inaddr6_arpa);
-    lblen = (uint16_t)(PICO_DNS_LABEL_INITIAL + STRLEN_PTR_IP6 + arpalen + PICO_DNS_LABEL_ROOT);
-    len = (uint16_t)(sizeof(struct pico_dns_prefix) + lblen + sizeof(struct pico_dns_query_suffix));
-    msg = PICO_ZALLOC(len);
-    if (!msg) {
-        pico_err = PICO_ERR_ENOMEM;
-        return -1;
-    }
-
-    prefix = (struct pico_dns_prefix *)msg;
-    domain = &prefix->domain;
-    qsuffix = (struct pico_dns_query_suffix *)(prefix->domain.name + lblen);
-    pico_dns_ipv6_set_ptr(ip, domain->name + PICO_DNS_LABEL_INITIAL);
-    memcpy(domain->name + PICO_DNS_LABEL_INITIAL + STRLEN_PTR_IP6, inaddr6_arpa, arpalen);
-    /* assemble dns message */
-    pico_dns_client_query_prefix(prefix);
-    pico_dns_client_query_domain(domain->name);
-    pico_dns_client_query_suffix(qsuffix, PICO_DNS_TYPE_PTR, PICO_DNS_CLASS_IN);
-    q = pico_dns_client_add_query(prefix, len, qsuffix, callback, arg);
-    if (!q) {
-        PICO_FREE(msg);
-        return -1;
-    }
-
-    if (pico_dns_client_send(q) < 0) {
-        pico_dns_client_del_query(q->id); /* frees msg */
-        return -1;
-    }
-
-    return 0;
+    return pico_dns_getname_univ(ip, callback, arg, PICO_DNS_ARPA6);
 }
-#endif
 
 int pico_dns_client_nameserver(struct pico_ip4 *ns, uint8_t flag)
 {
@@ -920,4 +786,15 @@
     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 */
+