Kenji Arai / mbed-os_TYBLE16

Dependents:   TYBLE16_simple_data_logger TYBLE16_MP3_Air

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 
00018 /* Declare __STDC_LIMIT_MACROS so stdint.h defines INT32_MAX when using C++ */
00019 #define __STDC_LIMIT_MACROS
00020 
00021 #include "nsapi_dns.h"
00022 #include "netsocket/UDPSocket.h"
00023 #include <string.h>
00024 #include <stdlib.h>
00025 #include <stdio.h>
00026 #include <stdint.h>
00027 #include "mbed_shared_queues.h"
00028 #include "events/EventQueue.h"
00029 #include "OnboardNetworkStack.h"
00030 #include "Kernel.h"
00031 #include "PlatformMutex.h"
00032 #include "SingletonPtr.h"
00033 
00034 #define CLASS_IN 1
00035 
00036 #define RR_A 1
00037 #define RR_AAAA 28
00038 
00039 // DNS options
00040 #define DNS_BUFFER_SIZE 512
00041 #define DNS_SERVERS_SIZE 5
00042 #define DNS_RESPONSE_MIN_SIZE 12
00043 #define DNS_STACK_SERVERS_NUM 5
00044 #define DNS_QUERY_QUEUE_SIZE 5
00045 #define DNS_HOST_NAME_MAX_LEN 255
00046 #define DNS_TIMER_TIMEOUT 100
00047 
00048 struct DNS_CACHE {
00049     nsapi_addr_t address;
00050     char *host;
00051     uint64_t expires;      /*!< time to live in milliseconds */
00052     uint64_t accessed;     /*!< last accessed */
00053 };
00054 
00055 struct SOCKET_CB_DATA {
00056     call_in_callback_cb_t call_in_cb;
00057     NetworkStack *stack;
00058 };
00059 
00060 enum dns_state {
00061     DNS_CREATED,           /*!< created, not yet making query to network */
00062     DNS_INITIATED,         /*!< making query to network */
00063     DNS_CANCELLED          /*!< cancelled, callback will not be called */
00064 };
00065 
00066 struct DNS_QUERY {
00067     intptr_t unique_id;
00068     nsapi_error_t status;
00069     NetworkStack *stack;
00070     char *host;
00071     const char *interface_name;
00072     NetworkStack::hostbyname_cb_t callback;
00073     call_in_callback_cb_t call_in_cb;
00074     nsapi_size_t addr_count;
00075     nsapi_version_t version;
00076     UDPSocket *socket;
00077     SOCKET_CB_DATA *socket_cb_data;
00078     nsapi_addr_t *addrs;
00079     uint32_t ttl;
00080     uint32_t total_timeout;
00081     uint32_t socket_timeout;
00082     uint16_t dns_message_id;
00083     uint8_t dns_server;
00084     uint8_t retries;
00085     uint8_t total_attempts;
00086     uint8_t send_success;
00087     uint8_t count;
00088     dns_state state;
00089 };
00090 
00091 static void nsapi_dns_cache_add(const char *host, nsapi_addr_t *address, uint32_t ttl);
00092 static nsapi_size_or_error_t nsapi_dns_cache_find(const char *host, nsapi_version_t version, nsapi_addr_t *address);
00093 static void nsapi_dns_cache_reset();
00094 
00095 static nsapi_error_t nsapi_dns_get_server_addr(NetworkStack *stack, uint8_t *index, uint8_t *total_attempts, uint8_t *send_success, SocketAddress *dns_addr, const char *interface_name);
00096 
00097 static void nsapi_dns_query_async_create(void *ptr);
00098 static nsapi_error_t nsapi_dns_query_async_delete(intptr_t unique_id);
00099 static void nsapi_dns_query_async_send(void *ptr);
00100 static void nsapi_dns_query_async_timeout(void);
00101 static void nsapi_dns_query_async_resp(DNS_QUERY *query, nsapi_error_t status, SocketAddress *address);
00102 static void nsapi_dns_query_async_socket_callback(void *ptr);
00103 static void nsapi_dns_query_async_socket_callback_handle(NetworkStack *stack);
00104 static void nsapi_dns_query_async_response(void *ptr);
00105 static void nsapi_dns_query_async_initiate_next(void);
00106 
00107 // *INDENT-OFF*
00108 static nsapi_addr_t dns_servers[DNS_SERVERS_SIZE] = {
00109     {NSAPI_IPv4 , {8, 8, 8, 8}},                             // Google
00110     {NSAPI_IPv6 , {0x20,0x01, 0x48,0x60, 0x48,0x60, 0,0,     // Google
00111                   0,0, 0,0, 0,0, 0x88,0x88}},
00112     {NSAPI_IPv4 , {209, 244, 0, 3}},                         // Level 3
00113     {NSAPI_IPv4 , {84, 200, 69, 80}},                        // DNS.WATCH
00114     {NSAPI_IPv6 , {0x20,0x01, 0x16,0x08, 0,0x10, 0,0x25,     // DNS.WATCH
00115                   0,0, 0,0, 0x1c,0x04, 0xb1,0x2f}},
00116 };
00117 // *INDENT-ON*
00118 
00119 #if (MBED_CONF_NSAPI_DNS_CACHE_SIZE > 0)
00120 static DNS_CACHE *dns_cache[MBED_CONF_NSAPI_DNS_CACHE_SIZE];
00121 // Protects cache shared between blocking and asynchronous calls
00122 static SingletonPtr<PlatformMutex>  dns_cache_mutex;
00123 #endif
00124 
00125 static uint16_t dns_message_id = 1;
00126 static intptr_t dns_unique_id = 1;
00127 static DNS_QUERY *dns_query_queue[DNS_QUERY_QUEUE_SIZE];
00128 // Protects from several threads running asynchronous DNS
00129 static SingletonPtr<PlatformMutex>  dns_mutex;
00130 static SingletonPtr<call_in_callback_cb_t> dns_call_in;
00131 static bool dns_timer_running = false;
00132 
00133 // DNS server configuration
00134 extern "C" nsapi_error_t nsapi_dns_add_server(nsapi_addr_t addr, const char *interface_name)
00135 {
00136     // check if addr was already added
00137     for (int i = 0; i < DNS_SERVERS_SIZE; i++) {
00138         if (memcmp(&addr, &dns_servers[i], sizeof(nsapi_addr_t)) == 0) {
00139             return NSAPI_ERROR_OK ;
00140         }
00141     }
00142 
00143     memmove(&dns_servers[1], &dns_servers[0],
00144             (DNS_SERVERS_SIZE - 1)*sizeof(nsapi_addr_t));
00145 
00146     dns_servers[0] = addr;
00147     return NSAPI_ERROR_OK ;
00148 }
00149 
00150 
00151 // DNS packet parsing
00152 static void dns_append_byte(uint8_t **p, uint8_t byte)
00153 {
00154     *(*p)++ = byte;
00155 }
00156 
00157 static void dns_append_word(uint8_t **p, uint16_t word)
00158 {
00159 
00160     dns_append_byte(p, 0xff & (word >> 8));
00161     dns_append_byte(p, 0xff & (word >> 0));
00162 }
00163 
00164 static void dns_append_name(uint8_t **p, const char *name, uint8_t len)
00165 {
00166     dns_append_byte(p, len);
00167     memcpy(*p, name, len);
00168     *p += len;
00169 }
00170 
00171 static uint8_t dns_scan_byte(const uint8_t **p)
00172 {
00173     return *(*p)++;
00174 }
00175 
00176 static uint16_t dns_scan_word(const uint8_t **p)
00177 {
00178     uint16_t a = dns_scan_byte(p);
00179     uint16_t b = dns_scan_byte(p);
00180     return (a << 8) | b;
00181 }
00182 
00183 static uint32_t dns_scan_word32(const uint8_t **p)
00184 {
00185     uint32_t value = dns_scan_byte(p) << 24;
00186     value |= dns_scan_byte(p) << 16;
00187     value |= dns_scan_byte(p) << 8;
00188     value |= dns_scan_byte(p);
00189 
00190     return value;
00191 }
00192 
00193 static int dns_append_question(uint8_t *ptr, uint16_t id, const char *host, nsapi_version_t version)
00194 {
00195     uint8_t *s_ptr = ptr;
00196     uint8_t **p = &ptr;
00197 
00198     // fill the header
00199     dns_append_word(p, id);     // id      = 1
00200     dns_append_word(p, 0x0100); // flags   = recursion required
00201     dns_append_word(p, 1);      // qdcount = 1
00202     dns_append_word(p, 0);      // ancount = 0
00203     dns_append_word(p, 0);      // nscount = 0
00204     dns_append_word(p, 0);      // arcount = 0
00205 
00206     // fill out the question names
00207     while (host[0]) {
00208         size_t label_len = strcspn(host, ".");
00209         dns_append_name(p, host, label_len);
00210         host += label_len + (host[label_len] == '.');
00211     }
00212 
00213     dns_append_byte(p, 0);
00214 
00215     // fill out question footer
00216     if (version != NSAPI_IPv6 ) {
00217         dns_append_word(p, RR_A);       // qtype  = ipv4
00218     } else {
00219         dns_append_word(p, RR_AAAA);    // qtype  = ipv6
00220     }
00221     dns_append_word(p, CLASS_IN);
00222 
00223     return *p - s_ptr;
00224 }
00225 
00226 static int dns_scan_response(const uint8_t *ptr, uint16_t exp_id, uint32_t *ttl, nsapi_addr_t *addr, unsigned addr_count)
00227 {
00228     const uint8_t **p = &ptr;
00229 
00230     // scan header
00231     uint16_t id    = dns_scan_word(p);
00232     uint16_t flags = dns_scan_word(p);
00233     bool    qr     = 0x1 & (flags >> 15);
00234     uint8_t opcode = 0xf & (flags >> 11);
00235     uint8_t rcode  = 0xf & (flags >>  0);
00236 
00237     uint16_t qdcount = dns_scan_word(p); // qdcount
00238     uint16_t ancount = dns_scan_word(p); // ancount
00239     dns_scan_word(p);                    // nscount
00240     dns_scan_word(p);                    // arcount
00241 
00242     // verify header is response to query
00243     if (!(id == exp_id && qr && opcode == 0)) {
00244         return -1;
00245     }
00246 
00247     if (rcode != 0) {
00248         return 0;
00249     }
00250 
00251     // skip questions
00252     for (int i = 0; i < qdcount; i++) {
00253         while (true) {
00254             uint8_t len = dns_scan_byte(p);
00255             if (len == 0) {
00256                 break;
00257             }
00258 
00259             *p += len;
00260         }
00261 
00262         dns_scan_word(p); // qtype
00263         dns_scan_word(p); // qclass
00264     }
00265 
00266     // scan each response
00267     unsigned count = 0;
00268 
00269     for (int i = 0; i < ancount && count < addr_count; i++) {
00270         while (true) {
00271             uint8_t len = dns_scan_byte(p);
00272             if (len == 0) {
00273                 break;
00274             } else if (len & 0xc0) { // this is link
00275                 dns_scan_byte(p);
00276                 break;
00277             }
00278 
00279             *p += len;
00280         }
00281 
00282         uint16_t rtype    = dns_scan_word(p);    // rtype
00283         uint16_t rclass   = dns_scan_word(p);    // rclass
00284         uint32_t ttl_val  = dns_scan_word32(p);  // ttl
00285         uint16_t rdlength = dns_scan_word(p);    // rdlength
00286 
00287         if (i == 0) {
00288             // Is interested only on first address that is stored to cache
00289             if (ttl_val > INT32_MAX) {
00290                 ttl_val = INT32_MAX;
00291             }
00292             *ttl = ttl_val;
00293         }
00294 
00295         if (rtype == RR_A && rclass == CLASS_IN && rdlength == NSAPI_IPv4_BYTES) {
00296             // accept A record
00297             addr->version = NSAPI_IPv4 ;
00298             for (int i = 0; i < NSAPI_IPv4_BYTES; i++) {
00299                 addr->bytes[i] = dns_scan_byte(p);
00300             }
00301 
00302             addr += 1;
00303             count += 1;
00304         } else if (rtype == RR_AAAA && rclass == CLASS_IN && rdlength == NSAPI_IPv6_BYTES) {
00305             // accept AAAA record
00306             addr->version = NSAPI_IPv6 ;
00307             for (int i = 0; i < NSAPI_IPv6_BYTES; i++) {
00308                 addr->bytes[i] = dns_scan_byte(p);
00309             }
00310 
00311             addr += 1;
00312             count += 1;
00313         } else {
00314             // skip unrecognized records
00315             *p += rdlength;
00316         }
00317     }
00318 
00319     return count;
00320 }
00321 
00322 static void nsapi_dns_cache_add(const char *host, nsapi_addr_t *address, uint32_t ttl)
00323 {
00324 #if (MBED_CONF_NSAPI_DNS_CACHE_SIZE > 0)
00325     // RFC 1034: if TTL is zero, entry is not added to cache
00326     if (ttl == 0) {
00327         return;
00328     }
00329 
00330     // Checks if already cached
00331     if (nsapi_dns_cache_find(host, address->version, NULL) == NSAPI_ERROR_OK ) {
00332         return;
00333     }
00334 
00335     dns_cache_mutex->lock();
00336 
00337     int index = -1;
00338     uint64_t accessed = UINT64_MAX;
00339 
00340     // Finds free or last accessed entry
00341     for (int i = 0; i < MBED_CONF_NSAPI_DNS_CACHE_SIZE; i++) {
00342         if (!dns_cache[i]) {
00343             index = i;
00344             break;
00345         } else if (dns_cache[i]->accessed <= accessed) {
00346             accessed = dns_cache[i]->accessed;
00347             index = i;
00348         }
00349     }
00350 
00351     if (index < 0) {
00352         dns_cache_mutex->unlock();
00353         return;
00354     }
00355 
00356     // Allocates in case entry is free, otherwise reuses
00357     if (!dns_cache[index]) {
00358         dns_cache[index] = new (std::nothrow) DNS_CACHE;
00359     } else {
00360         delete dns_cache[index]->host;
00361     }
00362 
00363     if (dns_cache[index]) {
00364         dns_cache[index]->address = *address;
00365         dns_cache[index]->host = new (std::nothrow) char[strlen(host) + 1];
00366         strcpy(dns_cache[index]->host, host);
00367         uint64_t ms_count = rtos::Kernel::get_ms_count();
00368         dns_cache[index]->expires = ms_count + (uint64_t) ttl * 1000;
00369         dns_cache[index]->accessed = ms_count;
00370     }
00371 
00372     dns_cache_mutex->unlock();
00373 #endif
00374 }
00375 
00376 static nsapi_error_t nsapi_dns_cache_find(const char *host, nsapi_version_t version, nsapi_addr_t *address)
00377 {
00378     nsapi_error_t ret_val = NSAPI_ERROR_NO_ADDRESS ;
00379 
00380 #if (MBED_CONF_NSAPI_DNS_CACHE_SIZE > 0)
00381     dns_cache_mutex->lock();
00382 
00383     for (int i = 0; i < MBED_CONF_NSAPI_DNS_CACHE_SIZE; i++) {
00384         if (dns_cache[i]) {
00385             uint64_t ms_count = rtos::Kernel::get_ms_count();
00386             // Checks all entries for expired entries
00387             if (ms_count > dns_cache[i]->expires) {
00388                 delete dns_cache[i]->host;
00389                 delete dns_cache[i];
00390                 dns_cache[i] = NULL;
00391             } else if ((version == NSAPI_UNSPEC  || version == dns_cache[i]->address.version) &&
00392                        strcmp(dns_cache[i]->host, host) == 0) {
00393                 if (address) {
00394                     *address = dns_cache[i]->address;
00395                 }
00396                 dns_cache[i]->accessed = ms_count;
00397                 ret_val = NSAPI_ERROR_OK ;
00398             }
00399         }
00400     }
00401 
00402     dns_cache_mutex->unlock();
00403 #endif
00404 
00405     return ret_val;
00406 }
00407 
00408 static void nsapi_dns_cache_reset()
00409 {
00410 #if (MBED_CONF_NSAPI_DNS_CACHE_SIZE > 0)
00411     dns_cache_mutex->lock();
00412     for (int i = 0; i < MBED_CONF_NSAPI_DNS_CACHE_SIZE; i++) {
00413         if (dns_cache[i]) {
00414             delete[] dns_cache[i]->host;
00415             dns_cache[i]->host = NULL;
00416             delete dns_cache[i];
00417             dns_cache[i] = NULL;
00418         }
00419     }
00420     dns_cache_mutex->unlock();
00421 #endif
00422 }
00423 
00424 static nsapi_error_t nsapi_dns_get_server_addr(NetworkStack *stack, uint8_t *index, uint8_t *total_attempts, uint8_t *send_success, SocketAddress *dns_addr, const char *interface_name)
00425 {
00426     bool dns_addr_set = false;
00427 
00428     if (*total_attempts == 0) {
00429         return NSAPI_ERROR_NO_ADDRESS ;
00430     }
00431 
00432     if (*index >= DNS_SERVERS_SIZE + DNS_STACK_SERVERS_NUM) {
00433         // If there are total attempts left and send to has been successful at least once on this round
00434         if (*total_attempts && *send_success) {
00435             *index = 0;
00436             *send_success = 0;
00437         } else {
00438             return NSAPI_ERROR_NO_ADDRESS ;
00439         }
00440     }
00441 
00442     if (*index < DNS_STACK_SERVERS_NUM) {
00443         nsapi_error_t ret = stack->get_dns_server(*index, dns_addr, interface_name);
00444         if (ret < 0) {
00445             *index = DNS_STACK_SERVERS_NUM;
00446         } else {
00447             dns_addr_set = true;
00448         }
00449     }
00450 
00451     if (!dns_addr_set) {
00452         dns_addr->set_addr(dns_servers[*index - DNS_STACK_SERVERS_NUM]);
00453     }
00454 
00455     dns_addr->set_port(53);
00456 
00457     return NSAPI_ERROR_OK ;
00458 }
00459 
00460 // core query function
00461 static nsapi_size_or_error_t nsapi_dns_query_multiple(NetworkStack *stack, const char *host,
00462                                                       nsapi_addr_t *addr, unsigned addr_count,  const char *interface_name, nsapi_version_t version)
00463 {
00464     // check for valid host name
00465     int host_len = host ? strlen(host) : 0;
00466     if (host_len > DNS_HOST_NAME_MAX_LEN || host_len == 0) {
00467         return NSAPI_ERROR_PARAMETER ;
00468     }
00469 
00470     // check cache
00471     if (nsapi_dns_cache_find(host, version, addr) == NSAPI_ERROR_OK ) {
00472         return 1;
00473     }
00474 
00475     // create a udp socket
00476     UDPSocket socket;
00477     int err = socket.open(stack);
00478     if (err) {
00479         return err;
00480     }
00481 
00482     socket.set_timeout (MBED_CONF_NSAPI_DNS_RESPONSE_WAIT_TIME);
00483 
00484     if (interface_name != NULL) {
00485         socket.setsockopt (NSAPI_SOCKET , NSAPI_BIND_TO_DEVICE , interface_name, NSAPI_INTERFACE_NAME_MAX_SIZE);
00486     }
00487     // create network packet
00488     uint8_t *const packet = (uint8_t *)malloc(DNS_BUFFER_SIZE);
00489     if (!packet) {
00490         return NSAPI_ERROR_NO_MEMORY ;
00491     }
00492 
00493     nsapi_size_or_error_t result = NSAPI_ERROR_DNS_FAILURE ;
00494 
00495     uint8_t retries = MBED_CONF_NSAPI_DNS_RETRIES;
00496     uint8_t index = 0;
00497     uint8_t total_attempts = MBED_CONF_NSAPI_DNS_TOTAL_ATTEMPTS;
00498     uint8_t send_success = 0;
00499 
00500     // check against each dns server
00501     while (true) {
00502         SocketAddress dns_addr;
00503         err = nsapi_dns_get_server_addr(stack, &index, &total_attempts, &send_success, &dns_addr, interface_name);
00504         if (err != NSAPI_ERROR_OK ) {
00505             break;
00506         }
00507 
00508         if (version != NSAPI_UNSPEC  && (dns_addr.get_ip_version() != version)) {
00509             retries = MBED_CONF_NSAPI_DNS_RETRIES;
00510             index++;
00511             continue;
00512         }
00513         // send the question
00514         int len = dns_append_question(packet, 1, host, dns_addr.get_ip_version());
00515 
00516         err = socket.sendto(dns_addr, packet, len);
00517         // send may fail for various reasons, including wrong address type - move on
00518         if (err < 0) {
00519             // goes to next dns server
00520             retries = MBED_CONF_NSAPI_DNS_RETRIES;
00521             index++;
00522             continue;
00523         }
00524 
00525         send_success++;
00526 
00527         if (total_attempts) {
00528             total_attempts--;
00529         }
00530 
00531         // recv the response
00532         err = socket.recvfrom(NULL, packet, DNS_BUFFER_SIZE);
00533         if (err == NSAPI_ERROR_WOULD_BLOCK ) {
00534             if (retries) {
00535                 // retries
00536                 retries--;
00537             } else {
00538                 // goes to next dns server
00539                 retries = MBED_CONF_NSAPI_DNS_RETRIES;
00540                 index++;
00541             }
00542             continue;
00543         } else if (err < 0) {
00544             result = err;
00545             break;
00546         }
00547 
00548         const uint8_t *response = packet;
00549         uint32_t ttl;
00550         int resp = dns_scan_response(response, 1, &ttl, addr, addr_count);
00551         if (resp > 0) {
00552             nsapi_dns_cache_add(host, addr, ttl);
00553             result = resp;
00554         } else if (resp < 0) {
00555             continue;
00556         }
00557 
00558         /* The DNS response is final, no need to check other servers */
00559         break;
00560     }
00561 
00562     // clean up packet
00563     free(packet);
00564 
00565     // clean up udp
00566     err = socket.close();
00567     if (err) {
00568         return err;
00569     }
00570 
00571     // return result
00572     return result;
00573 }
00574 
00575 // convenience functions for other forms of queries
00576 extern "C" nsapi_size_or_error_t nsapi_dns_query_multiple(nsapi_stack_t *stack, const char *host,
00577                                                           nsapi_addr_t *addr, nsapi_size_t addr_count, const char *interface_name, nsapi_version_t version)
00578 {
00579     NetworkStack *nstack = nsapi_create_stack(stack);
00580     return nsapi_dns_query_multiple(nstack, host, addr, addr_count, interface_name, version);
00581 }
00582 
00583 nsapi_size_or_error_t nsapi_dns_query_multiple(NetworkStack *stack, const char *host,
00584                                                SocketAddress *addresses, nsapi_size_t addr_count, const char *interface_name, nsapi_version_t version)
00585 {
00586     nsapi_addr_t *addrs = new (std::nothrow) nsapi_addr_t[addr_count];
00587     nsapi_size_or_error_t result = nsapi_dns_query_multiple(stack, host, addrs, addr_count, interface_name, version);
00588 
00589     if (result > 0) {
00590         for (int i = 0; i < result; i++) {
00591             addresses[i].set_addr(addrs[i]);
00592         }
00593     }
00594 
00595     delete[] addrs;
00596     return result;
00597 }
00598 
00599 extern "C" nsapi_error_t nsapi_dns_query(nsapi_stack_t *stack, const char *host,
00600                                          nsapi_addr_t *addr, nsapi_version_t version)
00601 {
00602     NetworkStack *nstack = nsapi_create_stack(stack);
00603     nsapi_size_or_error_t result = nsapi_dns_query_multiple(nstack, host, addr, 1, NULL, version);
00604     return (nsapi_error_t)((result > 0) ? 0 : result);
00605 }
00606 
00607 nsapi_error_t nsapi_dns_query(NetworkStack *stack, const char *host,
00608                               SocketAddress *address, nsapi_version_t version)
00609 {
00610     nsapi_addr_t addr;
00611     nsapi_size_or_error_t result = nsapi_dns_query_multiple(stack, host, &addr, 1, NULL, version);
00612     address->set_addr(addr);
00613     return (nsapi_error_t)((result > 0) ? 0 : result);
00614 }
00615 
00616 nsapi_error_t nsapi_dns_query(NetworkStack *stack, const char *host,
00617                               SocketAddress *address, const char *interface_name, nsapi_version_t version)
00618 {
00619     nsapi_addr_t addr;
00620     nsapi_size_or_error_t result = nsapi_dns_query_multiple(stack, host, &addr, 1, interface_name, version);
00621     address->set_addr(addr);
00622     return (nsapi_error_t)((result > 0) ? 0 : result);
00623 }
00624 
00625 nsapi_value_or_error_t nsapi_dns_query_async(NetworkStack *stack, const char *host,
00626                                              NetworkStack::hostbyname_cb_t callback, call_in_callback_cb_t call_in_cb,
00627                                              nsapi_version_t version)
00628 {
00629     return nsapi_dns_query_multiple_async(stack, host, callback, 0, call_in_cb, NULL, version);
00630 }
00631 
00632 nsapi_value_or_error_t nsapi_dns_query_async(NetworkStack *stack, const char *host,
00633                                              NetworkStack::hostbyname_cb_t callback, call_in_callback_cb_t call_in_cb,
00634                                              const char *interface_name, nsapi_version_t version)
00635 {
00636     return nsapi_dns_query_multiple_async(stack, host, callback, 0, call_in_cb, interface_name, version);
00637 }
00638 
00639 void nsapi_dns_call_in_set(call_in_callback_cb_t callback)
00640 {
00641     *dns_call_in.get() = callback;
00642 }
00643 
00644 void nsapi_dns_reset()
00645 {
00646     nsapi_dns_cache_reset();
00647     dns_message_id = 1;
00648     dns_unique_id = 1;
00649 }
00650 
00651 nsapi_error_t nsapi_dns_call_in(call_in_callback_cb_t cb, int delay, mbed::Callback<void()> func)
00652 {
00653     if (*dns_call_in.get()) {
00654         dns_call_in->call(delay, func);
00655     } else {
00656         return cb(delay, func);
00657     }
00658     return NSAPI_ERROR_OK ;
00659 }
00660 
00661 nsapi_value_or_error_t nsapi_dns_query_multiple_async(NetworkStack *stack, const char *host,
00662                                                       NetworkStack::hostbyname_cb_t callback, nsapi_size_t addr_count,
00663                                                       call_in_callback_cb_t call_in_cb, const char *interface_name, nsapi_version_t version)
00664 {
00665     dns_mutex->lock();
00666 
00667     if (!stack) {
00668         return NSAPI_ERROR_PARAMETER ;
00669     }
00670 
00671     // check for valid host name
00672     int host_len = host ? strlen(host) : 0;
00673     if (host_len > DNS_HOST_NAME_MAX_LEN || host_len == 0) {
00674         dns_mutex->unlock();
00675         return NSAPI_ERROR_PARAMETER ;
00676     }
00677 
00678     nsapi_addr address;
00679     if (nsapi_dns_cache_find(host, version, &address) == NSAPI_ERROR_OK ) {
00680         SocketAddress addr(address);
00681         dns_mutex->unlock();
00682         callback(NSAPI_ERROR_OK , &addr);
00683         return NSAPI_ERROR_OK ;
00684     }
00685 
00686     int index = -1;
00687 
00688     for (int i = 0; i < DNS_QUERY_QUEUE_SIZE; i++) {
00689         if (!dns_query_queue[i]) {
00690             index = i;
00691             break;
00692         }
00693     }
00694 
00695     if (index < 0) {
00696         dns_mutex->unlock();
00697         return NSAPI_ERROR_NO_MEMORY ;
00698     }
00699 
00700     DNS_QUERY *query = new (std::nothrow) DNS_QUERY;
00701 
00702     if (!query) {
00703         dns_mutex->unlock();
00704         return NSAPI_ERROR_NO_MEMORY ;
00705     }
00706 
00707     query->host = new (std::nothrow) char[host_len + 1];
00708     if (!query->host) {
00709         delete query;
00710         dns_mutex->unlock();
00711         return NSAPI_ERROR_NO_MEMORY ;
00712     }
00713     strcpy(query->host, host);
00714     query->status = NSAPI_ERROR_TIMEOUT ;
00715     query->callback = callback;
00716     query->call_in_cb = call_in_cb;
00717     query->stack = stack;
00718     query->addr_count = addr_count;
00719     query->version = version;
00720     query->socket = NULL;
00721     query->socket_cb_data = NULL;
00722     query->addrs = NULL;
00723     query->dns_server = 0;
00724     query->retries = MBED_CONF_NSAPI_DNS_RETRIES + 1;
00725     query->total_attempts =  MBED_CONF_NSAPI_DNS_TOTAL_ATTEMPTS;
00726     query->send_success = 0;
00727     query->dns_message_id = 0;
00728     query->socket_timeout = 0;
00729     query->total_timeout = MBED_CONF_NSAPI_DNS_TOTAL_ATTEMPTS * MBED_CONF_NSAPI_DNS_RESPONSE_WAIT_TIME + 500;
00730     query->count = 0;
00731     query->state = DNS_CREATED;
00732     query->interface_name = interface_name;
00733     query->unique_id = dns_unique_id++;
00734     if (query->unique_id > 0x7FFF) {
00735         query->unique_id = 1;
00736     }
00737 
00738     int ongoing_queries = 0;
00739 
00740     for (int i = 0; i < DNS_QUERY_QUEUE_SIZE; i++) {
00741         if (dns_query_queue[i]) {
00742             if (!query->socket && dns_query_queue[i]->socket && dns_query_queue[i]->stack == query->stack) {
00743                 query->socket = dns_query_queue[i]->socket;
00744                 query->socket_cb_data = dns_query_queue[i]->socket_cb_data;
00745             }
00746             ongoing_queries++;
00747         }
00748     }
00749 
00750     dns_query_queue[index] = query;
00751 
00752     // Add some overhead based on number of ongoing queries
00753     query->total_timeout += ongoing_queries * 500;
00754 
00755     if (!dns_timer_running) {
00756         if (nsapi_dns_call_in(query->call_in_cb, DNS_TIMER_TIMEOUT, mbed::callback(nsapi_dns_query_async_timeout)) != NSAPI_ERROR_OK ) {
00757             delete[] query->host;
00758             delete query;
00759             dns_query_queue[index] = NULL;
00760             dns_mutex->unlock();
00761             return NSAPI_ERROR_NO_MEMORY ;
00762         }
00763         dns_timer_running = true;
00764     }
00765 
00766     // Initiates query
00767     nsapi_dns_query_async_initiate_next();
00768 
00769     dns_mutex->unlock();
00770 
00771     return query->unique_id;
00772 }
00773 
00774 static void nsapi_dns_query_async_initiate_next(void)
00775 {
00776     intptr_t id = INTPTR_MAX;
00777     DNS_QUERY *query = NULL;
00778 
00779     // Trigger next query to start, find one that has been on queue longest
00780     for (int i = 0; i < DNS_QUERY_QUEUE_SIZE; i++) {
00781         if (dns_query_queue[i]) {
00782             if (dns_query_queue[i]->state == DNS_CREATED) {
00783                 if (dns_query_queue[i]->unique_id <= id) {
00784                     query = dns_query_queue[i];
00785                     id = dns_query_queue[i]->unique_id;
00786                 }
00787                 // If some query is already ongoing do not trigger
00788             } else if (dns_query_queue[i]->state == DNS_INITIATED) {
00789                 query = NULL;
00790                 break;
00791             }
00792         }
00793     }
00794 
00795     if (query) {
00796         query->state = DNS_INITIATED;
00797         nsapi_dns_call_in(query->call_in_cb, 0, mbed::callback(nsapi_dns_query_async_create, reinterpret_cast<void *>(query->unique_id)));
00798     }
00799 }
00800 
00801 static void nsapi_dns_query_async_timeout(void)
00802 {
00803     dns_mutex->lock();
00804 
00805     DNS_QUERY *query = NULL;
00806 
00807     for (int i = 0; i < DNS_QUERY_QUEUE_SIZE; i++) {
00808         if (dns_query_queue[i]) {
00809             if (dns_query_queue[i]->state == DNS_CANCELLED) {
00810                 // Delete cancelled
00811                 nsapi_dns_query_async_delete(dns_query_queue[i]->unique_id);
00812                 nsapi_dns_query_async_initiate_next();
00813                 continue;
00814             }
00815 
00816             if (dns_query_queue[i]->total_timeout > DNS_TIMER_TIMEOUT) {
00817                 dns_query_queue[i]->total_timeout -= DNS_TIMER_TIMEOUT;
00818             } else {
00819                 // If does not already have response, fails
00820                 if (dns_query_queue[i]->status == NSAPI_ERROR_TIMEOUT ) {
00821                     dns_query_queue[i]->socket_timeout = 0;
00822                     nsapi_dns_call_in(dns_query_queue[i]->call_in_cb, 0, mbed::callback(nsapi_dns_query_async_response, reinterpret_cast<void *>(dns_query_queue[i]->unique_id)));
00823                 }
00824             }
00825 
00826             if (dns_query_queue[i]->socket_timeout > 0) {
00827                 if (dns_query_queue[i]->socket_timeout > DNS_TIMER_TIMEOUT) {
00828                     dns_query_queue[i]->socket_timeout -= DNS_TIMER_TIMEOUT;
00829                 } else {
00830                     // Retries
00831                     dns_query_queue[i]->socket_timeout = 0;
00832                     nsapi_dns_call_in(dns_query_queue[i]->call_in_cb, 0,
00833                                       mbed::callback(nsapi_dns_query_async_send, reinterpret_cast<void *>(dns_query_queue[i]->unique_id)));
00834                 }
00835             }
00836 
00837             if (!query) {
00838                 query = dns_query_queue[i];
00839             }
00840         }
00841     }
00842 
00843     // Starts timer again
00844     if (query) {
00845         nsapi_dns_call_in(query->call_in_cb, DNS_TIMER_TIMEOUT, mbed::callback(nsapi_dns_query_async_timeout));
00846     } else {
00847         dns_timer_running = false;
00848     }
00849 
00850     dns_mutex->unlock();
00851 }
00852 
00853 nsapi_error_t nsapi_dns_query_async_cancel(nsapi_size_or_error_t id)
00854 {
00855     dns_mutex->lock();
00856 
00857     DNS_QUERY *query = NULL;
00858 
00859     for (int i = 0; i < DNS_QUERY_QUEUE_SIZE; i++) {
00860         if (dns_query_queue[i] && dns_query_queue[i]->unique_id == id) {
00861             query = dns_query_queue[i];
00862             break;
00863         }
00864     }
00865 
00866     if (!query || query->state == DNS_CANCELLED) {
00867         dns_mutex->unlock();
00868         return NSAPI_ERROR_PARAMETER ;
00869     }
00870 
00871     // Mark the query as cancelled, deleted by timer handler
00872     query->state = DNS_CANCELLED;
00873     // Do not call callback
00874     query->callback = 0;
00875 
00876     dns_mutex->unlock();
00877 
00878     return NSAPI_ERROR_OK ;
00879 }
00880 
00881 static void nsapi_dns_query_async_create(void *ptr)
00882 {
00883     dns_mutex->lock();
00884 
00885     intptr_t unique_id = reinterpret_cast<intptr_t>(ptr);
00886 
00887     DNS_QUERY *query = NULL;
00888 
00889     for (int i = 0; i < DNS_QUERY_QUEUE_SIZE; i++) {
00890         if (dns_query_queue[i] && dns_query_queue[i]->unique_id == unique_id) {
00891             query = dns_query_queue[i];
00892             break;
00893         }
00894     }
00895 
00896     if (!query || query->state == DNS_CANCELLED) {
00897         // Cancel has been called
00898         dns_mutex->unlock();
00899         return;
00900     }
00901 
00902     for (int i = 0; i < DNS_QUERY_QUEUE_SIZE; i++) {
00903         if (dns_query_queue[i] && dns_query_queue[i] != query) {
00904             if (!query->socket && dns_query_queue[i]->socket && dns_query_queue[i]->stack == query->stack) {
00905                 query->socket = dns_query_queue[i]->socket;
00906                 query->socket_cb_data = dns_query_queue[i]->socket_cb_data;
00907             }
00908         }
00909     }
00910 
00911     UDPSocket *socket;
00912 
00913     if (query->socket) {
00914         socket = query->socket;
00915     } else {
00916         socket = new (std::nothrow) UDPSocket;
00917         if (!socket) {
00918             nsapi_dns_query_async_resp(query, NSAPI_ERROR_NO_MEMORY , NULL);
00919             return;
00920         }
00921 
00922         int err = socket->open(query->stack);
00923 
00924         if (err) {
00925             delete socket;
00926             nsapi_dns_query_async_resp(query, err, NULL);
00927             return;
00928         }
00929 
00930         socket->set_timeout (0);
00931         if (query->interface_name != NULL) {
00932             socket->setsockopt (NSAPI_SOCKET , NSAPI_BIND_TO_DEVICE , query->interface_name, NSAPI_INTERFACE_NAME_MAX_SIZE);
00933         }
00934 
00935         if (!query->socket_cb_data) {
00936             query->socket_cb_data = new SOCKET_CB_DATA;
00937         }
00938         query->socket_cb_data->call_in_cb = query->call_in_cb;
00939         query->socket_cb_data->stack = query->stack;
00940         socket->sigio (mbed::callback(nsapi_dns_query_async_socket_callback, query->socket_cb_data));
00941 
00942         query->socket = socket;
00943     }
00944 
00945     dns_mutex->unlock();
00946 
00947     nsapi_dns_query_async_send(reinterpret_cast<void *>(query->unique_id));
00948 
00949 }
00950 
00951 static nsapi_error_t nsapi_dns_query_async_delete(intptr_t unique_id)
00952 {
00953     int index = -1;
00954     DNS_QUERY *query = NULL;
00955 
00956     for (int i = 0; i < DNS_QUERY_QUEUE_SIZE; i++) {
00957         if (dns_query_queue[i] && dns_query_queue[i]->unique_id == unique_id) {
00958             query = dns_query_queue[i];
00959             index = i;
00960             break;
00961         }
00962     }
00963 
00964     if (!query) {
00965         return NSAPI_ERROR_PARAMETER ;
00966     }
00967 
00968     bool close_socket = true;
00969 
00970     for (int i = 0; i < DNS_QUERY_QUEUE_SIZE; i++) {
00971         if (dns_query_queue[i] && dns_query_queue[i] != query && dns_query_queue[i]->socket &&
00972                 dns_query_queue[i]->stack == query->stack) {
00973             close_socket = false;
00974         }
00975     }
00976 
00977     if (close_socket && query->socket) {
00978         query->socket->close();
00979         delete query->socket;
00980         delete query->socket_cb_data;
00981     }
00982 
00983     if (query->addrs) {
00984         delete[] query->addrs;
00985     }
00986 
00987     delete[] query->host;
00988     delete query;
00989     dns_query_queue[index] = NULL;
00990 
00991     return NSAPI_ERROR_OK ;
00992 }
00993 
00994 static void nsapi_dns_query_async_resp(DNS_QUERY *query, nsapi_error_t status, SocketAddress *address)
00995 {
00996     NetworkStack::hostbyname_cb_t callback = query->callback;
00997     nsapi_dns_query_async_delete(query->unique_id);
00998     nsapi_dns_query_async_initiate_next();
00999 
01000     dns_mutex->unlock();
01001 
01002     if (callback) {
01003         callback(status, address);
01004     }
01005 }
01006 
01007 static void nsapi_dns_query_async_send(void *ptr)
01008 {
01009     dns_mutex->lock();
01010 
01011     intptr_t unique_id = reinterpret_cast<intptr_t>(ptr);
01012 
01013     DNS_QUERY *query = NULL;
01014 
01015     for (int i = 0; i < DNS_QUERY_QUEUE_SIZE; i++) {
01016         if (dns_query_queue[i] && dns_query_queue[i]->unique_id == unique_id) {
01017             query = dns_query_queue[i];
01018             break;
01019         }
01020     }
01021 
01022     if (!query || query->state != DNS_INITIATED) {
01023         // Cancel has been called
01024         dns_mutex->unlock();
01025         return;
01026     }
01027 
01028     if (query->retries) {
01029         query->retries--;
01030     } else {
01031         query->dns_server++;
01032         query->retries = MBED_CONF_NSAPI_DNS_RETRIES;
01033     }
01034 
01035     query->dns_message_id = dns_message_id++;
01036     if (dns_message_id == 0) {
01037         dns_message_id = 1;
01038     }
01039 
01040     // create network packet
01041     uint8_t *packet = (uint8_t *)malloc(DNS_BUFFER_SIZE);
01042     if (!packet) {
01043         nsapi_dns_query_async_resp(query, NSAPI_ERROR_NO_MEMORY , NULL);
01044         return;
01045     }
01046 
01047     while (true) {
01048         SocketAddress dns_addr;
01049         nsapi_size_or_error_t err = nsapi_dns_get_server_addr(query->stack, &(query->dns_server), &(query->total_attempts), &(query->send_success), &dns_addr, query->interface_name);
01050         if (err != NSAPI_ERROR_OK ) {
01051             nsapi_dns_query_async_resp(query, NSAPI_ERROR_TIMEOUT , NULL);
01052             free(packet);
01053             return;
01054         }
01055 
01056         if (query->version != NSAPI_UNSPEC  && dns_addr.get_ip_version() != query->version) {
01057             query->dns_server++;
01058             continue;
01059         }
01060         // send the question
01061         int len = dns_append_question(packet, query->dns_message_id, query->host, dns_addr.get_ip_version());
01062 
01063         err = query->socket->sendto(dns_addr, packet, len);
01064 
01065         if (err < 0) {
01066             if (err == NSAPI_ERROR_WOULD_BLOCK ) {
01067                 nsapi_dns_call_in(query->call_in_cb, DNS_TIMER_TIMEOUT, mbed::callback(nsapi_dns_query_async_send, ptr));
01068                 free(packet);
01069                 dns_mutex->unlock();
01070                 return; // Timeout handler will retry the connection if possible
01071             } else {
01072                 query->dns_server++;
01073             }
01074         } else {
01075             break;
01076         }
01077     }
01078 
01079     query->send_success++;
01080 
01081     if (query->total_attempts) {
01082         query->total_attempts--;
01083     }
01084 
01085     free(packet);
01086 
01087     query->socket_timeout = MBED_CONF_NSAPI_DNS_RESPONSE_WAIT_TIME;
01088 
01089     dns_mutex->unlock();
01090 }
01091 
01092 static void nsapi_dns_query_async_socket_callback(void *ptr)
01093 {
01094     SOCKET_CB_DATA *cb_data = static_cast<SOCKET_CB_DATA *>(ptr);
01095 
01096     if (cb_data) {
01097         nsapi_dns_call_in(cb_data->call_in_cb, 0, mbed::callback(nsapi_dns_query_async_socket_callback_handle, cb_data->stack));
01098     }
01099 }
01100 
01101 static void nsapi_dns_query_async_socket_callback_handle(NetworkStack *stack)
01102 {
01103     UDPSocket *socket = NULL;
01104 
01105     dns_mutex->lock();
01106 
01107     for (int i = 0; i < DNS_QUERY_QUEUE_SIZE; i++) {
01108         if (dns_query_queue[i] && dns_query_queue[i]->stack == stack) {
01109             socket = dns_query_queue[i]->socket;
01110             break;
01111         }
01112     }
01113 
01114     if (socket) {
01115         // create network packet
01116         uint8_t *packet = (uint8_t *)malloc(DNS_BUFFER_SIZE);
01117         if (!packet) {
01118             dns_mutex->unlock();
01119             return;
01120         }
01121 
01122         while (true) {
01123             // recv the response
01124             nsapi_size_or_error_t size = socket->recvfrom(NULL, packet, DNS_BUFFER_SIZE);
01125 
01126             if (size < DNS_RESPONSE_MIN_SIZE) {
01127                 break;
01128             }
01129 
01130             // gets id from response to associate with correct query
01131             uint16_t id = (packet[0] << 8) | packet[1];
01132 
01133             DNS_QUERY *query = NULL;
01134 
01135             for (int i = 0; i < DNS_QUERY_QUEUE_SIZE; i++) {
01136                 if (dns_query_queue[i] && dns_query_queue[i]->dns_message_id == id) {
01137                     query = dns_query_queue[i];
01138                     break;
01139                 }
01140             }
01141 
01142             if (!query || query->state != DNS_INITIATED) {
01143                 continue;
01144             }
01145 
01146             int requested_count = 1;
01147             if (query->addr_count > 1) {
01148                 requested_count = query->addr_count;
01149             }
01150 
01151             query->addrs = new (std::nothrow) nsapi_addr_t[requested_count];
01152 
01153             int resp = dns_scan_response(packet, id, &(query->ttl), query->addrs, requested_count);
01154 
01155             // Ignore invalid responses
01156             if (resp < 0) {
01157                 delete[] query->addrs;
01158                 query->addrs = 0;
01159             } else {
01160                 query->count = resp;
01161                 query->status = NSAPI_ERROR_DNS_FAILURE ; // Used in case failure, otherwise ok
01162                 query->socket_timeout = 0;
01163                 nsapi_dns_call_in(query->call_in_cb, 0, mbed::callback(nsapi_dns_query_async_response, reinterpret_cast<void *>(query->unique_id)));
01164             }
01165         }
01166 
01167         free(packet);
01168     }
01169 
01170     dns_mutex->unlock();
01171 }
01172 
01173 static void nsapi_dns_query_async_response(void *ptr)
01174 {
01175     dns_mutex->lock();
01176 
01177     intptr_t unique_id = reinterpret_cast<intptr_t>(ptr);
01178 
01179     DNS_QUERY *query = NULL;
01180 
01181     for (int i = 0; i < DNS_QUERY_QUEUE_SIZE; i++) {
01182         if (dns_query_queue[i] && dns_query_queue[i]->unique_id == unique_id) {
01183             query = dns_query_queue[i];
01184             break;
01185         }
01186     }
01187 
01188     if (query && query->state == DNS_INITIATED) {
01189         SocketAddress *addresses = NULL;
01190         nsapi_error_t status = query->status;
01191 
01192         if (query->count > 0) {
01193             addresses = new (std::nothrow) SocketAddress[query->count];
01194 
01195             for (int i = 0; i < query->count; i++) {
01196                 addresses[i].set_addr(query->addrs[i]);
01197             }
01198 
01199             // Adds address to cache
01200             nsapi_dns_cache_add(query->host, &(query->addrs[0]), query->ttl);
01201 
01202             status = NSAPI_ERROR_OK ;
01203             if (query->addr_count > 0) {
01204                 status = query->count;
01205             }
01206         }
01207 
01208         nsapi_dns_query_async_resp(query, status, addresses);
01209         delete[] addresses;
01210     } else {
01211         dns_mutex->unlock();
01212     }
01213 }