mbed-os

Fork of mbed-os by erkin yucel

Revision:
0:f269e3021894
diff -r 000000000000 -r f269e3021894 features/netsocket/nsapi_dns.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/features/netsocket/nsapi_dns.cpp	Sun Oct 23 15:10:02 2016 +0000
@@ -0,0 +1,306 @@
+/* nsapi_dns.cpp
+ * Original work Copyright (c) 2013 Henry Leinen (henry[dot]leinen [at] online [dot] de)
+ * Modified work Copyright (c) 2015 ARM Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "nsapi_dns.h"
+#include "netsocket/UDPSocket.h"
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#define CLASS_IN 1
+
+#define RR_A 1
+#define RR_AAAA 28
+
+// DNS options
+#define DNS_BUFFER_SIZE 512
+#define DNS_TIMEOUT 5000
+#define DNS_SERVERS_SIZE 5
+
+nsapi_addr_t dns_servers[DNS_SERVERS_SIZE] = {
+    {NSAPI_IPv4, {8, 8, 8, 8}},
+    {NSAPI_IPv4, {209, 244, 0, 3}},
+    {NSAPI_IPv4, {84, 200, 69, 80}},
+    {NSAPI_IPv4, {8, 26, 56, 26}},
+    {NSAPI_IPv4, {208, 67, 222, 222}},
+};
+
+// DNS server configuration
+extern "C" int nsapi_dns_add_server(nsapi_addr_t addr)
+{
+    memmove(&dns_servers[1], &dns_servers[0],
+            (DNS_SERVERS_SIZE-1)*sizeof(nsapi_addr_t));
+
+    dns_servers[0] = addr;
+    return 0;
+}
+
+
+// DNS packet parsing
+static void dns_append_byte(uint8_t **p, uint8_t byte)
+{
+    *(*p)++ = byte;
+}
+
+static void dns_append_word(uint8_t **p, uint16_t word)
+{
+
+    dns_append_byte(p, 0xff & (word >> 8));
+    dns_append_byte(p, 0xff & (word >> 0));
+}
+
+static void dns_append_name(uint8_t **p, const char *name, uint8_t len)
+{
+    dns_append_byte(p, len);
+    memcpy(*p, name, len);
+    *p += len;
+}
+
+static uint8_t dns_scan_byte(const uint8_t **p)
+{
+    return *(*p)++;
+}
+
+static uint16_t dns_scan_word(const uint8_t **p)
+{
+    uint16_t a = dns_scan_byte(p);
+    uint16_t b = dns_scan_byte(p);
+    return (a << 8) | b;
+}
+
+
+static void dns_append_question(uint8_t **p, const char *host, nsapi_version_t version)
+{
+    // fill the header
+    dns_append_word(p, 1);      // id      = 1
+    dns_append_word(p, 0x0100); // flags   = recursion required
+    dns_append_word(p, 1);      // qdcount = 1
+    dns_append_word(p, 0);      // ancount = 0
+    dns_append_word(p, 0);      // nscount = 0
+    dns_append_word(p, 0);      // arcount = 0
+
+    // fill out the question names
+    while (host[0]) {
+        size_t label_len = strcspn(host, ".");
+        dns_append_name(p, host, label_len);
+        host += label_len + (host[label_len] == '.');
+    }
+
+    dns_append_byte(p, 0);
+
+    // fill out question footer
+    if (version == NSAPI_IPv4) {
+        dns_append_word(p, RR_A);       // qtype  = ipv4
+    } else {
+        dns_append_word(p, RR_AAAA);    // qtype  = ipv6
+    }
+    dns_append_word(p, CLASS_IN);
+}
+
+static int dns_scan_response(const uint8_t **p, nsapi_addr_t *addr, unsigned addr_count)
+{
+    // scan header
+    uint16_t id    = dns_scan_word(p);
+    uint16_t flags = dns_scan_word(p);
+    bool    qr     = 0x1 & (flags >> 15);
+    uint8_t opcode = 0xf & (flags >> 11);
+    uint8_t rcode  = 0xf & (flags >>  0);
+
+    uint16_t qdcount = dns_scan_word(p); // qdcount
+    uint16_t ancount = dns_scan_word(p); // ancount
+    dns_scan_word(p);                    // nscount
+    dns_scan_word(p);                    // arcount
+
+    // verify header is response to query
+    if (!(id == 1 && qr && opcode == 0 && rcode == 0)) {
+        return 0;
+    }
+
+    // skip questions
+    for (int i = 0; i < qdcount; i++) {
+        while (true) {
+            uint8_t len = dns_scan_byte(p);
+            if (len == 0) {
+                break;
+            }
+
+            *p += len;
+        }
+
+        dns_scan_word(p); // qtype
+        dns_scan_word(p); // qclass
+    }
+
+    // scan each response
+    unsigned count = 0;
+
+    for (int i = 0; i < ancount && count < addr_count; i++) {
+        while (true) {
+            uint8_t len = dns_scan_byte(p);
+            if (len == 0) {
+                break;
+            } else if (len & 0xc0) { // this is link
+                dns_scan_byte(p);
+                break;
+            }
+
+            *p += len;
+        }
+
+        uint16_t rtype    = dns_scan_word(p); // rtype
+        uint16_t rclass   = dns_scan_word(p); // rclass
+        *p += 4;                              // ttl
+        uint16_t rdlength = dns_scan_word(p); // rdlength
+
+        if (rtype == RR_A && rclass == CLASS_IN && rdlength == NSAPI_IPv4_BYTES) {
+            // accept A record
+            addr->version = NSAPI_IPv4;
+            for (int i = 0; i < NSAPI_IPv4_BYTES; i++) {
+                addr->bytes[i] = dns_scan_byte(p);
+            }
+
+            addr += 1;
+            count += 1;
+        } else if (rtype == RR_AAAA && rclass == CLASS_IN && rdlength == NSAPI_IPv6_BYTES) {
+            // accept AAAA record
+            addr->version = NSAPI_IPv6;
+            for (int i = 0; i < NSAPI_IPv6_BYTES; i++) {
+                addr->bytes[i] = dns_scan_byte(p);
+            }
+
+            addr += 1;
+            count += 1;
+        } else {
+            // skip unrecognized records
+            *p += rdlength;
+        }
+    }
+
+    return count;
+}
+
+// core query function
+static int nsapi_dns_query_multiple(NetworkStack *stack, const char *host,
+        nsapi_addr_t *addr, unsigned addr_count, nsapi_version_t version)
+{
+    // check for valid host name
+    int host_len = host ? strlen(host) : 0;
+    if (host_len > 128 || host_len == 0) {
+        return NSAPI_ERROR_PARAMETER;
+    }
+
+    // create a udp socket
+    UDPSocket socket;
+    int err = socket.open(stack);
+    if (err) {
+        return err;
+    }
+
+    socket.set_timeout(DNS_TIMEOUT);
+
+    // create network packet
+    uint8_t *packet = (uint8_t *)malloc(DNS_BUFFER_SIZE);
+    if (!packet) {
+        return NSAPI_ERROR_NO_MEMORY;
+    }
+
+    int result = NSAPI_ERROR_DNS_FAILURE;
+
+    // check against each dns server
+    for (unsigned i = 0; i < DNS_SERVERS_SIZE; i++) {
+        // send the question
+        uint8_t *question = packet;
+        dns_append_question(&question, host, version);
+
+        err = socket.sendto(SocketAddress(dns_servers[i], 53), packet, DNS_BUFFER_SIZE);
+        if (err == NSAPI_ERROR_WOULD_BLOCK) {
+            continue;
+        } else if (err < 0) {
+            result = err;
+            break;
+        }
+
+        // recv the response
+        err = socket.recvfrom(NULL, packet, DNS_BUFFER_SIZE);
+        if (err == NSAPI_ERROR_WOULD_BLOCK) {
+            continue;
+        } else if (err < 0) {
+            result = err;
+            break;
+        }
+
+        const uint8_t *response = packet;
+        if (dns_scan_response(&response, addr, addr_count) > 0) {
+            result = NSAPI_ERROR_OK;
+        }
+
+        /* The DNS response is final, no need to check other servers */
+        break;
+    }
+
+    // clean up packet
+    free(packet);
+
+    // clean up udp
+    err = socket.close();
+    if (err) {
+        return err;
+    }
+
+    // return result
+    return result;
+}
+
+// convenience functions for other forms of queries
+extern "C" int nsapi_dns_query_multiple(nsapi_stack_t *stack, const char *host,
+        nsapi_addr_t *addr, unsigned addr_count, nsapi_version_t version)
+{
+    NetworkStack *nstack = nsapi_create_stack(stack);
+    return nsapi_dns_query_multiple(nstack, host, addr, addr_count, version);
+}
+
+int nsapi_dns_query_multiple(NetworkStack *stack, const char *host,
+        SocketAddress *addresses, unsigned addr_count, nsapi_version_t version)
+{
+    nsapi_addr_t *addrs = new nsapi_addr_t[addr_count];
+    int result = nsapi_dns_query_multiple(stack, host, addrs, addr_count, version);
+
+    if (result > 0) {
+        for (int i = 0; i < result; i++) {
+            addresses[i].set_addr(addrs[i]);
+        }
+    }
+
+    delete[] addrs;
+    return result;
+}
+
+extern "C" int nsapi_dns_query(nsapi_stack_t *stack, const char *host,
+        nsapi_addr_t *addr, nsapi_version_t version)
+{
+    NetworkStack *nstack = nsapi_create_stack(stack);
+    int result = nsapi_dns_query_multiple(nstack, host, addr, 1, version);
+    return (result > 0) ? 0 : result;
+}
+
+int nsapi_dns_query(NetworkStack *stack, const char *host,
+        SocketAddress *address, nsapi_version_t version)
+{
+    nsapi_addr_t addr;
+    int result = nsapi_dns_query_multiple(stack, host, &addr, 1, version);
+    address->set_addr(addr);
+    return (result > 0) ? 0 : result;
+}