Rtos API example

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 "netsocket/UDPSocket.h"
00019 #include <string.h>
00020 #include <stdlib.h>
00021 #include <stdio.h>
00022 
00023 #define CLASS_IN 1
00024 
00025 #define RR_A 1
00026 #define RR_AAAA 28
00027 
00028 // DNS options
00029 #define DNS_BUFFER_SIZE 512
00030 #define DNS_TIMEOUT 5000
00031 #define DNS_SERVERS_SIZE 5
00032 
00033 nsapi_addr_t dns_servers[DNS_SERVERS_SIZE] = {
00034     {NSAPI_IPv4 , {8, 8, 8, 8}},                             // Google
00035     {NSAPI_IPv4 , {209, 244, 0, 3}},                         // Level 3
00036     {NSAPI_IPv4 , {84, 200, 69, 80}},                        // DNS.WATCH
00037     {NSAPI_IPv6 , {0x20,0x01, 0x48,0x60, 0x48,0x60, 0,0,     // Google
00038                   0,0, 0,0, 0,0, 0x88,0x88}},
00039     {NSAPI_IPv6 , {0x20,0x01, 0x16,0x08, 0,0x10, 0,0x25,     // DNS.WATCH
00040                   0,0, 0,0, 0x1c,0x04, 0xb1,0x2f}},
00041 };
00042 
00043 // DNS server configuration
00044 extern "C" nsapi_error_t nsapi_dns_add_server(nsapi_addr_t addr)
00045 {
00046     memmove(&dns_servers[1], &dns_servers[0],
00047             (DNS_SERVERS_SIZE-1)*sizeof(nsapi_addr_t));
00048 
00049     dns_servers[0] = addr;
00050     return NSAPI_ERROR_OK ;
00051 }
00052 
00053 
00054 // DNS packet parsing
00055 static void dns_append_byte(uint8_t **p, uint8_t byte)
00056 {
00057     *(*p)++ = byte;
00058 }
00059 
00060 static void dns_append_word(uint8_t **p, uint16_t word)
00061 {
00062 
00063     dns_append_byte(p, 0xff & (word >> 8));
00064     dns_append_byte(p, 0xff & (word >> 0));
00065 }
00066 
00067 static void dns_append_name(uint8_t **p, const char *name, uint8_t len)
00068 {
00069     dns_append_byte(p, len);
00070     memcpy(*p, name, len);
00071     *p += len;
00072 }
00073 
00074 static uint8_t dns_scan_byte(const uint8_t **p)
00075 {
00076     return *(*p)++;
00077 }
00078 
00079 static uint16_t dns_scan_word(const uint8_t **p)
00080 {
00081     uint16_t a = dns_scan_byte(p);
00082     uint16_t b = dns_scan_byte(p);
00083     return (a << 8) | b;
00084 }
00085 
00086 
00087 static void dns_append_question(uint8_t **p, const char *host, nsapi_version_t version)
00088 {
00089     // fill the header
00090     dns_append_word(p, 1);      // id      = 1
00091     dns_append_word(p, 0x0100); // flags   = recursion required
00092     dns_append_word(p, 1);      // qdcount = 1
00093     dns_append_word(p, 0);      // ancount = 0
00094     dns_append_word(p, 0);      // nscount = 0
00095     dns_append_word(p, 0);      // arcount = 0
00096 
00097     // fill out the question names
00098     while (host[0]) {
00099         size_t label_len = strcspn(host, ".");
00100         dns_append_name(p, host, label_len);
00101         host += label_len + (host[label_len] == '.');
00102     }
00103 
00104     dns_append_byte(p, 0);
00105 
00106     // fill out question footer
00107     if (version != NSAPI_IPv6 ) {
00108         dns_append_word(p, RR_A);       // qtype  = ipv4
00109     } else {
00110         dns_append_word(p, RR_AAAA);    // qtype  = ipv6
00111     }
00112     dns_append_word(p, CLASS_IN);
00113 }
00114 
00115 static int dns_scan_response(const uint8_t **p, nsapi_addr_t *addr, unsigned addr_count)
00116 {
00117     // scan header
00118     uint16_t id    = dns_scan_word(p);
00119     uint16_t flags = dns_scan_word(p);
00120     bool    qr     = 0x1 & (flags >> 15);
00121     uint8_t opcode = 0xf & (flags >> 11);
00122     uint8_t rcode  = 0xf & (flags >>  0);
00123 
00124     uint16_t qdcount = dns_scan_word(p); // qdcount
00125     uint16_t ancount = dns_scan_word(p); // ancount
00126     dns_scan_word(p);                    // nscount
00127     dns_scan_word(p);                    // arcount
00128 
00129     // verify header is response to query
00130     if (!(id == 1 && qr && opcode == 0 && rcode == 0)) {
00131         return 0;
00132     }
00133 
00134     // skip questions
00135     for (int i = 0; i < qdcount; i++) {
00136         while (true) {
00137             uint8_t len = dns_scan_byte(p);
00138             if (len == 0) {
00139                 break;
00140             }
00141 
00142             *p += len;
00143         }
00144 
00145         dns_scan_word(p); // qtype
00146         dns_scan_word(p); // qclass
00147     }
00148 
00149     // scan each response
00150     unsigned count = 0;
00151 
00152     for (int i = 0; i < ancount && count < addr_count; i++) {
00153         while (true) {
00154             uint8_t len = dns_scan_byte(p);
00155             if (len == 0) {
00156                 break;
00157             } else if (len & 0xc0) { // this is link
00158                 dns_scan_byte(p);
00159                 break;
00160             }
00161 
00162             *p += len;
00163         }
00164 
00165         uint16_t rtype    = dns_scan_word(p); // rtype
00166         uint16_t rclass   = dns_scan_word(p); // rclass
00167         *p += 4;                              // ttl
00168         uint16_t rdlength = dns_scan_word(p); // rdlength
00169 
00170         if (rtype == RR_A && rclass == CLASS_IN && rdlength == NSAPI_IPv4_BYTES) {
00171             // accept A record
00172             addr->version = NSAPI_IPv4 ;
00173             for (int i = 0; i < NSAPI_IPv4_BYTES; i++) {
00174                 addr->bytes[i] = dns_scan_byte(p);
00175             }
00176 
00177             addr += 1;
00178             count += 1;
00179         } else if (rtype == RR_AAAA && rclass == CLASS_IN && rdlength == NSAPI_IPv6_BYTES) {
00180             // accept AAAA record
00181             addr->version = NSAPI_IPv6 ;
00182             for (int i = 0; i < NSAPI_IPv6_BYTES; i++) {
00183                 addr->bytes[i] = dns_scan_byte(p);
00184             }
00185 
00186             addr += 1;
00187             count += 1;
00188         } else {
00189             // skip unrecognized records
00190             *p += rdlength;
00191         }
00192     }
00193 
00194     return count;
00195 }
00196 
00197 // core query function
00198 static nsapi_size_or_error_t nsapi_dns_query_multiple(NetworkStack *stack, const char *host,
00199         nsapi_addr_t *addr, unsigned addr_count, nsapi_version_t version)
00200 {
00201     // check for valid host name
00202     int host_len = host ? strlen(host) : 0;
00203     if (host_len > 128 || host_len == 0) {
00204         return NSAPI_ERROR_PARAMETER ;
00205     }
00206 
00207     // create a udp socket
00208     UDPSocket socket;
00209     int err = socket.open(stack);
00210     if (err) {
00211         return err;
00212     }
00213 
00214     socket.set_timeout(DNS_TIMEOUT);
00215 
00216     // create network packet
00217     uint8_t *packet = (uint8_t *)malloc(DNS_BUFFER_SIZE);
00218     if (!packet) {
00219         return NSAPI_ERROR_NO_MEMORY ;
00220     }
00221 
00222     nsapi_size_or_error_t result = NSAPI_ERROR_DNS_FAILURE ;
00223 
00224     // check against each dns server
00225     for (unsigned i = 0; i < DNS_SERVERS_SIZE; i++) {
00226         // send the question
00227         uint8_t *question = packet;
00228         dns_append_question(&question, host, version);
00229 
00230         err = socket.sendto(SocketAddress(dns_servers[i], 53), packet, question - packet);
00231         // send may fail for various reasons, including wrong address type - move on
00232         if (err < 0) {
00233             continue;
00234         }
00235 
00236         // recv the response
00237         err = socket.recvfrom(NULL, packet, DNS_BUFFER_SIZE);
00238         if (err == NSAPI_ERROR_WOULD_BLOCK ) {
00239             continue;
00240         } else if (err < 0) {
00241             result = err;
00242             break;
00243         }
00244 
00245         const uint8_t *response = packet;
00246         if (dns_scan_response(&response, addr, addr_count) > 0) {
00247             result = NSAPI_ERROR_OK ;
00248         }
00249 
00250         /* The DNS response is final, no need to check other servers */
00251         break;
00252     }
00253 
00254     // clean up packet
00255     free(packet);
00256 
00257     // clean up udp
00258     err = socket.close();
00259     if (err) {
00260         return err;
00261     }
00262 
00263     // return result
00264     return result;
00265 }
00266 
00267 // convenience functions for other forms of queries
00268 extern "C" nsapi_size_or_error_t nsapi_dns_query_multiple(nsapi_stack_t *stack, const char *host,
00269         nsapi_addr_t *addr, nsapi_size_t addr_count, nsapi_version_t version)
00270 {
00271     NetworkStack *nstack = nsapi_create_stack(stack);
00272     return nsapi_dns_query_multiple(nstack, host, addr, addr_count, version);
00273 }
00274 
00275 nsapi_size_or_error_t nsapi_dns_query_multiple(NetworkStack *stack, const char *host,
00276         SocketAddress *addresses, nsapi_size_t addr_count, nsapi_version_t version)
00277 {
00278     nsapi_addr_t *addrs = new nsapi_addr_t[addr_count];
00279     nsapi_size_or_error_t result = nsapi_dns_query_multiple(stack, host, addrs, addr_count, version);
00280 
00281     if (result > 0) {
00282         for (int i = 0; i < result; i++) {
00283             addresses[i].set_addr(addrs[i]);
00284         }
00285     }
00286 
00287     delete[] addrs;
00288     return result;
00289 }
00290 
00291 extern "C" nsapi_error_t nsapi_dns_query(nsapi_stack_t *stack, const char *host,
00292         nsapi_addr_t *addr, nsapi_version_t version)
00293 {
00294     NetworkStack *nstack = nsapi_create_stack(stack);
00295     nsapi_size_or_error_t result = nsapi_dns_query_multiple(nstack, host, addr, 1, version);
00296     return (nsapi_error_t)((result > 0) ? 0 : result);
00297 }
00298 
00299 nsapi_error_t nsapi_dns_query(NetworkStack *stack, const char *host,
00300         SocketAddress *address, nsapi_version_t version)
00301 {
00302     nsapi_addr_t addr;
00303     nsapi_size_or_error_t result = nsapi_dns_query_multiple(stack, host, &addr, 1, version);
00304     address->set_addr(addr);
00305     return (nsapi_error_t)((result > 0) ? 0 : result);
00306 }