pico_dns_client.c 22.1 KB
Newer Older
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 10 11
#include "pico_addressing.h"
#include "pico_socket.h"
#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 18

#ifdef PICO_SUPPORT_DNS_CLIENT

19 20
#ifdef PICO_SUPPORT_IPV4

21 22 23 24 25
#ifdef DEBUG_DNS
    #define dns_dbg dbg
#else
    #define dns_dbg(...) do {} while(0)
#endif
26

27
/* DNS response length */
28 29
#define PICO_DNS_MAX_QUERY_LEN 255
#define PICO_DNS_MAX_QUERY_LABEL_LEN 63
30

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

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

39 40
struct pico_dns_ns
{
41
    struct pico_ip4 ns; /* nameserver */
42 43
};

44
static 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
}
Toon Stegen's avatar
Toon Stegen committed
49
static PICO_TREE_DECLARE(NSTable, dns_ns_cmp);
50

51
struct pico_dns_query
52
{
53 54 55 56 57 58 59 60 61 62
    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;
63 64
};

65
static int dns_query_cmp(void *ka, void *kb)
66
{
67 68 69 70
    struct pico_dns_query *a = ka, *b = kb;
    if (a->id == b->id)
        return 0;

Daniele Lacamera's avatar
Daniele Lacamera committed
71
    return (a->id < b->id) ? (-1) : (1);
72
}
Toon Stegen's avatar
Toon Stegen committed
73
static PICO_TREE_DECLARE(DNSTable, dns_query_cmp);
74

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

79 80 81 82
    test.ns = *ns_addr;
    found = pico_tree_findKey(&NSTable, &test);
    if (!found)
        return -1;
83

84
    pico_tree_delete(&NSTable, found);
85
    PICO_FREE(found);
86

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

91
    return 0;
92 93 94 95
}

static struct pico_dns_ns *pico_dns_client_add_ns(struct pico_ip4 *ns_addr)
{
96
    struct pico_dns_ns *dns = NULL, *found = NULL, test = {{0}};
danielinux's avatar
danielinux committed
97 98 99
    struct pico_ip4 zero = {
        0
    };                          /* 0.0.0.0 */
100 101 102 103 104

    /* Do not add 0.0.0.0 addresses, which some DHCP servers might reply */
    if (!pico_ipv4_compare(ns_addr, &zero))
    {
        pico_err = PICO_ERR_EINVAL;
danielinux's avatar
danielinux committed
105
        return NULL;
106
    }
107

108
    dns = PICO_ZALLOC(sizeof(struct pico_dns_ns));
109 110 111 112 113 114 115 116
    if (!dns) {
        pico_err = PICO_ERR_ENOMEM;
        return NULL;
    }

    dns->ns = *ns_addr;

    found = pico_tree_insert(&NSTable, dns);
117
    if (found) { /* nameserver already present or out of memory */
118
        PICO_FREE(dns);
119 120 121 122
        if ((void *)found == (void *)&LEAF)
            return NULL;
        else
            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 128 129 130 131
    found = pico_tree_findKey(&NSTable, &test);
    if (found && (found->ns.addr != ns_addr->addr))
        pico_dns_client_del_ns(&found->ns);

    return dns;
132 133
}

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

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

144 145 146 147 148
    nxtnode = pico_tree_next(node);
    nxtdns = nxtnode->keyValue;
    if (!nxtdns)
        nxtdns = (struct pico_dns_ns *)pico_tree_first(&NSTable);

149
    return *nxtdns;
150
}
151

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

157
    q = PICO_ZALLOC(sizeof(struct pico_dns_query));
158 159
    if (!q)
        return NULL;
160

161 162 163 164 165 166 167 168 169 170 171
    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) {
172
        PICO_FREE(q);
173 174
        return NULL;
    }
175

176 177
    found = pico_tree_insert(&DNSTable, q);
    if (found) {
178 179
        if ((void *)found != (void *)&LEAF) /* If found == &LEAF we're out of memory and pico_err is set */
            pico_err = PICO_ERR_EAGAIN;
180
        pico_socket_close(q->s);
181
        PICO_FREE(q);
182 183
        return NULL;
    }
184

185
    return q;
186 187 188 189
}

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

194 195 196 197
    test.id = id;
    found = pico_tree_findKey(&DNSTable, &test);
    if (!found)
        return -1;
198

199
    PICO_FREE(found->query);
200 201
    pico_socket_close(found->s);
    pico_tree_delete(&DNSTable, found);
202
    PICO_FREE(found);
203
    return 0;
204 205
}

206
static struct pico_dns_query *pico_dns_client_find_query(uint16_t id)
207
{
208 209 210
    struct pico_dns_query test = {
        0
    }, *found = NULL;
211

212 213 214 215 216 217
    test.id = id;
    found = pico_tree_findKey(&DNSTable, &test);
    if (found)
        return found;
    else
        return NULL;
218 219 220
}

/* seek end of string */
221
static char *pico_dns_client_seek(char *ptr)
222
{
223 224
    if (!ptr)
        return NULL;
225

226 227
    while (*ptr != 0)
        ptr++;
228
    return ptr + 1;
229 230
}

231
static struct pico_dns_query *pico_dns_client_idcheck(uint16_t id)
232
{
233 234 235
    struct pico_dns_query test = {
        0
    };
236

237 238
    test.id = id;
    return pico_tree_findKey(&DNSTable, &test);
239 240
}

241
static int pico_dns_client_query_header(struct pico_dns_header *hdr)
242
{
243 244
    uint16_t id = 0;
    uint8_t retry = 32;
245

246 247 248 249 250 251 252
    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;

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
}

259
static int pico_dns_client_check_header(struct pico_dns_header *pre)
260
{
261 262 263 264 265 266 267 268 269 270 271
    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;
272
}
273

274
static int pico_dns_client_check_qsuffix(struct pico_dns_question_suffix *suf, struct pico_dns_query *q)
275
{
276 277 278
    if (!suf)
        return -1;

279 280 281 282 283 284
    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;
285 286
}

287 288 289
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;
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 295 296
    return 0;
}

297
static int pico_dns_client_check_asuffix(struct pico_dns_record_suffix *suf, struct pico_dns_query *q)
298
{
299 300 301 302 303
    if (!suf) {
        pico_err = PICO_ERR_EINVAL;
        return -1;
    }

304 305
    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));
306 307 308
        return -1;
    }

309
    if (long_be(suf->rttl) > PICO_DNS_MAX_TTL) {
Laurens Miers's avatar
Laurens Miers committed
310
        dns_dbg("DNS WARNING: received TTL (%u) > MAX (%u)\n", long_be(suf->rttl), PICO_DNS_MAX_TTL);
311 312 313 314
        return -1;
    }

    return 0;
315
}
316

317
static 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 321
    uint16_t comp = 0, compression = 0;
    uint16_t i = 0;
Toon Stegen's avatar
Toon Stegen committed
322

323 324
    if (!suf)
        return NULL;
325 326

    while (i++ < short_be(pre->ancount)) {
Toon Stegen's avatar
Toon Stegen committed
327
        comp = short_from(suf);
328 329 330
        compression = short_be(comp);
        switch (compression >> 14)
        {
331 332 333 334 335 336 337 338
        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;
339

340 341 342 343
        case PICO_DNS_LABEL:
            dns_dbg("DNS: label\n");
            suf = pico_dns_client_seek(suf);
            break;
344

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

350
        asuffix = (struct pico_dns_record_suffix *)suf;
351 352
        if (!asuffix)
            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 357
            continue;
        }
358

Toon Stegen's avatar
Toon Stegen committed
359
        return suf;
360
    }
361
    return NULL;
362
}
363

364 365
static int pico_dns_client_send(struct pico_dns_query *q)
{
366
    uint16_t *paramID = PICO_ZALLOC(sizeof(uint16_t));
367 368 369 370 371
    if (!paramID) {
        pico_err = PICO_ERR_ENOMEM;
        return -1;
    }

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

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

379 380
    pico_socket_send(q->s, q->query, q->len);
    *paramID = q->id;
381 382 383 384
    if (!pico_timer_add(PICO_DNS_CLIENT_RETRANS, pico_dns_client_retransmission, paramID)) {
        dns_dbg("DNS: Failed to start retransmission timer\n");
        goto failure;
    }
385 386

    return 0;
387 388

failure:
389
    PICO_FREE(paramID);
390
    return -1;
391
}
392

393
static void pico_dns_client_retransmission(pico_time now, void *arg)
394
{
395 396 397
    struct pico_dns_query *q = NULL;
    struct pico_dns_query dummy;
    IGNORE_PARAMETER(now);
398

399 400
    if(!arg)
        return;
401

402 403 404
    /* search for the dns query and free used space */
    dummy.id = *(uint16_t *)arg;
    q = (struct pico_dns_query *)pico_tree_findKey(&DNSTable, &dummy);
405
    PICO_FREE(arg);
406 407 408 409 410 411

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

412 413
    q->retrans++;
    if (q->retrans <= PICO_DNS_CLIENT_MAX_RETRANS) {
414 415 416 417 418 419 420
        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);
    }
421 422
}

423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443
static int pico_dns_client_check_rdlength(uint16_t qtype, uint16_t rdlength)
{
    switch (qtype)
    {
        case PICO_DNS_TYPE_A:
            if (rdlength != PICO_DNS_RR_A_RDLENGTH)
                return -1;
            break;
#ifdef PICO_SUPPORT_IPV6
        case PICO_DNS_TYPE_AAAA:
            if (rdlength != PICO_DNS_RR_AAAA_RDLENGTH)
                return -1;
            break;
#endif
        default:
            break;
    }

    return 0;
}

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

450 451 452 453 454
    if (pico_dns_client_check_rdlength(q->qtype, short_be(asuffix->rdlength)) < 0) {
        dns_dbg("DNS ERROR: Invalid RR rdlength: %u\n", short_be(asuffix->rdlength));
        return -1;
    }

455 456
    switch (q->qtype)
    {
457 458 459 460 461
    case PICO_DNS_TYPE_A:
        ip = long_from(rdata);
        str = PICO_ZALLOC(PICO_DNS_IPV4_ADDR_LEN);
        pico_ipv4_to_string(str, ip);
        break;
462
#ifdef PICO_SUPPORT_IPV6
463 464 465 466 467 468 469 470
    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;
    }
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 476
        str = PICO_ZALLOC((size_t)(short_be(asuffix->rdlength) -
                                   PICO_DNS_LABEL_INITIAL));
477 478 479 480
        if (!str) {
            pico_err = PICO_ERR_ENOMEM;
            return -1;
        }
481

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

485 486 487
    default:
        dns_dbg("DNS ERROR: incorrect qtype (%u)\n", q->qtype);
        break;
488 489 490 491 492 493 494 495
    }

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

496 497 498
    if (str)
        PICO_FREE(str);

499
    return 0;
500
}
501

502
static char dns_response[PICO_IP_MRU] = {
Daniele Lacamera's avatar
Daniele Lacamera committed
503
    0
504
};
505

506
static void pico_dns_try_fallback_cname(struct pico_dns_query *q, struct pico_dns_header *h, struct pico_dns_question_suffix *qsuffix)
507 508 509
{
    uint16_t type = q->qtype;
    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 517 518 519 520 521 522 523

    /* 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;
524
    p_asuffix = (char *)qsuffix + sizeof(struct pico_dns_question_suffix);
525 526 527 528
    p_asuffix = pico_dns_client_seek_suffix(p_asuffix, h, q);
    if (!p_asuffix) {
        return;
    }
529

530
    /* Found CNAME response. Re-initiating query. */
531
    asuffix = (struct pico_dns_record_suffix *)p_asuffix;
532 533 534 535 536 537 538 539 540
    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);
541
    if (cname[0] == '.')
Daniele Lacamera's avatar
Daniele Lacamera committed
542
        cname++;
543

544 545
    dns_dbg("Restarting query for name '%s'\n", cname);
    pico_dns_client_getaddr_init(cname, proto, q->callback, q->arg);
546
    PICO_FREE(cname_orig);
547 548
    pico_dns_client_del_query(q->id);
}
Daniele Lacamera's avatar
Daniele Lacamera committed
549

550 551
static void pico_dns_client_callback(uint16_t ev, struct pico_socket *s)
{
552 553
    struct pico_dns_header *header = NULL;
    char *domain;
554 555
    struct pico_dns_question_suffix *qsuffix = NULL;
    struct pico_dns_record_suffix *asuffix = NULL;
556 557 558 559 560 561 562
    struct pico_dns_query *q = NULL;
    char *p_asuffix = NULL;

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

564
    if (ev & PICO_SOCK_EV_RD) {
565
        if (pico_socket_read(s, dns_response, PICO_IP_MRU) < 0)
566 567
            return;
    }
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 579
    if (!q)
        return;
580

581 582 583
    if (pico_dns_client_check_qsuffix(qsuffix, q) < 0)
        return;

584 585 586
    if (pico_dns_client_check_url(header, q) < 0)
        return;

587
    p_asuffix = (char *)qsuffix + sizeof(struct pico_dns_question_suffix);
588
    p_asuffix = pico_dns_client_seek_suffix(p_asuffix, header, q);
589 590
    if (!p_asuffix) {
        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
}

600
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)
601 602 603 604
{
    char *domain;
    char inaddr_arpa[14];
    uint16_t strlen = 0, arpalen = 0;
605

606
    if (!url) {
607 608 609 610
        pico_err = PICO_ERR_EINVAL;
        return -1;
    }

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

616 617
#ifdef PICO_SUPPORT_IPV6
    else if (arpa == PICO_DNS_ARPA6) {
618
        strcpy(inaddr_arpa, ".IP6.ARPA");
Toon Stegen's avatar
Toon Stegen committed
619
        strlen = STRLEN_PTR_IP6;
620 621
    }
#endif
Toon Stegen's avatar
Toon Stegen committed
622
    else {
623
        strcpy(inaddr_arpa, "");
624
        strlen = pico_dns_strlen(url);
Toon Stegen's avatar
Toon Stegen committed
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 631
    *header = PICO_ZALLOC(*hdrlen);
    if (!*header) {
632 633 634 635
        pico_err = PICO_ERR_ENOMEM;
        return -1;
    }

636 637
    *header = (struct pico_dns_header *)*header;
    domain = (char *) *header + sizeof(struct pico_dns_header);
638
    *qsuffix = (struct pico_dns_question_suffix *)(domain + *urlen);
639 640 641

    if(arpa == PICO_DNS_ARPA4) {
        memcpy(domain + PICO_DNS_LABEL_INITIAL, url, strlen);
Devon Kerkhove's avatar
Devon Kerkhove committed
642
        pico_dns_mirror_addr(domain + PICO_DNS_LABEL_INITIAL);
643
        memcpy(domain + PICO_DNS_LABEL_INITIAL + strlen, inaddr_arpa, arpalen);
644
    }
645

646 647
#ifdef PICO_SUPPORT_IPV6
    else if (arpa == PICO_DNS_ARPA6) {
648 649
        pico_dns_ipv6_set_ptr(url, domain + PICO_DNS_LABEL_INITIAL);
        memcpy(domain + PICO_DNS_LABEL_INITIAL + STRLEN_PTR_IP6, inaddr_arpa, arpalen);
650 651 652
    }
#endif
    else {
653 654
        memcpy(domain + PICO_DNS_LABEL_INITIAL, url, strlen);
    }
655 656

    /* assemble dns message */
657
    pico_dns_client_query_header(*header);
658
    pico_dns_name_to_dns_notation(domain, strlen);
659 660 661 662

    return 0;
}

663 664 665 666 667 668 669 670 671
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;
TassThomas's avatar
TassThomas committed
672
        while((*p != (char)0)) {
673
            if (*p == '.') {
TassThomas's avatar
TassThomas committed
674 675 676
                label = ++p;
                break;
            }
677

678 679 680 681 682 683 684 685 686 687 688 689 690 691 692
            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;
    }
693

694 695 696 697
    if (strlen(url) > PICO_DNS_MAX_QUERY_LEN) {
        pico_err = PICO_ERR_EINVAL;
        return -1;
    }
698

699 700 701 702
    if (pico_dns_client_addr_label_check_len(url) < 0) {
        pico_err = PICO_ERR_EINVAL;
        return -1;
    }
703

704 705 706
    return 0;
}

707 708 709
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;
710
    struct pico_dns_question_suffix *qsuffix = NULL;
711 712 713 714
    struct pico_dns_query *q = NULL;
    uint16_t len = 0, lblen = 0;
    (void)proto;

715
    if (pico_dns_client_getaddr_check(url, callback) < 0)
716 717 718
        return -1;

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

#ifdef PICO_SUPPORT_IPV6
    if (proto == PICO_PROTO_IPV6) {
723
        pico_dns_question_fill_suffix(qsuffix, PICO_DNS_TYPE_AAAA, PICO_DNS_CLASS_IN);
724 725
    } else
#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 732 733 734 735 736 737 738 739
        return -1;
    }

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

    return 0;
740 741
}

742 743 744 745 746 747 748 749 750 751
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);
}

752
static 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 760 761 762 763
    if (!ip || !callback) {
        pico_err = PICO_ERR_EINVAL;
        return -1;
    }

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 772 773 774
        return -1;
    }

    if (pico_dns_client_send(q) < 0) {
775
        pico_dns_client_del_query(q->id); /* frees header */
776 777
        return -1;
    }
Daniele Lacamera's avatar
Daniele Lacamera committed
778

779 780 781
    return 0;
}

782
int pico_dns_client_getname(const char *ip, void (*callback)(char *, void *), void *arg)
783
{
784 785
    return pico_dns_getname_univ(ip, callback, arg, PICO_DNS_ARPA4);
}
786

787

788 789
int pico_dns_client_getname6(const char *ip, void (*callback)(char *, void *), void *arg)
{
Toon Stegen's avatar
Toon Stegen committed
790
    return pico_dns_getname_univ(ip, callback, arg, PICO_DNS_ARPA6);
791 792 793 794
}

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

800 801
    switch (flag)
    {
802 803 804 805 806 807 808 809
    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) {
810 811
            pico_err = PICO_ERR_EINVAL;
            return -1;
812 813 814 815 816 817 818
        }

        break;

    default:
        pico_err = PICO_ERR_EINVAL;
        return -1;
819 820
    }
    return 0;
821 822
}

823
int pico_dns_client_init(void)
824
{
825 826 827
    struct pico_ip4 default_ns = {
        0
    };
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 837 838 839 840 841 842 843

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 */

844

845
#endif /* PICO_SUPPORT_DNS_CLIENT */
846