1/*********************************************************************
2   PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
3   See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
4   .
5   Authors: Kristof Roelants
6 *********************************************************************/
7#include "pico_config.h"
8#include "pico_stack.h"
9#include "pico_addressing.h"
10#include "pico_socket.h"
11#include "pico_ipv4.h"
12#include "pico_ipv6.h"
13#include "pico_dns_client.h"
14#include "pico_dns_common.h"
15#include "pico_tree.h"
16
17#ifdef PICO_SUPPORT_DNS_CLIENT
18
19#ifdef PICO_SUPPORT_IPV4
20
21#ifdef DEBUG_DNS
22    #define dns_dbg dbg
23#else
24    #define dns_dbg(...) do {} while(0)
25#endif
26
27/* DNS response length */
28#define PICO_DNS_MAX_QUERY_LEN 255
29#define PICO_DNS_MAX_QUERY_LABEL_LEN 63
30
31/* DNS client retransmission time (msec) + frequency */
32#define PICO_DNS_CLIENT_RETRANS 4000
33#define PICO_DNS_CLIENT_MAX_RETRANS 3
34
35static void pico_dns_client_callback(uint16_t ev, struct pico_socket *s);
36static void pico_dns_client_retransmission(pico_time now, void *arg);
37static int pico_dns_client_getaddr_init(const char *url, uint16_t proto, void (*callback)(char *, void *), void *arg);
38
39struct pico_dns_ns
40{
41    struct pico_ip4 ns; /* nameserver */
42};
43
44static int dns_ns_cmp(void *ka, void *kb)
45{
46    struct pico_dns_ns *a = ka, *b = kb;
47    return pico_ipv4_compare(&a->ns, &b->ns);
48}
49static PICO_TREE_DECLARE(NSTable, dns_ns_cmp);
50
51struct pico_dns_query
52{
53    char *query;
54    uint16_t len;
55    uint16_t id;
56    uint16_t qtype;
57    uint16_t qclass;
58    uint8_t retrans;
59    struct pico_dns_ns q_ns;
60    struct pico_socket *s;
61    void (*callback)(char *, void *);
62    void *arg;
63};
64
65static int dns_query_cmp(void *ka, void *kb)
66{
67    struct pico_dns_query *a = ka, *b = kb;
68    if (a->id == b->id)
69        return 0;
70
71    return (a->id < b->id) ? (-1) : (1);
72}
73static PICO_TREE_DECLARE(DNSTable, dns_query_cmp);
74
75static int pico_dns_client_del_ns(struct pico_ip4 *ns_addr)
76{
77    struct pico_dns_ns test = {{0}}, *found = NULL;
78
79    test.ns = *ns_addr;
80    found = pico_tree_findKey(&NSTable, &test);
81    if (!found)
82        return -1;
83
84    pico_tree_delete(&NSTable, found);
85    PICO_FREE(found);
86
87    /* no NS left, add default NS */
88    if (pico_tree_empty(&NSTable))
89        pico_dns_client_init();
90
91    return 0;
92}
93
94static struct pico_dns_ns *pico_dns_client_add_ns(struct pico_ip4 *ns_addr)
95{
96    struct pico_dns_ns *dns = NULL, *found = NULL, test = {{0}};
97    struct pico_ip4 zero = {
98        0
99    };                          /* 0.0.0.0 */
100
101    /* Do not add 0.0.0.0 addresses, which some DHCP servers might reply */
102    if (!pico_ipv4_compare(ns_addr, &zero))
103    {
104        pico_err = PICO_ERR_EINVAL;
105        return NULL;
106    }
107
108    dns = PICO_ZALLOC(sizeof(struct pico_dns_ns));
109    if (!dns) {
110        pico_err = PICO_ERR_ENOMEM;
111        return NULL;
112    }
113
114    dns->ns = *ns_addr;
115
116    found = pico_tree_insert(&NSTable, dns);
117    if (found) { /* nameserver already present or out of memory */
118        PICO_FREE(dns);
119        if ((void *)found == (void *)&LEAF)
120            return NULL;
121        else
122            return found;
123    }
124
125    /* default NS found, remove it */
126    pico_string_to_ipv4(PICO_DNS_NS_DEFAULT, (uint32_t *)&test.ns.addr);
127    found = pico_tree_findKey(&NSTable, &test);
128    if (found && (found->ns.addr != ns_addr->addr))
129        pico_dns_client_del_ns(&found->ns);
130
131    return dns;
132}
133
134static struct pico_dns_ns pico_dns_client_next_ns(struct pico_ip4 *ns_addr)
135{
136    struct pico_dns_ns dns = {{0}}, *nxtdns = NULL;
137    struct pico_tree_node *node = NULL, *nxtnode = NULL;
138
139    dns.ns = *ns_addr;
140    node = pico_tree_findNode(&NSTable, &dns);
141    if (!node)
142        return dns; /* keep using current NS */
143
144    nxtnode = pico_tree_next(node);
145    nxtdns = nxtnode->keyValue;
146    if (!nxtdns)
147        nxtdns = (struct pico_dns_ns *)pico_tree_first(&NSTable);
148
149    return *nxtdns;
150}
151
152static struct pico_dns_query *pico_dns_client_add_query(struct pico_dns_header *hdr, uint16_t len, struct pico_dns_question_suffix *suffix,
153                                                        void (*callback)(char *, void *), void *arg)
154{
155    struct pico_dns_query *q = NULL, *found = NULL;
156
157    q = PICO_ZALLOC(sizeof(struct pico_dns_query));
158    if (!q)
159        return NULL;
160
161    q->query = (char *)hdr;
162    q->len = len;
163    q->id = short_be(hdr->id);
164    q->qtype = short_be(suffix->qtype);
165    q->qclass = short_be(suffix->qclass);
166    q->retrans = 1;
167    q->q_ns = *((struct pico_dns_ns *)pico_tree_first(&NSTable));
168    q->callback = callback;
169    q->arg = arg;
170    q->s = pico_socket_open(PICO_PROTO_IPV4, PICO_PROTO_UDP, &pico_dns_client_callback);
171    if (!q->s) {
172        PICO_FREE(q);
173        return NULL;
174    }
175
176    found = pico_tree_insert(&DNSTable, q);
177    if (found) {
178        if ((void *)found != (void *)&LEAF) /* If found == &LEAF we're out of memory and pico_err is set */
179            pico_err = PICO_ERR_EAGAIN;
180        pico_socket_close(q->s);
181        PICO_FREE(q);
182        return NULL;
183    }
184
185    return q;
186}
187
188static int pico_dns_client_del_query(uint16_t id)
189{
190    struct pico_dns_query test = {
191        0
192    }, *found = NULL;
193
194    test.id = id;
195    found = pico_tree_findKey(&DNSTable, &test);
196    if (!found)
197        return -1;
198
199    PICO_FREE(found->query);
200    pico_socket_close(found->s);
201    pico_tree_delete(&DNSTable, found);
202    PICO_FREE(found);
203    return 0;
204}
205
206static struct pico_dns_query *pico_dns_client_find_query(uint16_t id)
207{
208    struct pico_dns_query test = {
209        0
210    }, *found = NULL;
211
212    test.id = id;
213    found = pico_tree_findKey(&DNSTable, &test);
214    if (found)
215        return found;
216    else
217        return NULL;
218}
219
220/* seek end of string */
221static char *pico_dns_client_seek(char *ptr)
222{
223    if (!ptr)
224        return NULL;
225
226    while (*ptr != 0)
227        ptr++;
228    return ptr + 1;
229}
230
231static struct pico_dns_query *pico_dns_client_idcheck(uint16_t id)
232{
233    struct pico_dns_query test = {
234        0
235    };
236
237    test.id = id;
238    return pico_tree_findKey(&DNSTable, &test);
239}
240
241static int pico_dns_client_query_header(struct pico_dns_header *hdr)
242{
243    uint16_t id = 0;
244    uint8_t retry = 32;
245
246    do {
247        id = (uint16_t)(pico_rand() & 0xFFFFU);
248        dns_dbg("DNS: generated id %u\n", id);
249    } while (retry-- && pico_dns_client_idcheck(id));
250    if (!retry)
251        return -1;
252
253    hdr->id = short_be(id);
254    pico_dns_fill_packet_header(hdr, 1, 0, 0, 0); /* 1 question, 0 answers */
255
256    return 0;
257}
258
259static int pico_dns_client_check_header(struct pico_dns_header *pre)
260{
261    if (pre->qr != PICO_DNS_QR_RESPONSE || pre->opcode != PICO_DNS_OPCODE_QUERY || pre->rcode != PICO_DNS_RCODE_NO_ERROR) {
262        dns_dbg("DNS ERROR: OPCODE %d | TC %d | RCODE %d\n", pre->opcode, pre->tc, pre->rcode);
263        return -1;
264    }
265
266    if (short_be(pre->ancount) < 1) {
267        dns_dbg("DNS ERROR: ancount < 1\n");
268        return -1;
269    }
270
271    return 0;
272}
273
274static int pico_dns_client_check_qsuffix(struct pico_dns_question_suffix *suf, struct pico_dns_query *q)
275{
276    if (!suf)
277        return -1;
278
279    if (short_be(suf->qtype) != q->qtype || short_be(suf->qclass) != q->qclass) {
280        dns_dbg("DNS ERROR: received qtype (%u) or qclass (%u) incorrect\n", short_be(suf->qtype), short_be(suf->qclass));
281        return -1;
282    }
283
284    return 0;
285}
286
287static int pico_dns_client_check_url(struct pico_dns_header *resp, struct pico_dns_query *q)
288{
289    char *recv_name = (char*)(resp) + sizeof(struct pico_dns_header) + PICO_DNS_LABEL_INITIAL;
290    char *exp_name = (char *)(q->query) + sizeof(struct pico_dns_header) + PICO_DNS_LABEL_INITIAL;
291    if (strcasecmp(recv_name,  exp_name) != 0)
292        return -1;
293
294    return 0;
295}
296
297static int pico_dns_client_check_asuffix(struct pico_dns_record_suffix *suf, struct pico_dns_query *q)
298{
299    if (!suf) {
300        pico_err = PICO_ERR_EINVAL;
301        return -1;
302    }
303
304    if (short_be(suf->rtype) != q->qtype || short_be(suf->rclass) != q->qclass) {
305        dns_dbg("DNS WARNING: received qtype (%u) or qclass (%u) incorrect\n", short_be(suf->rtype), short_be(suf->rclass));
306        return -1;
307    }
308
309    if (long_be(suf->rttl) > PICO_DNS_MAX_TTL) {
310        dns_dbg("DNS WARNING: received TTL (%u) > MAX (%u)\n", long_be(suf->rttl), PICO_DNS_MAX_TTL);
311        return -1;
312    }
313
314    return 0;
315}
316
317static char *pico_dns_client_seek_suffix(char *suf, struct pico_dns_header *pre, struct pico_dns_query *q)
318{
319    struct pico_dns_record_suffix *asuffix = NULL;
320    uint16_t comp = 0, compression = 0;
321    uint16_t i = 0;
322
323    if (!suf)
324        return NULL;
325
326    while (i++ < short_be(pre->ancount)) {
327        comp = short_from(suf);
328        compression = short_be(comp);
329        switch (compression >> 14)
330        {
331        case PICO_DNS_POINTER:
332            while (compression >> 14 == PICO_DNS_POINTER) {
333                dns_dbg("DNS: pointer\n");
334                suf += sizeof(uint16_t);
335                comp = short_from(suf);
336                compression = short_be(comp);
337            }
338            break;
339
340        case PICO_DNS_LABEL:
341            dns_dbg("DNS: label\n");
342            suf = pico_dns_client_seek(suf);
343            break;
344
345        default:
346            dns_dbg("DNS ERROR: incorrect compression (%u) value\n", compression);
347            return NULL;
348        }
349
350        asuffix = (struct pico_dns_record_suffix *)suf;
351        if (!asuffix)
352            break;
353
354        if (pico_dns_client_check_asuffix(asuffix, q) < 0) {
355            suf += (sizeof(struct pico_dns_record_suffix) + short_be(asuffix->rdlength));
356            continue;
357        }
358
359        return suf;
360    }
361    return NULL;
362}
363
364static int pico_dns_client_send(struct pico_dns_query *q)
365{
366    uint16_t *paramID = PICO_ZALLOC(sizeof(uint16_t));
367    if (!paramID) {
368        pico_err = PICO_ERR_ENOMEM;
369        return -1;
370    }
371
372    dns_dbg("DNS: sending query to %08X\n", q->q_ns.ns.addr);
373    if (!q->s)
374        goto failure;
375
376    if (pico_socket_connect(q->s, &q->q_ns.ns, short_be(PICO_DNS_NS_PORT)) < 0)
377        goto failure;
378
379    pico_socket_send(q->s, q->query, q->len);
380    *paramID = q->id;
381    if (!pico_timer_add(PICO_DNS_CLIENT_RETRANS, pico_dns_client_retransmission, paramID)) {
382        dns_dbg("DNS: Failed to start retransmission timer\n");
383        goto failure;
384    }
385
386    return 0;
387
388failure:
389    PICO_FREE(paramID);
390    return -1;
391}
392
393static void pico_dns_client_retransmission(pico_time now, void *arg)
394{
395    struct pico_dns_query *q = NULL;
396    struct pico_dns_query dummy;
397    IGNORE_PARAMETER(now);
398
399    if(!arg)
400        return;
401
402    /* search for the dns query and free used space */
403    dummy.id = *(uint16_t *)arg;
404    q = (struct pico_dns_query *)pico_tree_findKey(&DNSTable, &dummy);
405    PICO_FREE(arg);
406
407    /* dns query successful? */
408    if (!q) {
409        return;
410    }
411
412    q->retrans++;
413    if (q->retrans <= PICO_DNS_CLIENT_MAX_RETRANS) {
414        q->q_ns = pico_dns_client_next_ns(&q->q_ns.ns);
415        pico_dns_client_send(q);
416    } else {
417        pico_err = PICO_ERR_EIO;
418        q->callback(NULL, q->arg);
419        pico_dns_client_del_query(q->id);
420    }
421}
422
423static int pico_dns_client_check_rdlength(uint16_t qtype, uint16_t rdlength)
424{
425    switch (qtype)
426    {
427        case PICO_DNS_TYPE_A:
428            if (rdlength != PICO_DNS_RR_A_RDLENGTH)
429                return -1;
430            break;
431#ifdef PICO_SUPPORT_IPV6
432        case PICO_DNS_TYPE_AAAA:
433            if (rdlength != PICO_DNS_RR_AAAA_RDLENGTH)
434                return -1;
435            break;
436#endif
437        default:
438            break;
439    }
440
441    return 0;
442}
443
444static int pico_dns_client_user_callback(struct pico_dns_record_suffix *asuffix, struct pico_dns_query *q)
445{
446    uint32_t ip = 0;
447    char *str = NULL;
448    char *rdata = (char *) asuffix + sizeof(struct pico_dns_record_suffix);
449
450    if (pico_dns_client_check_rdlength(q->qtype, short_be(asuffix->rdlength)) < 0) {
451        dns_dbg("DNS ERROR: Invalid RR rdlength: %u\n", short_be(asuffix->rdlength));
452        return -1;
453    }
454
455    switch (q->qtype)
456    {
457    case PICO_DNS_TYPE_A:
458        ip = long_from(rdata);
459        str = PICO_ZALLOC(PICO_DNS_IPV4_ADDR_LEN);
460        pico_ipv4_to_string(str, ip);
461        break;
462#ifdef PICO_SUPPORT_IPV6
463    case PICO_DNS_TYPE_AAAA:
464    {
465        struct pico_ip6 ip6;
466        memcpy(&ip6.addr, rdata, sizeof(struct pico_ip6));
467        str = PICO_ZALLOC(PICO_DNS_IPV6_ADDR_LEN);
468        pico_ipv6_to_string(str, ip6.addr);
469        break;
470    }
471#endif
472    case PICO_DNS_TYPE_PTR:
473        /* TODO: check for decompression / rdlength vs. decompressed length */
474        pico_dns_notation_to_name(rdata, short_be(asuffix->rdlength));
475        str = PICO_ZALLOC((size_t)(short_be(asuffix->rdlength) -
476                                   PICO_DNS_LABEL_INITIAL));
477        if (!str) {
478            pico_err = PICO_ERR_ENOMEM;
479            return -1;
480        }
481
482        memcpy(str, rdata + PICO_DNS_LABEL_INITIAL, short_be(asuffix->rdlength) - PICO_DNS_LABEL_INITIAL);
483        break;
484
485    default:
486        dns_dbg("DNS ERROR: incorrect qtype (%u)\n", q->qtype);
487        break;
488    }
489
490    if (q->retrans) {
491        q->callback(str, q->arg);
492        q->retrans = 0;
493        pico_dns_client_del_query(q->id);
494    }
495
496    if (str)
497        PICO_FREE(str);
498
499    return 0;
500}
501
502static char dns_response[PICO_IP_MRU] = {
503    0
504};
505
506static void pico_dns_try_fallback_cname(struct pico_dns_query *q, struct pico_dns_header *h, struct pico_dns_question_suffix *qsuffix)
507{
508    uint16_t type = q->qtype;
509    uint16_t proto = PICO_PROTO_IPV4;
510    struct pico_dns_record_suffix *asuffix = NULL;
511    char *p_asuffix = NULL;
512    char *cname_orig = NULL;
513    char *cname = NULL;
514    uint16_t cname_len;
515
516    /* Try to use CNAME only if A or AAAA query is ongoing */
517    if (type != PICO_DNS_TYPE_A && type != PICO_DNS_TYPE_AAAA)
518        return;
519
520    if (type == PICO_DNS_TYPE_AAAA)
521        proto = PICO_PROTO_IPV6;
522
523    q->qtype = PICO_DNS_TYPE_CNAME;
524    p_asuffix = (char *)qsuffix + sizeof(struct pico_dns_question_suffix);
525    p_asuffix = pico_dns_client_seek_suffix(p_asuffix, h, q);
526    if (!p_asuffix) {
527        return;
528    }
529
530    /* Found CNAME response. Re-initiating query. */
531    asuffix = (struct pico_dns_record_suffix *)p_asuffix;
532    cname = pico_dns_decompress_name((char *)asuffix + sizeof(struct pico_dns_record_suffix), (pico_dns_packet *)h); /* allocates memory! */
533    cname_orig = cname; /* to free later */
534
535    if (cname == NULL)
536        return;
537
538    cname_len = (uint16_t)(pico_dns_strlen(cname) + 1);
539
540    pico_dns_notation_to_name(cname, cname_len);
541    if (cname[0] == '.')
542        cname++;
543
544    dns_dbg("Restarting query for name '%s'\n", cname);
545    pico_dns_client_getaddr_init(cname, proto, q->callback, q->arg);
546    PICO_FREE(cname_orig);
547    pico_dns_client_del_query(q->id);
548}
549
550static void pico_dns_client_callback(uint16_t ev, struct pico_socket *s)
551{
552    struct pico_dns_header *header = NULL;
553    char *domain;
554    struct pico_dns_question_suffix *qsuffix = NULL;
555    struct pico_dns_record_suffix *asuffix = NULL;
556    struct pico_dns_query *q = NULL;
557    char *p_asuffix = NULL;
558
559    if (ev == PICO_SOCK_EV_ERR) {
560        dns_dbg("DNS: socket error received\n");
561        return;
562    }
563
564    if (ev & PICO_SOCK_EV_RD) {
565        if (pico_socket_read(s, dns_response, PICO_IP_MRU) < 0)
566            return;
567    }
568
569    header = (struct pico_dns_header *)dns_response;
570    domain = (char *)header + sizeof(struct pico_dns_header);
571    qsuffix = (struct pico_dns_question_suffix *)pico_dns_client_seek(domain);
572    /* valid asuffix is determined dynamically later on */
573
574    if (pico_dns_client_check_header(header) < 0)
575        return;
576
577    q = pico_dns_client_find_query(short_be(header->id));
578    if (!q)
579        return;
580
581    if (pico_dns_client_check_qsuffix(qsuffix, q) < 0)
582        return;
583
584    if (pico_dns_client_check_url(header, q) < 0)
585        return;
586
587    p_asuffix = (char *)qsuffix + sizeof(struct pico_dns_question_suffix);
588    p_asuffix = pico_dns_client_seek_suffix(p_asuffix, header, q);
589    if (!p_asuffix) {
590        pico_dns_try_fallback_cname(q, header, qsuffix);
591        return;
592    }
593
594    asuffix = (struct pico_dns_record_suffix *)p_asuffix;
595    pico_dns_client_user_callback(asuffix, q);
596
597    return;
598}
599
600static 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)
601{
602    char *domain;
603    char inaddr_arpa[14];
604    uint16_t strlen = 0, arpalen = 0;
605
606    if (!url) {
607        pico_err = PICO_ERR_EINVAL;
608        return -1;
609    }
610
611    if(arpa == PICO_DNS_ARPA4) {
612        strcpy(inaddr_arpa, ".in-addr.arpa");
613        strlen = pico_dns_strlen(url);
614    }
615
616#ifdef PICO_SUPPORT_IPV6
617    else if (arpa == PICO_DNS_ARPA6) {
618        strcpy(inaddr_arpa, ".IP6.ARPA");
619        strlen = STRLEN_PTR_IP6;
620    }
621#endif
622    else {
623        strcpy(inaddr_arpa, "");
624        strlen = pico_dns_strlen(url);
625    }
626
627    arpalen = pico_dns_strlen(inaddr_arpa);
628    *urlen = (uint16_t)(PICO_DNS_LABEL_INITIAL + strlen + arpalen + PICO_DNS_LABEL_ROOT);
629    *hdrlen = (uint16_t)(sizeof(struct pico_dns_header) + *urlen + sizeof(struct pico_dns_question_suffix));
630    *header = PICO_ZALLOC(*hdrlen);
631    if (!*header) {
632        pico_err = PICO_ERR_ENOMEM;
633        return -1;
634    }
635
636    *header = (struct pico_dns_header *)*header;
637    domain = (char *) *header + sizeof(struct pico_dns_header);
638    *qsuffix = (struct pico_dns_question_suffix *)(domain + *urlen);
639
640    if(arpa == PICO_DNS_ARPA4) {
641        memcpy(domain + PICO_DNS_LABEL_INITIAL, url, strlen);
642        pico_dns_mirror_addr(domain + PICO_DNS_LABEL_INITIAL);
643        memcpy(domain + PICO_DNS_LABEL_INITIAL + strlen, inaddr_arpa, arpalen);
644    }
645
646#ifdef PICO_SUPPORT_IPV6
647    else if (arpa == PICO_DNS_ARPA6) {
648        pico_dns_ipv6_set_ptr(url, domain + PICO_DNS_LABEL_INITIAL);
649        memcpy(domain + PICO_DNS_LABEL_INITIAL + STRLEN_PTR_IP6, inaddr_arpa, arpalen);
650    }
651#endif
652    else {
653        memcpy(domain + PICO_DNS_LABEL_INITIAL, url, strlen);
654    }
655
656    /* assemble dns message */
657    pico_dns_client_query_header(*header);
658    pico_dns_name_to_dns_notation(domain, strlen);
659
660    return 0;
661}
662
663static int pico_dns_client_addr_label_check_len(const char *url)
664{
665    const char *p, *label;
666    int count;
667    label = url;
668    p = label;
669
670    while(*p != (char) 0) {
671        count = 0;
672        while((*p != (char)0)) {
673            if (*p == '.') {
674                label = ++p;
675                break;
676            }
677
678            count++;
679            p++;
680            if (count > PICO_DNS_MAX_QUERY_LABEL_LEN)
681                return -1;
682        }
683    }
684    return 0;
685}
686
687static int pico_dns_client_getaddr_check(const char *url, void (*callback)(char *, void *))
688{
689    if (!url || !callback) {
690        pico_err = PICO_ERR_EINVAL;
691        return -1;
692    }
693
694    if (strlen(url) > PICO_DNS_MAX_QUERY_LEN) {
695        pico_err = PICO_ERR_EINVAL;
696        return -1;
697    }
698
699    if (pico_dns_client_addr_label_check_len(url) < 0) {
700        pico_err = PICO_ERR_EINVAL;
701        return -1;
702    }
703
704    return 0;
705}
706
707static int pico_dns_client_getaddr_init(const char *url, uint16_t proto, void (*callback)(char *, void *), void *arg)
708{
709    struct pico_dns_header *header = NULL;
710    struct pico_dns_question_suffix *qsuffix = NULL;
711    struct pico_dns_query *q = NULL;
712    uint16_t len = 0, lblen = 0;
713    (void)proto;
714
715    if (pico_dns_client_getaddr_check(url, callback) < 0)
716        return -1;
717
718    if(pico_dns_create_message(&header, &qsuffix, PICO_DNS_NO_ARPA, url, &lblen, &len) != 0)
719        return -1;
720
721#ifdef PICO_SUPPORT_IPV6
722    if (proto == PICO_PROTO_IPV6) {
723        pico_dns_question_fill_suffix(qsuffix, PICO_DNS_TYPE_AAAA, PICO_DNS_CLASS_IN);
724    } else
725#endif
726    pico_dns_question_fill_suffix(qsuffix, PICO_DNS_TYPE_A, PICO_DNS_CLASS_IN);
727
728    q = pico_dns_client_add_query(header, len, qsuffix, callback, arg);
729    if (!q) {
730        PICO_FREE(header);
731        return -1;
732    }
733
734    if (pico_dns_client_send(q) < 0) {
735        pico_dns_client_del_query(q->id); /* frees msg */
736        return -1;
737    }
738
739    return 0;
740}
741
742int pico_dns_client_getaddr(const char *url, void (*callback)(char *, void *), void *arg)
743{
744    return pico_dns_client_getaddr_init(url, PICO_PROTO_IPV4, callback, arg);
745}
746
747int pico_dns_client_getaddr6(const char *url, void (*callback)(char *, void *), void *arg)
748{
749    return pico_dns_client_getaddr_init(url, PICO_PROTO_IPV6, callback, arg);
750}
751
752static int pico_dns_getname_univ(const char *ip, void (*callback)(char *, void *), void *arg, enum pico_dns_arpa arpa)
753{
754    struct pico_dns_header *header = NULL;
755    struct pico_dns_question_suffix *qsuffix = NULL;
756    struct pico_dns_query *q = NULL;
757    uint16_t len = 0, lblen = 0;
758
759    if (!ip || !callback) {
760        pico_err = PICO_ERR_EINVAL;
761        return -1;
762    }
763
764    if(pico_dns_create_message(&header, &qsuffix, arpa, ip, &lblen, &len) != 0)
765        return -1;
766
767    pico_dns_question_fill_suffix(qsuffix, PICO_DNS_TYPE_PTR, PICO_DNS_CLASS_IN);
768    q = pico_dns_client_add_query(header, len, qsuffix, callback, arg);
769    if (!q) {
770        PICO_FREE(header);
771        return -1;
772    }
773
774    if (pico_dns_client_send(q) < 0) {
775        pico_dns_client_del_query(q->id); /* frees header */
776        return -1;
777    }
778
779    return 0;
780}
781
782int pico_dns_client_getname(const char *ip, void (*callback)(char *, void *), void *arg)
783{
784    return pico_dns_getname_univ(ip, callback, arg, PICO_DNS_ARPA4);
785}
786
787
788int pico_dns_client_getname6(const char *ip, void (*callback)(char *, void *), void *arg)
789{
790    return pico_dns_getname_univ(ip, callback, arg, PICO_DNS_ARPA6);
791}
792
793int pico_dns_client_nameserver(struct pico_ip4 *ns, uint8_t flag)
794{
795    if (!ns) {
796        pico_err = PICO_ERR_EINVAL;
797        return -1;
798    }
799
800    switch (flag)
801    {
802    case PICO_DNS_NS_ADD:
803        if (!pico_dns_client_add_ns(ns))
804            return -1;
805
806        break;
807
808    case PICO_DNS_NS_DEL:
809        if (pico_dns_client_del_ns(ns) < 0) {
810            pico_err = PICO_ERR_EINVAL;
811            return -1;
812        }
813
814        break;
815
816    default:
817        pico_err = PICO_ERR_EINVAL;
818        return -1;
819    }
820    return 0;
821}
822
823int pico_dns_client_init(void)
824{
825    struct pico_ip4 default_ns = {
826        0
827    };
828
829    if (pico_string_to_ipv4(PICO_DNS_NS_DEFAULT, (uint32_t *)&default_ns.addr) < 0)
830        return -1;
831
832    return pico_dns_client_nameserver(&default_ns, PICO_DNS_NS_ADD);
833}
834
835#else
836
837int pico_dns_client_init(void)
838{
839    dbg("ERROR Trying to initialize DNS module: IPv4 not supported in this build.\n");
840    return -1;
841}
842#endif /* PICO_SUPPORT_IPV4 */
843
844
845#endif /* PICO_SUPPORT_DNS_CLIENT */
846
847