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