Lee Kai Xuan / mbed-os

Fork of mbed-os by erkin yucel

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}},
00035     {NSAPI_IPv4 , {209, 244, 0, 3}},
00036     {NSAPI_IPv4 , {84, 200, 69, 80}},
00037     {NSAPI_IPv4 , {8, 26, 56, 26}},
00038     {NSAPI_IPv4 , {208, 67, 222, 222}},
00039 };
00040 
00041 // DNS server configuration
00042 extern "C" int nsapi_dns_add_server(nsapi_addr_t addr)
00043 {
00044     memmove(&dns_servers[1], &dns_servers[0],
00045             (DNS_SERVERS_SIZE-1)*sizeof(nsapi_addr_t));
00046 
00047     dns_servers[0] = addr;
00048     return 0;
00049 }
00050 
00051 
00052 // DNS packet parsing
00053 static void dns_append_byte(uint8_t **p, uint8_t byte)
00054 {
00055     *(*p)++ = byte;
00056 }
00057 
00058 static void dns_append_word(uint8_t **p, uint16_t word)
00059 {
00060 
00061     dns_append_byte(p, 0xff & (word >> 8));
00062     dns_append_byte(p, 0xff & (word >> 0));
00063 }
00064 
00065 static void dns_append_name(uint8_t **p, const char *name, uint8_t len)
00066 {
00067     dns_append_byte(p, len);
00068     memcpy(*p, name, len);
00069     *p += len;
00070 }
00071 
00072 static uint8_t dns_scan_byte(const uint8_t **p)
00073 {
00074     return *(*p)++;
00075 }
00076 
00077 static uint16_t dns_scan_word(const uint8_t **p)
00078 {
00079     uint16_t a = dns_scan_byte(p);
00080     uint16_t b = dns_scan_byte(p);
00081     return (a << 8) | b;
00082 }
00083 
00084 
00085 static void dns_append_question(uint8_t **p, const char *host, nsapi_version_t version)
00086 {
00087     // fill the header
00088     dns_append_word(p, 1);      // id      = 1
00089     dns_append_word(p, 0x0100); // flags   = recursion required
00090     dns_append_word(p, 1);      // qdcount = 1
00091     dns_append_word(p, 0);      // ancount = 0
00092     dns_append_word(p, 0);      // nscount = 0
00093     dns_append_word(p, 0);      // arcount = 0
00094 
00095     // fill out the question names
00096     while (host[0]) {
00097         size_t label_len = strcspn(host, ".");
00098         dns_append_name(p, host, label_len);
00099         host += label_len + (host[label_len] == '.');
00100     }
00101 
00102     dns_append_byte(p, 0);
00103 
00104     // fill out question footer
00105     if (version == NSAPI_IPv4 ) {
00106         dns_append_word(p, RR_A);       // qtype  = ipv4
00107     } else {
00108         dns_append_word(p, RR_AAAA);    // qtype  = ipv6
00109     }
00110     dns_append_word(p, CLASS_IN);
00111 }
00112 
00113 static int dns_scan_response(const uint8_t **p, nsapi_addr_t *addr, unsigned addr_count)
00114 {
00115     // scan header
00116     uint16_t id    = dns_scan_word(p);
00117     uint16_t flags = dns_scan_word(p);
00118     bool    qr     = 0x1 & (flags >> 15);
00119     uint8_t opcode = 0xf & (flags >> 11);
00120     uint8_t rcode  = 0xf & (flags >>  0);
00121 
00122     uint16_t qdcount = dns_scan_word(p); // qdcount
00123     uint16_t ancount = dns_scan_word(p); // ancount
00124     dns_scan_word(p);                    // nscount
00125     dns_scan_word(p);                    // arcount
00126 
00127     // verify header is response to query
00128     if (!(id == 1 && qr && opcode == 0 && rcode == 0)) {
00129         return 0;
00130     }
00131 
00132     // skip questions
00133     for (int i = 0; i < qdcount; i++) {
00134         while (true) {
00135             uint8_t len = dns_scan_byte(p);
00136             if (len == 0) {
00137                 break;
00138             }
00139 
00140             *p += len;
00141         }
00142 
00143         dns_scan_word(p); // qtype
00144         dns_scan_word(p); // qclass
00145     }
00146 
00147     // scan each response
00148     unsigned count = 0;
00149 
00150     for (int i = 0; i < ancount && count < addr_count; i++) {
00151         while (true) {
00152             uint8_t len = dns_scan_byte(p);
00153             if (len == 0) {
00154                 break;
00155             } else if (len & 0xc0) { // this is link
00156                 dns_scan_byte(p);
00157                 break;
00158             }
00159 
00160             *p += len;
00161         }
00162 
00163         uint16_t rtype    = dns_scan_word(p); // rtype
00164         uint16_t rclass   = dns_scan_word(p); // rclass
00165         *p += 4;                              // ttl
00166         uint16_t rdlength = dns_scan_word(p); // rdlength
00167 
00168         if (rtype == RR_A && rclass == CLASS_IN && rdlength == NSAPI_IPv4_BYTES) {
00169             // accept A record
00170             addr->version = NSAPI_IPv4 ;
00171             for (int i = 0; i < NSAPI_IPv4_BYTES; i++) {
00172                 addr->bytes[i] = dns_scan_byte(p);
00173             }
00174 
00175             addr += 1;
00176             count += 1;
00177         } else if (rtype == RR_AAAA && rclass == CLASS_IN && rdlength == NSAPI_IPv6_BYTES) {
00178             // accept AAAA record
00179             addr->version = NSAPI_IPv6 ;
00180             for (int i = 0; i < NSAPI_IPv6_BYTES; i++) {
00181                 addr->bytes[i] = dns_scan_byte(p);
00182             }
00183 
00184             addr += 1;
00185             count += 1;
00186         } else {
00187             // skip unrecognized records
00188             *p += rdlength;
00189         }
00190     }
00191 
00192     return count;
00193 }
00194 
00195 // core query function
00196 static int nsapi_dns_query_multiple(NetworkStack *stack, const char *host,
00197         nsapi_addr_t *addr, unsigned addr_count, nsapi_version_t version)
00198 {
00199     // check for valid host name
00200     int host_len = host ? strlen(host) : 0;
00201     if (host_len > 128 || host_len == 0) {
00202         return NSAPI_ERROR_PARAMETER ;
00203     }
00204 
00205     // create a udp socket
00206     UDPSocket socket;
00207     int err = socket.open(stack);
00208     if (err) {
00209         return err;
00210     }
00211 
00212     socket.set_timeout(DNS_TIMEOUT);
00213 
00214     // create network packet
00215     uint8_t *packet = (uint8_t *)malloc(DNS_BUFFER_SIZE);
00216     if (!packet) {
00217         return NSAPI_ERROR_NO_MEMORY ;
00218     }
00219 
00220     int result = NSAPI_ERROR_DNS_FAILURE ;
00221 
00222     // check against each dns server
00223     for (unsigned i = 0; i < DNS_SERVERS_SIZE; i++) {
00224         // send the question
00225         uint8_t *question = packet;
00226         dns_append_question(&question, host, version);
00227 
00228         err = socket.sendto(SocketAddress(dns_servers[i], 53), packet, DNS_BUFFER_SIZE);
00229         if (err == NSAPI_ERROR_WOULD_BLOCK ) {
00230             continue;
00231         } else if (err < 0) {
00232             result = err;
00233             break;
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" int nsapi_dns_query_multiple(nsapi_stack_t *stack, const char *host,
00269         nsapi_addr_t *addr, unsigned 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 int nsapi_dns_query_multiple(NetworkStack *stack, const char *host,
00276         SocketAddress *addresses, unsigned addr_count, nsapi_version_t version)
00277 {
00278     nsapi_addr_t *addrs = new nsapi_addr_t[addr_count];
00279     int 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" int 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     int result = nsapi_dns_query_multiple(nstack, host, addr, 1, version);
00296     return (result > 0) ? 0 : result;
00297 }
00298 
00299 int nsapi_dns_query(NetworkStack *stack, const char *host,
00300         SocketAddress *address, nsapi_version_t version)
00301 {
00302     nsapi_addr_t addr;
00303     int result = nsapi_dns_query_multiple(stack, host, &addr, 1, version);
00304     address->set_addr(addr);
00305     return (result > 0) ? 0 : result;
00306 }