takashi kadono / Mbed OS Nucleo_446

Dependencies:   ssd1331

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