joey shelton / LED_Demo

Dependencies:   MAX44000 PWM_Tone_Library nexpaq_mdk

Fork of LED_Demo by Maxim nexpaq

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers nsapi_dns.cpp Source File

nsapi_dns.cpp

00001 /* nsapi_dns.cpp
00002  * Original work Copyright (c) 2013 Henry Leinen (henry[dot]leinen [at] online [dot] de)
00003  * Modified work Copyright (c) 2015 ARM Limited
00004  *
00005  * Licensed under the Apache License, Version 2.0 (the "License");
00006  * you may not use this file except in compliance with the License.
00007  * You may obtain a copy of the License at
00008  *
00009  *     http://www.apache.org/licenses/LICENSE-2.0
00010  *
00011  * Unless required by applicable law or agreed to in writing, software
00012  * distributed under the License is distributed on an "AS IS" BASIS,
00013  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00014  * See the License for the specific language governing permissions and
00015  * limitations under the License.
00016  */
00017 #include "nsapi_dns.h"
00018 #include "network-socket/UDPSocket.h"
00019 #include <string.h>
00020 #include <stdlib.h>
00021 
00022 
00023 // DNS options
00024 #define DNS_BUFFER_SIZE 256 
00025 #define DNS_TIMEOUT 5000
00026 #define DNS_SERVERS_SIZE 5
00027 
00028 nsapi_addr_t dns_servers[DNS_SERVERS_SIZE] = {
00029     {NSAPI_IPv4, {8, 8, 8, 8}},
00030     {NSAPI_IPv4, {209, 244, 0, 3}},
00031     {NSAPI_IPv4, {84, 200, 69, 80}},
00032     {NSAPI_IPv4, {8, 26, 56, 26}},
00033     {NSAPI_IPv4, {208, 67, 222, 222}},
00034 };
00035 
00036 
00037 // DNS server configuration
00038 extern "C" int nsapi_dns_add_server(nsapi_addr_t addr)
00039 {
00040     memmove(&dns_servers[1], &dns_servers[0],
00041             (DNS_SERVERS_SIZE-1)*sizeof(nsapi_addr_t));
00042 
00043     dns_servers[0] = addr;
00044     return 0;
00045 }
00046 
00047 
00048 // DNS packet parsing
00049 static void dns_append_byte(uint8_t **p, uint8_t byte)
00050 {
00051     *(*p)++ = byte;
00052 }
00053 
00054 static void dns_append_word(uint8_t **p, uint16_t word)
00055 {
00056 
00057     dns_append_byte(p, 0xff & (word >> 8));
00058     dns_append_byte(p, 0xff & (word >> 0));
00059 }
00060 
00061 static void dns_append_name(uint8_t **p, const char *name, uint8_t len)
00062 {
00063     dns_append_byte(p, len);
00064     memcpy(*p, name, len);
00065     *p += len;
00066 }
00067 
00068 static uint8_t dns_scan_byte(const uint8_t **p)
00069 {
00070     return *(*p)++;
00071 }
00072 
00073 static uint16_t dns_scan_word(const uint8_t **p)
00074 {
00075     uint16_t a = dns_scan_byte(p);
00076     uint16_t b = dns_scan_byte(p);
00077     return (a << 8) | b;
00078 }
00079 
00080 
00081 static void dns_append_question(uint8_t **p, const char *host, nsapi_version_t version)
00082 {
00083     // fill the header
00084     dns_append_word(p, 1);      // id      = 1
00085     dns_append_word(p, 0x0100); // flags   = recursion required
00086     dns_append_word(p, 1);      // qdcount = 1
00087     dns_append_word(p, 0);      // ancount = 0
00088     dns_append_word(p, 0);      // nscount = 0
00089     dns_append_word(p, 0);      // arcount = 0
00090 
00091     // fill out the question names
00092     while (host[0]) {
00093         size_t label_len = strcspn(host, ".");
00094         dns_append_name(p, host, label_len);
00095         host += label_len + (host[label_len] == '.');
00096     }
00097 
00098     dns_append_byte(p, 0);
00099 
00100     // fill out question footer
00101     if (version == NSAPI_IPv4) {
00102         dns_append_word(p, 1);  // qtype  = ipv4
00103     } else {
00104         dns_append_word(p, 28); // qtype  = ipv6
00105     }
00106     dns_append_word(p, 1);      // qclass = 1
00107 }
00108 
00109 static int dns_scan_response(const uint8_t **p, nsapi_addr_t *addr, unsigned addr_count)
00110 {
00111     // scan header
00112     uint16_t id    = dns_scan_word(p);
00113     uint16_t flags = dns_scan_word(p);
00114     bool    qr     = 0x1 & (flags >> 15);
00115     uint8_t opcode = 0xf & (flags >> 11);
00116     uint8_t rcode  = 0xf & (flags >>  0);
00117 
00118     uint16_t qdcount = dns_scan_word(p); // qdcount
00119     uint16_t ancount = dns_scan_word(p); // ancount
00120     dns_scan_word(p);                    // nscount
00121     dns_scan_word(p);                    // arcount
00122 
00123     // verify header is response to query
00124     if (!(id == 1 && qr && opcode == 0 && rcode == 0)) {
00125         return 0;
00126     }
00127 
00128     // skip questions
00129     for (int i = 0; i < qdcount; i++) {
00130         while (true) {
00131             uint8_t len = dns_scan_byte(p);
00132             if (len == 0) {
00133                 break;
00134             }
00135 
00136             *p += len;
00137         }
00138 
00139         dns_scan_word(p); // qtype
00140         dns_scan_word(p); // qclass
00141     }
00142 
00143     // scan each response
00144     unsigned count = 0;
00145 
00146     for (int i = 0; i < ancount && count < addr_count; i++) {
00147         while (true) {
00148             uint8_t len = dns_scan_byte(p);
00149             if (len == 0) {
00150                 break;
00151             } else if (len & 0xc0) { // this is link
00152                 dns_scan_byte(p);
00153                 break;
00154             }
00155 
00156             *p += len;
00157         }
00158 
00159         uint16_t rtype    = dns_scan_word(p); // rtype
00160         uint16_t rclass   = dns_scan_word(p); // rclass
00161         *p += 4;                              // ttl
00162         uint16_t rdlength = dns_scan_word(p); // rdlength
00163 
00164         if (rtype == 1 && rclass == 1 && rdlength == NSAPI_IPv4_BYTES) {
00165             // accept A record
00166             addr->version = NSAPI_IPv4;
00167             for (int i = 0; i < NSAPI_IPv4_BYTES; i++) {
00168                 addr->bytes[i] = dns_scan_byte(p);
00169             }
00170 
00171             addr += 1;
00172             count += 1;
00173         } else if (rtype == 28 && rclass == 1 && rdlength == NSAPI_IPv6_BYTES) {
00174             // accept AAAA record
00175             addr->version = NSAPI_IPv6;
00176             for (int i = 0; i < NSAPI_IPv6_BYTES; i++) {
00177                 addr->bytes[i] = dns_scan_byte(p);
00178             }
00179 
00180             addr += 1;
00181             count += 1;
00182         } else {
00183             // skip unrecognized records
00184             *p += rdlength;
00185         }
00186     }
00187 
00188     return count;
00189 }
00190 
00191 // core query function
00192 static int nsapi_dns_query_multiple(NetworkStack *stack,
00193         nsapi_addr_t *addr, unsigned addr_count,
00194         const char *host, nsapi_version_t version)
00195 {
00196     // check for valid host name
00197     int host_len = host ? strlen(host) : 0;
00198     if (host_len > 128 || host_len == 0) {
00199         return NSAPI_ERROR_PARAMETER;
00200     }
00201 
00202     // create a udp socket
00203     UDPSocket socket;
00204     int err = socket.open(stack);
00205     if (err) {
00206         return err;
00207     }
00208 
00209     socket.set_timeout(DNS_TIMEOUT);
00210 
00211     // create network packet
00212     uint8_t *packet = (uint8_t *)malloc(DNS_BUFFER_SIZE);
00213     if (!packet) {
00214         return NSAPI_ERROR_NO_MEMORY;
00215     }
00216 
00217     int result = NSAPI_ERROR_DNS_FAILURE;
00218 
00219     // check against each dns server
00220     for (unsigned i = 0; i < DNS_SERVERS_SIZE; i++) {
00221         // send the question
00222         uint8_t *p = packet;
00223         dns_append_question(&p, host, version);
00224 
00225         err = socket.sendto(SocketAddress(dns_servers[i], 53), packet, DNS_BUFFER_SIZE);
00226         if (err == NSAPI_ERROR_WOULD_BLOCK) {
00227             continue;
00228         } else if (err < 0) {
00229             result = err;
00230             break;
00231         }
00232 
00233         // recv the response
00234         err = socket.recvfrom(NULL, packet, DNS_BUFFER_SIZE);
00235         if (err == NSAPI_ERROR_WOULD_BLOCK) {
00236             continue;
00237         } else if (err < 0) {
00238             result = err;
00239             break;
00240         }
00241 
00242         p = packet;
00243         int found = dns_scan_response((const uint8_t **)&p, addr, addr_count);
00244         if (found) {
00245             result = found;
00246             break;
00247         }
00248     }
00249 
00250     // clean up packet
00251     free(packet);
00252 
00253     // clean up udp
00254     err = socket.close();
00255     if (err) {
00256         return err;
00257     }
00258 
00259     // return result
00260     return result;
00261 }
00262 
00263 // convenience functions for other forms of queries
00264 extern "C" int nsapi_dns_query_multiple(nsapi_stack_t *stack,
00265         nsapi_addr_t *addr, unsigned addr_count,
00266         const char *host, nsapi_version_t version)
00267 {
00268     NetworkStack *nstack = nsapi_create_stack(stack);
00269     return nsapi_dns_query_multiple(nstack, addr, addr_count, host, version);
00270 }
00271 
00272 int nsapi_dns_query_multiple(NetworkStack *stack,
00273         SocketAddress *addresses, unsigned addr_count,
00274         const char *host, nsapi_version_t version)
00275 {
00276     nsapi_addr_t *addrs = new nsapi_addr_t[addr_count];
00277     int result = nsapi_dns_query_multiple(stack, addrs, addr_count, host, version);
00278 
00279     if (result > 0) {
00280         for (int i = 0; i < result; i++) {
00281             addresses[i].set_addr(addrs[i]);
00282         }
00283     }
00284 
00285     delete[] addrs;
00286     return result;
00287 }
00288 
00289 extern "C" int nsapi_dns_query(nsapi_stack_t *stack,
00290         nsapi_addr_t *addr, const char *host, nsapi_version_t version)
00291 {
00292     NetworkStack *nstack = nsapi_create_stack(stack);
00293     int result = nsapi_dns_query_multiple(nstack, addr, 1, host, version);
00294     return (result > 0) ? 0 : result;
00295 }
00296 
00297 int nsapi_dns_query(NetworkStack *stack,
00298         SocketAddress *address, const char *host, nsapi_version_t version)
00299 {
00300     nsapi_addr_t addr;
00301     int result = nsapi_dns_query_multiple(stack, &addr, 1, host, version);
00302     address->set_addr(addr);
00303     return (result > 0) ? 0 : result;
00304 }