Corrected defects

- Changed call_in/call methods of the stack to callback provided by the stack
- Specified what are limitations for operations that are made in callback
- Added pure virtual class DNS that defines DNS operations
- Added cancel operation and unique ID to DNS request used in cancel
- Added DNS configuration options to netsocket/mbed_lib.json for retries,
  response wait time and cache size
- Changed host name to use dynamic memory in DNS query list and cache,
  set maximum length for the name to 255 bytes.
- Added mutex to asynchronous DNS
- Reworked retries: there is now total retry count and a server specific count
- Ignores invalid incoming UDP socket messages (DNS header is not valid), and retries DNS query
- Reworked DNS module asynchronous operation functions
- Corrected other review issues (nothrow new, missing free, missing mutex unlock etc.)
pull/6847/head
Mika Leppänen 2018-05-03 16:13:10 +03:00 committed by Kevin Bracey
parent b7e8400c2c
commit 1c01f5dda4
17 changed files with 695 additions and 357 deletions

View File

@ -207,11 +207,6 @@ void LWIP::tcpip_thread_callback(void *ptr)
}
}
nsapi_error_t LWIP::call(mbed::Callback<void()> func)
{
return call_in(0, func);
}
nsapi_error_t LWIP::call_in(int delay, mbed::Callback<void()> func)
{
lwip_callback *cb = new lwip_callback;
@ -229,6 +224,12 @@ nsapi_error_t LWIP::call_in(int delay, mbed::Callback<void()> func)
return NSAPI_ERROR_OK;
}
LWIP::call_in_callback_cb_t LWIP::get_call_in_callback()
{
call_in_callback_cb_t cb(this, &LWIP::call_in);
return cb;
}
const char *LWIP::get_ip_address()
{
if (!default_interface) {

View File

@ -209,27 +209,6 @@ public:
*/
virtual nsapi_error_t get_dns_server(int index, SocketAddress *address);
/** Call a callback
*
* Call a callback from the network stack context. If returns error
* callback will not be called.
*
* @param func Callback to be called
* @return 0 on success, negative error code on failure
*/
virtual nsapi_error_t call(mbed::Callback<void()> func);
/** Call a callback after a delay
*
* Call a callback from the network stack context after a delay. If
* returns error callback will not be called.
*
* @param delay Delay in milliseconds
* @param func Callback to be called
* @return 0 on success, negative error code on failure
*/
virtual nsapi_error_t call_in(int delay, mbed::Callback<void()> func);
/** Get the local IP address
*
* @return Null-terminated representation of the local IP address
@ -438,6 +417,37 @@ protected:
int optname, void *optval, unsigned *optlen);
private:
/** Call in callback
*
* Callback is used to call the call in method of the network stack.
*/
typedef mbed::Callback<nsapi_error_t (int delay_ms, mbed::Callback<void()> user_cb)> call_in_callback_cb_t;
/** Get a call in callback
*
* Get a call in callback from the network stack context.
*
* Callback should not take more than 10ms to execute, otherwise it might
* prevent underlying thread processing. A portable user of the callback
* should not make calls to network operations due to stack size limitations.
* The callback should not perform expensive operations such as socket recv/send
* calls or blocking operations.
*
* @return Call in callback
*/
virtual call_in_callback_cb_t get_call_in_callback();
/** Call a callback after a delay
*
* Call a callback from the network stack context after a delay. If function
* returns error callback will not be called.
*
* @param delay Delay in milliseconds
* @param func Callback to be called
* @return 0 on success, negative error code on failure
*/
nsapi_error_t call_in(int delay, mbed::Callback<void()> func);
struct mbed_lwip_socket {
bool in_use;

View File

@ -482,20 +482,14 @@ void sys_msleep(u32_t ms) {
osThreadId_t lwip_tcpip_thread_id = 0;
bool sys_tcpip_thread_set(void)
void sys_tcpip_thread_set(void)
{
lwip_tcpip_thread_id = osThreadGetId();
}
bool sys_tcpip_thread_check(void)
{
osThreadId_t thread_id = osThreadGetId();
if (thread_id == lwip_tcpip_thread_id) {
return true;
} else {
return false;
}
return osThreadGetId() == lwip_tcpip_thread_id;
}
// Keep a pool of thread structures

View File

@ -85,7 +85,7 @@ typedef sys_thread_data_t* sys_thread_t;
// === PROTECTION ===
typedef int sys_prot_t;
bool sys_tcpip_thread_set(void);
void sys_tcpip_thread_set(void);
bool sys_tcpip_thread_check(void);
#else

View File

@ -910,6 +910,8 @@ netconn_join_leave_group(struct netconn *conn,
#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */
#if LWIP_DNS
#if LWIP_FULL_DNS
/**
* @ingroup netconn_common
* Execute a DNS query, only one IP address is returned
@ -989,6 +991,8 @@ netconn_gethostbyname(const char *name, ip_addr_t *addr)
API_VAR_FREE(MEMP_DNS_API_MSG, msg);
return err;
}
#endif /* LWIP_FULL_DNS */
#endif /* LWIP_DNS*/
#if LWIP_NETCONN_SEM_PER_THREAD

View File

@ -1892,6 +1892,7 @@ lwip_netconn_do_join_leave_group(void *m)
#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */
#if LWIP_DNS
#if LWIP_FULL_DNS
/**
* Callback function that is called when DNS name is resolved
* (or on timeout). A waiting application thread is waked up by
@ -1943,5 +1944,6 @@ lwip_netconn_do_gethostbyname(void *arg)
}
}
#endif /* LWIP_DNS */
#endif /* LWIP_FULL_DNS */
#endif /* LWIP_NETCONN */

View File

@ -461,11 +461,6 @@ void Nanostack::call_event_tasklet_main(arm_event_s *event)
}
}
nsapi_error_t Nanostack::call(mbed::Callback<void()> func)
{
return call_in(0, func);
}
nsapi_error_t Nanostack::call_in(int delay, mbed::Callback<void()> func)
{
if (call_event_tasklet < 0) {
@ -494,10 +489,12 @@ nsapi_error_t Nanostack::call_in(int delay, mbed::Callback<void()> func)
if (delay) {
uint32_t ticks = eventOS_event_timer_ms_to_ticks(delay);
if (!eventOS_event_send_in(&event, ticks)) {
delete cb;
return NSAPI_ERROR_NO_MEMORY;
}
} else {
if (eventOS_event_send(&event) < 0) {
delete cb;
return NSAPI_ERROR_NO_MEMORY;
}
}
@ -505,6 +502,12 @@ nsapi_error_t Nanostack::call_in(int delay, mbed::Callback<void()> func)
return NSAPI_ERROR_OK;
}
Nanostack::call_in_callback_cb_t Nanostack::get_call_in_callback()
{
call_in_callback_cb_t cb(this, &Nanostack::call_in);
return cb;
}
const char * Nanostack::get_ip_address()
{
NanostackLockGuard lock;

View File

@ -44,27 +44,6 @@ public:
/* Local variant with stronger typing and manual address specification */
nsapi_error_t add_ethernet_interface(EMAC &emac, bool default_if, Nanostack::EthernetInterface **interface_out, const uint8_t *mac_addr = NULL);
/** Call a callback
*
* Call a callback from the network stack context. If returns error
* callback will not be called.
*
* @param func Callback to be called
* @return 0 on success, negative error code on failure
*/
virtual nsapi_error_t call(mbed::Callback<void()> func);
/** Call a callback after a delay
*
* Call a callback from the network stack context after a delay. If
* returns error callback will not be called.
*
* @param delay Delay in milliseconds
* @param func Callback to be called
* @return 0 on success, negative error code on failure
*/
virtual nsapi_error_t call_in(int delay, mbed::Callback<void()> func);
protected:
Nanostack();
@ -266,6 +245,38 @@ protected:
virtual nsapi_error_t getsockopt(void *handle, int level, int optname, void *optval, unsigned *optlen);
private:
/** Call in callback
*
* Callback is used to call the call in method of the network stack.
*/
typedef mbed::Callback<nsapi_error_t (int delay_ms, mbed::Callback<void()> user_cb)> call_in_callback_cb_t;
/** Get a call in callback
*
* Get a call in callback from the network stack context.
*
* Callback should not take more than 10ms to execute, otherwise it might
* prevent underlying thread processing. A portable user of the callback
* should not make calls to network operations due to stack size limitations.
* The callback should not perform expensive operations such as socket recv/send
* calls or blocking operations.
*
* @return Call in callback
*/
virtual call_in_callback_cb_t get_call_in_callback();
/** Call a callback after a delay
*
* Call a callback from the network stack context after a delay. If function
* returns error callback will not be called.
*
* @param delay Delay in milliseconds
* @param func Callback to be called
* @return 0 on success, negative error code on failure
*/
nsapi_error_t call_in(int delay, mbed::Callback<void()> func);
struct nanostack_callback {
mbed::Callback<void()> callback;
};

96
features/netsocket/DNS.h Normal file
View File

@ -0,0 +1,96 @@
/*
* Copyright (c) 2018 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef DNS_H
#define DNS_H
class DNS {
public:
/** Translates a hostname to an IP address with specific version
*
* The hostname may be either a domain name or an IP address. If the
* hostname is an IP address, no network transactions will be performed.
*
* If no stack-specific DNS resolution is provided, the hostname
* will be resolve using a UDP socket on the stack.
*
* @param host Hostname to resolve
* @param address Destination for the host SocketAddress
* @param version IP version of address to resolve, NSAPI_UNSPEC indicates
* version is chosen by the stack (defaults to NSAPI_UNSPEC)
* @return 0 on success, negative error code on failure
*/
virtual nsapi_error_t gethostbyname(const char *host,
SocketAddress *address, nsapi_version_t version = NSAPI_UNSPEC) = 0;
/** Hostname translation callback (asynchronous)
*
* Callback will be called after DNS resolution completes or a failure occurs.
*
* Callback should not take more than 10ms to execute, otherwise it might
* prevent underlying thread processing. A portable user of the callback
* should not make calls to network operations due to stack size limitations.
* The callback should not perform expensive operations such as socket recv/send
* calls or blocking operations.
*
* @param status 0 on success, negative error code on failure
* @param address On success, destination for the host SocketAddress
*/
typedef mbed::Callback<void (nsapi_error_t result, SocketAddress *address)> hostbyname_cb_t;
/** Translates a hostname to an IP address (asynchronous)
*
* The hostname may be either a domain name or an IP address. If the
* hostname is an IP address, no network transactions will be performed.
*
* If no stack-specific DNS resolution is provided, the hostname
* will be resolve using a UDP socket on the stack.
*
* Call is non-blocking. Result of the DNS operation is returned by the callback.
* If this function returns failure, callback will not be called. In case result
* is success (IP address was found from DNS cache), callback will be called
* before function returns.
*
* @param host Hostname to resolve
* @param callback Callback that is called for result
* @param version IP version of address to resolve, NSAPI_UNSPEC indicates
* version is chosen by the stack (defaults to NSAPI_UNSPEC)
* @return 0 on success, negative error code on failure or an unique id that
* represents the hostname translation operation and can be passed to
* cancel
*/
virtual nsapi_error_t gethostbyname_async(const char *host, hostbyname_cb_t callback,
nsapi_version_t version = NSAPI_UNSPEC) = 0;
/** Cancels asynchronous hostname translation
*
* When translation is cancelled, callback will not be called.
*
* @param id Unique id of the hostname translation operation
* @return 0 on success, negative error code on failure
*/
virtual nsapi_error_t gethostbyname_async_cancel(nsapi_error_t id) = 0;
/** Add a domain name server to list of servers to query
*
* @param address Destination for the host address
* @return 0 on success, negative error code on failure
*/
virtual nsapi_error_t add_dns_server(const SocketAddress &address) = 0;
};
#endif

View File

@ -60,9 +60,14 @@ nsapi_error_t NetworkInterface::gethostbyname(const char *name, SocketAddress *a
return get_stack()->gethostbyname(name, address, version);
}
nsapi_error_t NetworkInterface::gethostbyname_async(const char *host, hostbyname_cb_t callback, void *data, nsapi_version_t version)
nsapi_error_t NetworkInterface::gethostbyname_async(const char *host, hostbyname_cb_t callback, nsapi_version_t version)
{
return get_stack()->gethostbyname_async(host, callback, data, version);
return get_stack()->gethostbyname_async(host, callback, version);
}
nsapi_error_t NetworkInterface::gethostbyname_async_cancel(nsapi_error_t handle)
{
return get_stack()->gethostbyname_async_cancel(handle);
}
nsapi_error_t NetworkInterface::add_dns_server(const SocketAddress &address)

View File

@ -20,6 +20,8 @@
#include "netsocket/nsapi_types.h"
#include "netsocket/SocketAddress.h"
#include "Callback.h"
#include "DNS.h"
// Predeclared classes
class NetworkStack;
@ -34,7 +36,7 @@ class EMACInterface;
* Common interface that is shared between network devices
* @addtogroup netsocket
*/
class NetworkInterface {
class NetworkInterface: public DNS {
public:
@ -118,8 +120,8 @@ public:
* If no stack-specific DNS resolution is provided, the hostname
* will be resolve using a UDP socket on the stack.
*
* @param address Destination for the host SocketAddress
* @param host Hostname to resolve
* @param address Destination for the host SocketAddress
* @param version IP version of address to resolve, NSAPI_UNSPEC indicates
* version is chosen by the stack (defaults to NSAPI_UNSPEC)
* @return 0 on success, negative error code on failure
@ -129,14 +131,18 @@ public:
/** Hostname translation callback (asynchronous)
*
* Callback will be called after DNS resolution completes or a failure
* occurs.
* Callback will be called after DNS resolution completes or a failure occurs.
*
* Callback should not take more than 10ms to execute, otherwise it might
* prevent underlying thread processing. A portable user of the callback
* should not make calls to network operations due to stack size limitations.
* The callback should not perform expensive operations such as socket recv/send
* calls or blocking operations.
*
* @param status 0 on success, negative error code on failure
* @param address On success, destination for the host SocketAddress
* @param data Caller defined data
*/
typedef mbed::Callback<void (nsapi_error_t result, SocketAddress *address, void *data)> hostbyname_cb_t;
typedef mbed::Callback<void (nsapi_error_t result, SocketAddress *address)> hostbyname_cb_t;
/** Translates a hostname to an IP address (asynchronous)
*
@ -147,17 +153,29 @@ public:
* will be resolve using a UDP socket on the stack.
*
* Call is non-blocking. Result of the DNS operation is returned by the callback.
* If this function returns failure, callback will not be called.
* If this function returns failure, callback will not be called. In case result
* is success (IP address was found from DNS cache), callback will be called
* before function returns.
*
* @param host Hostname to resolve
* @param callback Callback that is called for result
* @param data Caller defined data returned in callback
* @param version IP version of address to resolve, NSAPI_UNSPEC indicates
* version is chosen by the stack (defaults to NSAPI_UNSPEC)
* @return 0 on success, negative error code on failure or an unique id that
* represents the hostname translation operation and can be passed to
* cancel
*/
virtual nsapi_error_t gethostbyname_async(const char *host, hostbyname_cb_t callback,
nsapi_version_t version = NSAPI_UNSPEC);
/** Cancels asynchronous hostname translation
*
* When translation is cancelled, callback will not be called.
*
* @param id Unique id of the hostname translation operation
* @return 0 on success, negative error code on failure
*/
virtual nsapi_error_t gethostbyname_async(const char *host, hostbyname_cb_t callback, void *data,
nsapi_version_t version = NSAPI_UNSPEC);
virtual nsapi_error_t gethostbyname_async_cancel(nsapi_error_t id);
/** Add a domain name server to list of servers to query
*

View File

@ -49,7 +49,7 @@ nsapi_error_t NetworkStack::gethostbyname(const char *name, SocketAddress *addre
return nsapi_dns_query(this, name, address, version);
}
nsapi_error_t NetworkStack::gethostbyname_async(const char *name, hostbyname_cb_t callback, void *data, nsapi_version_t version)
nsapi_error_t NetworkStack::gethostbyname_async(const char *name, hostbyname_cb_t callback, nsapi_version_t version)
{
SocketAddress address;
@ -59,6 +59,7 @@ nsapi_error_t NetworkStack::gethostbyname_async(const char *name, hostbyname_cb_
return NSAPI_ERROR_DNS_FAILURE;
}
callback(NSAPI_ERROR_OK, &address);
return NSAPI_ERROR_OK;
}
@ -71,7 +72,14 @@ nsapi_error_t NetworkStack::gethostbyname_async(const char *name, hostbyname_cb_
}
}
return nsapi_dns_query_async(this, name, callback, data, version);
call_in_callback_cb_t call_in_cb = get_call_in_callback();
return nsapi_dns_query_async(this, name, callback, call_in_cb, version);
}
nsapi_error_t NetworkStack::gethostbyname_async_cancel(nsapi_error_t handle)
{
return nsapi_dns_query_async_cancel(handle);
}
nsapi_error_t NetworkStack::add_dns_server(const SocketAddress &address)
@ -104,6 +112,39 @@ nsapi_error_t NetworkStack::getsockopt(void *handle, int level, int optname, voi
return NSAPI_ERROR_UNSUPPORTED;
}
nsapi_error_t NetworkStack::call_in(int delay, mbed::Callback<void()> func)
{
events::EventQueue *event_queue = mbed::mbed_event_queue();
if (!event_queue) {
return NSAPI_ERROR_NO_MEMORY;
}
if (delay > 0) {
if (event_queue->call_in(delay, func) == 0) {
return NSAPI_ERROR_NO_MEMORY;
}
} else {
if (event_queue->call(func) == 0) {
return NSAPI_ERROR_NO_MEMORY;
}
}
return NSAPI_ERROR_OK;
}
typedef mbed::Callback<nsapi_error_t (int delay_ms, mbed::Callback<void()> user_cb)> call_in_callback_cb_t;
call_in_callback_cb_t NetworkStack::get_call_in_callback()
{
events::EventQueue *event_queue = mbed::mbed_event_queue();
if (!event_queue) {
return NULL;
}
call_in_callback_cb_t cb(this, &NetworkStack::call_in);
return cb;
}
// NetworkStackWrapper class for encapsulating the raw nsapi_stack structure
class NetworkStackWrapper : public NetworkStack

View File

@ -21,6 +21,7 @@
#include "nsapi_types.h"
#include "netsocket/SocketAddress.h"
#include "netsocket/NetworkInterface.h"
#include "DNS.h"
// Predeclared classes
class OnboardNetworkStack;
@ -33,7 +34,7 @@ class OnboardNetworkStack;
* for instantiating network sockets.
* @addtogroup netsocket
*/
class NetworkStack
class NetworkStack: public DNS
{
public:
virtual ~NetworkStack() {};
@ -64,14 +65,18 @@ public:
/** Hostname translation callback (asynchronous)
*
* Callback will be called after DNS resolution completes or a failure
* occurs.
* Callback will be called after DNS resolution completes or a failure occurs.
*
* Callback should not take more than 10ms to execute, otherwise it might
* prevent underlying thread processing. A portable user of the callback
* should not make calls to network operations due to stack size limitations.
* The callback should not perform expensive operations such as socket recv/send
* calls or blocking operations.
*
* @param status 0 on success, negative error code on failure
* @param address On success, destination for the host SocketAddress
* @param data Caller defined data
*/
typedef mbed::Callback<void (nsapi_error_t result, SocketAddress *address, void *data)> hostbyname_cb_t;
typedef mbed::Callback<void (nsapi_error_t result, SocketAddress *address)> hostbyname_cb_t;
/** Translates a hostname to an IP address (asynchronous)
*
@ -82,17 +87,29 @@ public:
* will be resolve using a UDP socket on the stack.
*
* Call is non-blocking. Result of the DNS operation is returned by the callback.
* If this function returns failure, callback will not be called.
* If this function returns failure, callback will not be called. In case result
* is success (IP address was found from DNS cache), callback will be called
* before function returns.
*
* @param host Hostname to resolve
* @param callback Callback that is called for result
* @param data Caller defined data returned in callback
* @param version IP version of address to resolve, NSAPI_UNSPEC indicates
* version is chosen by the stack (defaults to NSAPI_UNSPEC)
* @return 0 on success, negative error code on failure or an unique id that
* represents the hostname translation operation and can be passed to
* cancel
*/
virtual nsapi_error_t gethostbyname_async(const char *host, hostbyname_cb_t callback,
nsapi_version_t version = NSAPI_UNSPEC);
/** Cancels asynchronous hostname translation
*
* When translation is cancelled, callback will not be called.
*
* @param id Unique id of the hostname translation operation
* @return 0 on success, negative error code on failure
*/
virtual nsapi_error_t gethostbyname_async(const char *host, hostbyname_cb_t callback, void *data,
nsapi_version_t version = NSAPI_UNSPEC);
virtual nsapi_error_t gethostbyname_async_cancel(nsapi_error_t id);
/** Add a domain name server to list of servers to query
*
@ -347,8 +364,40 @@ protected:
*/
virtual nsapi_error_t getsockopt(nsapi_socket_t handle, int level,
int optname, void *optval, unsigned *optlen);
};
private:
/** Call in callback
*
* Callback is used to call the call in method of the network stack.
*/
typedef mbed::Callback<nsapi_error_t (int delay_ms, mbed::Callback<void()> user_cb)> call_in_callback_cb_t;
/** Get a call in callback
*
* Get a call in callback from the network stack context.
*
* Callback should not take more than 10ms to execute, otherwise it might
* prevent underlying thread processing. A portable user of the callback
* should not make calls to network operations due to stack size limitations.
* The callback should not perform expensive operations such as socket recv/send
* calls or blocking operations.
*
* @return Call in callback
*/
virtual call_in_callback_cb_t get_call_in_callback();
/** Call a callback after a delay
*
* Call a callback from the network stack context after a delay. If function
* returns error callback will not be called.
*
* @param delay Delay in milliseconds
* @param func Callback to be called
* @return 0 on success, negative error code on failure
*/
virtual nsapi_error_t call_in(int delay, mbed::Callback<void()> func);
};
/** Convert a raw nsapi_stack_t object into a C++ NetworkStack object
*

View File

@ -39,8 +39,6 @@ public:
*/
static OnboardNetworkStack &get_default_instance();
virtual OnboardNetworkStack *onboardNetworkStack() { return this; }
/** Representation of a stack's view of an interface.
*
* Provides facilities required by a driver to implement the application
@ -123,27 +121,6 @@ public:
virtual char *get_gateway(char *buf, nsapi_size_t buflen) = 0;
};
/** Call a callback
*
* Call a callback from the network stack context. If returns error
* callback will not be called.
*
* @param func Callback to be called
* @return 0 on success, negative error code on failure
*/
virtual nsapi_error_t call(mbed::Callback<void()> func) = 0;
/** Call a callback after a delay
*
* Call a callback from the network stack context after a delay. If
* returns error callback will not be called.
*
* @param delay Delay in milliseconds
* @param func Callback to be called
* @return 0 on success, negative error code on failure
*/
virtual nsapi_error_t call_in(int delay, mbed::Callback<void()> func) = 0;
/** Register a network interface with the IP stack
*
* Connects EMAC layer with the IP stack and initializes all the required infrastructure.

View File

@ -2,6 +2,22 @@
"name": "nsapi",
"config": {
"present": 1,
"default-stack": "LWIP"
"default-stack": "LWIP",
"dns-response-wait-time": {
"help": "How long the DNS translator waits for a reply from a server",
"value": 5000
},
"dns-total-retries": {
"help": "Number of total DNS query retries that the DNS translator makes",
"value": 3
},
"dns-retries": {
"help": "Number of DNS query retries that the DNS translator makes per server",
"value": 0
},
"dns-cache-size": {
"help": "Number of cached host name resolutions",
"value": 3
}
}
}

View File

@ -31,17 +31,32 @@
// DNS options
#define DNS_BUFFER_SIZE 512
#define DNS_TIMEOUT 5000
#define DNS_SERVERS_SIZE 5
#define DNS_RESPONSE_MIN_SIZE 12
#define DNS_MAX_TTL 604800
#define DNS_CACHE_SIZE 3
#define DNS_STACK_SERVERS_NUM 5
#define DNS_QUERY_QUEUE_SIZE 5
#define DNS_HOST_NAME_MAX_LEN 255
#ifndef MBED_CONF_NSAPI_DNS_RESPONSE_WAIT_TIME
#define MBED_CONF_NSAPI_DNS_RESPONSE_WAIT_TIME 5000
#endif
#ifndef MBED_CONF_NSAPI_DNS_TOTAL_RETRIES
#define MBED_CONF_NSAPI_DNS_TOTAL_RETRIES 3
#endif
#ifndef MBED_CONF_NSAPI_DNS_RETRIES
#define MBED_CONF_NSAPI_DNS_RETRIES 0
#endif
#ifndef MBED_CONF_NSAPI_DNS_CACHE_SIZE
#define MBED_CONF_NSAPI_DNS_CACHE_SIZE 3
#endif
struct DNS_CACHE {
SocketAddress address;
char host[128];
nsapi_addr_t address;
char *host;
uint64_t expires; /*!< time to live in milliseconds */
uint64_t accessed; /*!< last accessed */
};
@ -49,33 +64,35 @@ struct DNS_CACHE {
struct DNS_QUERY {
int unique_id;
NetworkStack *stack;
char host[128];
char *host;
NetworkStack::hostbyname_cb_t callback;
void *cb_data;
call_in_callback_cb_t call_in_cb;
nsapi_size_t addr_count;
nsapi_version_t version;
UDPSocket *socket;
nsapi_addr_t *addrs;
int dns_server;
int retries;
int total_retries;
int dns_message_id;
int count;
uint32_t ttl;
};
typedef nsapi_error_t (*nsapi_dns_call_t)(mbed::Callback<void()> func);
typedef nsapi_error_t (*nsapi_dns_call_in_t)(int delay, mbed::Callback<void()> func);
static void nsapi_dns_cache_add(const char *host, SocketAddress *address, uint32_t ttl);
static nsapi_size_or_error_t nsapi_dns_cache_find(const char *host, nsapi_version_t version, SocketAddress *address);
static void nsapi_dns_cache_add(const char *host, nsapi_addr_t *address, uint32_t ttl);
static nsapi_size_or_error_t nsapi_dns_cache_find(const char *host, nsapi_version_t version, nsapi_addr_t *address);
static nsapi_error_t nsapi_dns_get_server_addr(NetworkStack *stack, int *index, SocketAddress *dns_addr);
static nsapi_error_t nsapi_dns_get_server_addr(NetworkStack *stack, int *index, int *total_retries, SocketAddress *dns_addr);
static void nsapi_dns_query_async_create(DNS_QUERY *query);
static void nsapi_dns_query_async_create(void *ptr);
static nsapi_error_t nsapi_dns_query_async_delete(int unique_id);
static void nsapi_dns_query_async_send(void *ptr);
static void nsapi_dns_query_async_resp(DNS_QUERY *query, nsapi_error_t status, SocketAddress *address);
static void nsapi_dns_query_async_socket_callback(NetworkStack *stack);
static void nsapi_dns_query_async_socket_callback_handle(NetworkStack *stack);
static nsapi_error_t nsapi_dns_call_default(mbed::Callback<void()> func);
static nsapi_error_t nsapi_dns_call_in_default(int delay, mbed::Callback<void()> func);
static void nsapi_dns_query_async_response(void *ptr);
static nsapi_addr_t dns_servers[DNS_SERVERS_SIZE] = {
{NSAPI_IPv4, {8, 8, 8, 8}}, // Google
@ -87,13 +104,15 @@ static nsapi_addr_t dns_servers[DNS_SERVERS_SIZE] = {
0,0, 0,0, 0x1c,0x04, 0xb1,0x2f}},
};
static DNS_CACHE *dns_cache[DNS_CACHE_SIZE];
static DNS_CACHE *dns_cache[MBED_CONF_NSAPI_DNS_CACHE_SIZE];
static uint16_t dns_message_id = 0;
static int dns_unique_id = 0;
static int dns_unique_id = 1;
static DNS_QUERY *dns_query_queue[DNS_QUERY_QUEUE_SIZE];
// Protects cache shared between blocking and asynchronous calls
static rtos::Mutex dns_cache_mutex;
static nsapi_dns_call_t dns_call = nsapi_dns_call_default;
static nsapi_dns_call_in_t dns_call_in = nsapi_dns_call_in_default;
// Protects from several threads running asynchronous DNS
static rtos::Mutex dns_mutex;
static nsapi_dns_call_in_t dns_call_in = 0;
// DNS server configuration
extern "C" nsapi_error_t nsapi_dns_add_server(nsapi_addr_t addr)
@ -199,7 +218,7 @@ static int dns_scan_response(const uint8_t *ptr, uint16_t exp_id, uint32_t *ttl,
// verify header is response to query
if (!(id == exp_id && qr && opcode == 0 && rcode == 0)) {
return 0;
return -1;
}
// skip questions
@ -273,7 +292,7 @@ static int dns_scan_response(const uint8_t *ptr, uint16_t exp_id, uint32_t *ttl,
return count;
}
static void nsapi_dns_cache_add(const char *host, SocketAddress *address, uint32_t ttl)
static void nsapi_dns_cache_add(const char *host, nsapi_addr_t *address, uint32_t ttl)
{
// RFC 1034: if TTL is zero, entry is not added to cache
if (!ttl) {
@ -281,17 +300,17 @@ static void nsapi_dns_cache_add(const char *host, SocketAddress *address, uint32
}
// Checks if already cached
if (nsapi_dns_cache_find(host, address->get_ip_version(), NULL) == NSAPI_ERROR_OK) {
if (nsapi_dns_cache_find(host, address->version, NULL) == NSAPI_ERROR_OK) {
return;
}
dns_cache_mutex.lock();
int index = -1;
uint64_t accessed = -1;
uint64_t accessed = ~0;
// Finds free or last accessed entry
for (int i = 0; i < DNS_CACHE_SIZE; i++) {
for (int i = 0; i < MBED_CONF_NSAPI_DNS_CACHE_SIZE; i++) {
if (!dns_cache[i]) {
index = i;
break;
@ -302,17 +321,21 @@ static void nsapi_dns_cache_add(const char *host, SocketAddress *address, uint32
}
if (index < 0) {
dns_cache_mutex.unlock();
return;
}
// Allocates in case entry is free, otherwise reuses
if (!dns_cache[index]) {
dns_cache[index] = new DNS_CACHE;
dns_cache[index] = new (std::nothrow) DNS_CACHE;
} else {
free(dns_cache[index]->host);
}
if (dns_cache[index]) {
dns_cache[index]->address = *address;
strncpy(dns_cache[index]->host, host, 127);
dns_cache[index]->host = (char *) malloc(strlen(host) + 1);
strcpy(dns_cache[index]->host, host);
uint64_t ms_count = rtos::Kernel::get_ms_count();
dns_cache[index]->expires = ms_count + ttl * 1000;
dns_cache[index]->accessed = ms_count;
@ -321,21 +344,22 @@ static void nsapi_dns_cache_add(const char *host, SocketAddress *address, uint32
dns_cache_mutex.unlock();
}
static nsapi_error_t nsapi_dns_cache_find(const char *host, nsapi_version_t version, SocketAddress *address)
static nsapi_error_t nsapi_dns_cache_find(const char *host, nsapi_version_t version, nsapi_addr_t *address)
{
nsapi_error_t ret_val = NSAPI_ERROR_NO_ADDRESS;
dns_cache_mutex.lock();
for (int i = 0; i < DNS_CACHE_SIZE; i++) {
for (int i = 0; i < MBED_CONF_NSAPI_DNS_CACHE_SIZE; i++) {
if (dns_cache[i]) {
uint64_t ms_count = rtos::Kernel::get_ms_count();
// Checks all entries for expired entries
if (ms_count > dns_cache[i]->expires) {
free(dns_cache[i]->host);
delete dns_cache[i];
dns_cache[i] = NULL;
} else if (((version == NSAPI_UNSPEC) || (version == dns_cache[i]->address.get_ip_version())) &&
(strncmp(dns_cache[i]->host, host, 127) == 0)) {
} else if ((version == NSAPI_UNSPEC || version == dns_cache[i]->address.version) &&
strcmp(dns_cache[i]->host, host) == 0) {
if (address) {
*address = dns_cache[i]->address;
}
@ -350,14 +374,22 @@ static nsapi_error_t nsapi_dns_cache_find(const char *host, nsapi_version_t vers
return ret_val;
}
static nsapi_error_t nsapi_dns_get_server_addr(NetworkStack *stack, int *index, SocketAddress *dns_addr)
static nsapi_error_t nsapi_dns_get_server_addr(NetworkStack *stack, int *index, int *total_retries, SocketAddress *dns_addr)
{
bool dns_addr_set = false;
if (*index >= DNS_SERVERS_SIZE + DNS_STACK_SERVERS_NUM) {
if (*total_retries == 0) {
return NSAPI_ERROR_NO_ADDRESS;
}
if (*index >= DNS_SERVERS_SIZE + DNS_STACK_SERVERS_NUM) {
if (*total_retries) {
*index = 0;
} else {
return NSAPI_ERROR_NO_ADDRESS;
}
}
if (*index < DNS_STACK_SERVERS_NUM) {
nsapi_error_t ret = stack->get_dns_server(*index, dns_addr);
if (ret < 0) {
@ -382,14 +414,12 @@ static nsapi_size_or_error_t nsapi_dns_query_multiple(NetworkStack *stack, const
{
// check for valid host name
int host_len = host ? strlen(host) : 0;
if (host_len > 128 || host_len == 0) {
if (host_len > DNS_HOST_NAME_MAX_LEN || host_len == 0) {
return NSAPI_ERROR_PARAMETER;
}
// check cache
SocketAddress address;
if (nsapi_dns_cache_find(host, version, &address) == NSAPI_ERROR_OK) {
*addr = address.get_addr();
if (nsapi_dns_cache_find(host, version, addr) == NSAPI_ERROR_OK) {
return 1;
}
@ -400,7 +430,7 @@ static nsapi_size_or_error_t nsapi_dns_query_multiple(NetworkStack *stack, const
return err;
}
socket.set_timeout(DNS_TIMEOUT);
socket.set_timeout(MBED_CONF_NSAPI_DNS_RESPONSE_WAIT_TIME);
// create network packet
uint8_t * const packet = (uint8_t *)malloc(DNS_BUFFER_SIZE);
@ -410,14 +440,15 @@ static nsapi_size_or_error_t nsapi_dns_query_multiple(NetworkStack *stack, const
nsapi_size_or_error_t result = NSAPI_ERROR_DNS_FAILURE;
bool retry = false;
int retries = MBED_CONF_NSAPI_DNS_RETRIES;
int index = 0;
int total_retries = MBED_CONF_NSAPI_DNS_TOTAL_RETRIES;
// check against each dns server
while (true) {
SocketAddress dns_addr;
err = nsapi_dns_get_server_addr(stack, &index, &dns_addr);
err = nsapi_dns_get_server_addr(stack, &index, &total_retries, &dns_addr);
if (err != NSAPI_ERROR_OK) {
break;
}
@ -429,20 +460,24 @@ static nsapi_size_or_error_t nsapi_dns_query_multiple(NetworkStack *stack, const
// send may fail for various reasons, including wrong address type - move on
if (err < 0) {
// goes to next dns server
retry = false;
retries = MBED_CONF_NSAPI_DNS_RETRIES;
index++;
continue;
}
if (total_retries) {
total_retries--;
}
// recv the response
err = socket.recvfrom(NULL, packet, DNS_BUFFER_SIZE);
if (err == NSAPI_ERROR_WOULD_BLOCK) {
if (!retry) {
// retries once
retry = true;
if (retries) {
// retries
retries--;
} else {
// goes to next dns server
retry = false;
retries = MBED_CONF_NSAPI_DNS_RETRIES;
index++;
}
continue;
@ -453,13 +488,12 @@ static nsapi_size_or_error_t nsapi_dns_query_multiple(NetworkStack *stack, const
const uint8_t *response = packet;
uint32_t ttl;
int count = dns_scan_response(response, 1, &ttl, addr, addr_count);
if (count > 0) {
// Adds address to cache
SocketAddress address(*addr);
nsapi_dns_cache_add(host, &address, ttl);
result = count;
int resp = dns_scan_response(response, 1, &ttl, addr, addr_count);
if (resp > 0) {
nsapi_dns_cache_add(host, addr, ttl);
result = resp;
} else if (resp < 0) {
continue;
}
/* The DNS response is final, no need to check other servers */
@ -490,7 +524,7 @@ extern "C" nsapi_size_or_error_t nsapi_dns_query_multiple(nsapi_stack_t *stack,
nsapi_size_or_error_t nsapi_dns_query_multiple(NetworkStack *stack, const char *host,
SocketAddress *addresses, nsapi_size_t addr_count, nsapi_version_t version)
{
nsapi_addr_t *addrs = new nsapi_addr_t[addr_count];
nsapi_addr_t *addrs = new (std::nothrow) nsapi_addr_t[addr_count];
nsapi_size_or_error_t result = nsapi_dns_query_multiple(stack, host, addrs, addr_count, version);
if (result > 0) {
@ -521,39 +555,10 @@ nsapi_error_t nsapi_dns_query(NetworkStack *stack, const char *host,
}
nsapi_error_t nsapi_dns_query_async(NetworkStack *stack, const char *host,
NetworkStack::hostbyname_cb_t callback, void *data, nsapi_version_t version)
NetworkStack::hostbyname_cb_t callback, call_in_callback_cb_t call_in_cb,
nsapi_version_t version)
{
nsapi_size_or_error_t result = nsapi_dns_query_multiple_async(stack, host, callback, data, 1, version);
return (nsapi_error_t)((result > 0) ? 0 : result);
}
static nsapi_error_t nsapi_dns_call_default(mbed::Callback<void()> func)
{
events::EventQueue *event_queue = mbed::mbed_event_queue();
if (!event_queue) {
return NSAPI_ERROR_NO_MEMORY;
}
if (event_queue->call(func) == 0) {
return NSAPI_ERROR_NO_MEMORY;
}
return NSAPI_ERROR_OK ;
}
static nsapi_error_t nsapi_dns_call_in_default(int delay, mbed::Callback<void()> func)
{
events::EventQueue *event_queue = mbed::mbed_event_queue();
if (!event_queue) {
return NSAPI_ERROR_NO_MEMORY;
}
if (event_queue->call_in(delay, func) == 0) {
return NSAPI_ERROR_NO_MEMORY;
}
return NSAPI_ERROR_OK ;
}
void nsapi_dns_call_set(nsapi_dns_call_t callback)
{
dns_call = callback;
return nsapi_dns_query_multiple_async(stack, host, callback, 0, call_in_cb, version);
}
void nsapi_dns_call_in_set(nsapi_dns_call_in_t callback)
@ -561,32 +566,19 @@ void nsapi_dns_call_in_set(nsapi_dns_call_in_t callback)
dns_call_in = callback;
}
static nsapi_error_t nsapi_dns_call(NetworkStack *stack, mbed::Callback<void()> func)
nsapi_error_t nsapi_dns_call_in(call_in_callback_cb_t cb, int delay, mbed::Callback<void()> func)
{
if (stack->onboardNetworkStack()) {
OnboardNetworkStack *onboard_stack = reinterpret_cast<OnboardNetworkStack *>(stack);
return onboard_stack->call(func);
} else {
dns_call(func);
}
return NSAPI_ERROR_OK;
}
static nsapi_error_t nsapi_dns_call_in(NetworkStack *stack, int delay, mbed::Callback<void()> func)
{
if (stack->onboardNetworkStack()) {
OnboardNetworkStack *onboard_stack = reinterpret_cast<OnboardNetworkStack *>(stack);
return onboard_stack->call_in(delay, func);
} else {
if (dns_call_in) {
dns_call_in(delay, func);
} else {
return cb(delay, func);
}
return NSAPI_ERROR_OK;
}
nsapi_error_t nsapi_dns_query_multiple_async(NetworkStack *stack, const char *host,
NetworkStack::hostbyname_cb_t callback, void *data, nsapi_size_t addr_count, nsapi_version_t version)
NetworkStack::hostbyname_cb_t callback, nsapi_size_t addr_count,
call_in_callback_cb_t call_in_cb, nsapi_version_t version)
{
if (!stack) {
return NSAPI_ERROR_PARAMETER;
@ -594,129 +586,87 @@ nsapi_error_t nsapi_dns_query_multiple_async(NetworkStack *stack, const char *ho
// check for valid host name
int host_len = host ? strlen(host) : 0;
if (host_len > 128 || host_len == 0) {
if (host_len > DNS_HOST_NAME_MAX_LEN || host_len == 0) {
return NSAPI_ERROR_PARAMETER;
}
DNS_QUERY *query = new DNS_QUERY;
nsapi_addr address;
if (nsapi_dns_cache_find(host, version, &address) == NSAPI_ERROR_OK) {
SocketAddress addr(address);
callback(NSAPI_ERROR_OK, &addr);
return NSAPI_ERROR_OK;
}
if (!query) {
dns_mutex.lock();
int index = -1;
for (int i = 0; i < DNS_QUERY_QUEUE_SIZE; i++) {
if (!dns_query_queue[i]) {
index = i;
break;
}
}
if (index < 0) {
dns_mutex.unlock();
return NSAPI_ERROR_NO_MEMORY;
}
query->unique_id = 0;
DNS_QUERY *query = new (std::nothrow) DNS_QUERY;
if (!query) {
dns_mutex.unlock();
return NSAPI_ERROR_NO_MEMORY;
}
query->host = (char *) malloc(host_len + 1);
strcpy(query->host, host);
query->callback = callback;
query->cb_data = data;
query->call_in_cb = call_in_cb;
query->stack = stack;
query->addr_count = addr_count;
query->version = version;
query->socket = NULL;
query->addrs = NULL;
query->dns_server = 0;
query->retries = 2;
query->retries = MBED_CONF_NSAPI_DNS_RETRIES + 1;
query->total_retries = MBED_CONF_NSAPI_DNS_TOTAL_RETRIES;
query->dns_message_id = -1;
if (nsapi_dns_call(stack, mbed::callback(nsapi_dns_query_async_create, query)) != NSAPI_ERROR_OK) {
delete query;
return NSAPI_ERROR_NO_MEMORY;
}
return NSAPI_ERROR_IN_PROGRESS ;
}
static void nsapi_dns_query_async_create(DNS_QUERY *query)
{
SocketAddress address;
if (nsapi_dns_cache_find(query->host, query->version, &address) == NSAPI_ERROR_OK) {
nsapi_dns_query_async_resp(query, NSAPI_ERROR_OK, &address);
return;
}
int index = -1;
for (int i = 0; i < DNS_QUERY_QUEUE_SIZE; i++) {
if (dns_query_queue[i]) {
if (dns_query_queue[i]->stack == query->stack) {
query->socket = dns_query_queue[i]->socket;
}
} else if (index < 0) {
index = i;
}
}
if (index < 0) {
nsapi_dns_query_async_resp(query, NSAPI_ERROR_NO_MEMORY, NULL);
return;
}
UDPSocket *socket;
if (query->socket) {
socket = query->socket;
} else {
socket = new UDPSocket;
if (!socket) {
nsapi_dns_query_async_resp(query, NSAPI_ERROR_NO_MEMORY, NULL);
return;
}
int err = socket->open(query->stack);
if (err) {
delete socket;
nsapi_dns_query_async_resp(query, err, NULL);
return;
}
socket->set_timeout(0);
socket->sigio(mbed::callback(nsapi_dns_query_async_socket_callback, query->stack));
query->socket = socket;
}
query->unique_id = dns_unique_id++;
if (query->unique_id > 0x7FFF) {
query->unique_id = 1;
}
dns_query_queue[index] = query;
nsapi_dns_query_async_send(reinterpret_cast<void *>(query->unique_id));
}
static void nsapi_dns_query_async_delete(DNS_QUERY *query)
{
int index = -1;
bool close_socket = true;
for (int i = 0; i < DNS_QUERY_QUEUE_SIZE; i++) {
if (dns_query_queue[i]) {
if (dns_query_queue[i] == query) {
index = i;
} else if (dns_query_queue[i]->stack == query->stack) {
close_socket = false;
}
}
}
if (index < 0) {
return;
}
if (close_socket) {
query->socket->close();
delete query->socket;
}
dns_query_queue[index] = NULL;
if (nsapi_dns_call_in(query->call_in_cb, 0, mbed::callback(nsapi_dns_query_async_create, reinterpret_cast<void *>(query->unique_id))) != NSAPI_ERROR_OK) {
delete query;
dns_mutex.unlock();
return NSAPI_ERROR_NO_MEMORY;
}
static void nsapi_dns_query_async_resp(DNS_QUERY *query, nsapi_error_t status, SocketAddress *address)
{
query->callback(status, address, query->cb_data);
nsapi_dns_query_async_delete(query);
dns_mutex.unlock();
return query->unique_id;
}
static void nsapi_dns_query_async_send(void *ptr)
nsapi_error_t nsapi_dns_query_async_cancel(nsapi_error_t id)
{
dns_mutex.lock();
nsapi_error_t ret = nsapi_dns_query_async_delete(id);
dns_mutex.unlock();
return ret;
}
static void nsapi_dns_query_async_create(void *ptr)
{
dns_mutex.lock();
int unique_id = reinterpret_cast<int>(ptr);
DNS_QUERY *query = NULL;
@ -729,7 +679,114 @@ static void nsapi_dns_query_async_send(void *ptr)
}
if (!query) {
// Cancel has been called
dns_mutex.unlock();
return;
}
for (int i = 0; i < DNS_QUERY_QUEUE_SIZE; i++) {
if (dns_query_queue[i] && dns_query_queue[i] != query) {
if (dns_query_queue[i]->socket && dns_query_queue[i]->stack == query->stack) {
query->socket = dns_query_queue[i]->socket;
}
}
}
UDPSocket *socket;
if (query->socket) {
socket = query->socket;
} else {
socket = new (std::nothrow) UDPSocket;
if (!socket) {
nsapi_dns_query_async_resp(query, NSAPI_ERROR_NO_MEMORY, NULL);
dns_mutex.unlock();
return;
}
int err = socket->open(query->stack);
if (err) {
delete socket;
nsapi_dns_query_async_resp(query, err, NULL);
dns_mutex.unlock();
return;
}
socket->set_timeout(0);
socket->sigio(mbed::callback(nsapi_dns_query_async_socket_callback, query->stack));
query->socket = socket;
}
dns_mutex.unlock();
nsapi_dns_query_async_send(reinterpret_cast<void *>(query->unique_id));
}
static nsapi_error_t nsapi_dns_query_async_delete(int unique_id)
{
int index = -1;
for (int i = 0; i < DNS_QUERY_QUEUE_SIZE; i++) {
if (dns_query_queue[i] && dns_query_queue[i]->unique_id == unique_id) {
index = i;
break;
}
}
if (index < 0) {
return NSAPI_ERROR_DEVICE_ERROR;
}
bool close_socket = true;
for (int i = 0; i < DNS_QUERY_QUEUE_SIZE; i++) {
if (i != index && dns_query_queue[i] && dns_query_queue[i]->socket &&
dns_query_queue[i]->stack == dns_query_queue[index]->stack) {
close_socket = false;
}
}
if (close_socket && dns_query_queue[index]->socket) {
dns_query_queue[index]->socket->close();
delete dns_query_queue[index]->socket;
}
if (dns_query_queue[index]->addrs) {
delete[] dns_query_queue[index]->addrs;
}
free(dns_query_queue[index]->host);
delete dns_query_queue[index];
dns_query_queue[index] = NULL;
return NSAPI_ERROR_OK;
}
static void nsapi_dns_query_async_resp(DNS_QUERY *query, nsapi_error_t status, SocketAddress *address)
{
query->callback(status, address);
nsapi_dns_query_async_delete(query->unique_id);
}
static void nsapi_dns_query_async_send(void *ptr)
{
dns_mutex.lock();
int unique_id = reinterpret_cast<int>(ptr);
DNS_QUERY *query = NULL;
for (int i = 0; i < DNS_QUERY_QUEUE_SIZE; i++) {
if (dns_query_queue[i] && dns_query_queue[i]->unique_id == unique_id) {
query = dns_query_queue[i];
break;
}
}
if (!query) {
// Cancel has been called
dns_mutex.unlock();
return;
}
@ -737,7 +794,7 @@ static void nsapi_dns_query_async_send(void *ptr)
query->retries--;
} else {
query->dns_server++;
query->retries = 1;
query->retries = MBED_CONF_NSAPI_DNS_RETRIES;
}
query->dns_message_id = dns_message_id++;
@ -746,6 +803,7 @@ static void nsapi_dns_query_async_send(void *ptr)
uint8_t *packet = (uint8_t *)malloc(DNS_BUFFER_SIZE);
if (!packet) {
nsapi_dns_query_async_resp(query, NSAPI_ERROR_NO_MEMORY, NULL);
dns_mutex.unlock();
return;
}
@ -754,10 +812,11 @@ static void nsapi_dns_query_async_send(void *ptr)
while (true) {
SocketAddress dns_addr;
nsapi_size_or_error_t err = nsapi_dns_get_server_addr(query->stack, &(query->dns_server), &dns_addr);
nsapi_size_or_error_t err = nsapi_dns_get_server_addr(query->stack, &(query->dns_server), &(query->total_retries), &dns_addr);
if (err != NSAPI_ERROR_OK) {
nsapi_dns_query_async_resp(query, NSAPI_ERROR_DNS_FAILURE, NULL);
free(packet);
dns_mutex.unlock();
return;
}
@ -770,23 +829,26 @@ static void nsapi_dns_query_async_send(void *ptr)
}
}
if (query->total_retries) {
query->total_retries--;
}
free(packet);
if (nsapi_dns_call_in(query->stack, DNS_TIMEOUT,
if (nsapi_dns_call_in(query->call_in_cb, MBED_CONF_NSAPI_DNS_RESPONSE_WAIT_TIME,
mbed::callback(nsapi_dns_query_async_send, reinterpret_cast<void *>(unique_id))) != NSAPI_ERROR_OK) {
nsapi_dns_query_async_resp(query, NSAPI_ERROR_NO_MEMORY, NULL);
}
dns_mutex.unlock();
}
static void nsapi_dns_query_async_socket_callback(NetworkStack *stack)
{
nsapi_dns_call(stack, mbed::callback(nsapi_dns_query_async_socket_callback_handle, stack));
}
static void nsapi_dns_query_async_socket_callback_handle(NetworkStack *stack)
{
UDPSocket *socket = NULL;
dns_mutex.lock();
for (int i = 0; i < DNS_QUERY_QUEUE_SIZE; i++) {
if (dns_query_queue[i] && dns_query_queue[i]->stack == stack) {
socket = dns_query_queue[i]->socket;
@ -798,19 +860,20 @@ static void nsapi_dns_query_async_socket_callback_handle(NetworkStack *stack)
// create network packet
uint8_t *packet = (uint8_t *)malloc(DNS_BUFFER_SIZE);
if (!packet) {
dns_mutex.unlock();
return;
}
while (true) {
// recv the response
nsapi_size_or_error_t size = socket->recvfrom(NULL, packet, DNS_BUFFER_SIZE);
if (size < DNS_RESPONSE_MIN_SIZE) {
free(packet);
return;
break;
}
// gets id from response to associate with correct query
uint16_t id = (*packet << 8) | *(packet + 1);
uint16_t id = (packet[0] << 8) | packet[1];
DNS_QUERY *query = NULL;
@ -822,34 +885,70 @@ static void nsapi_dns_query_async_socket_callback_handle(NetworkStack *stack)
}
if (!query) {
free(packet);
return;
continue;
}
nsapi_addr_t *addrs = new nsapi_addr_t[query->addr_count];
int requested_count = 1;
if (query->addr_count > 1) {
requested_count = query->addr_count;
}
uint32_t ttl;
int count = dns_scan_response((const uint8_t *) packet, id, &ttl, addrs, query->addr_count);
query->addrs = new (std::nothrow) nsapi_addr_t[requested_count];
int resp = dns_scan_response(packet, id, &(query->ttl), query->addrs, requested_count);
// Ignore invalid responses
if (resp < 0) {
delete[] query->addrs;
query->addrs = 0;
} else {
query->count = resp;
nsapi_dns_call_in(query->call_in_cb, 0, mbed::callback(nsapi_dns_query_async_response, reinterpret_cast<void *>(query->unique_id)));
}
}
free(packet);
}
if (count > 0) {
SocketAddress *addresses = new SocketAddress[count];
dns_mutex.unlock();
}
for (int i = 0; i < count; i++) {
addresses[i].set_addr(addrs[i]);
static void nsapi_dns_query_async_response(void *ptr)
{
dns_mutex.lock();
int unique_id = reinterpret_cast<int>(ptr);
DNS_QUERY *query = NULL;
for (int i = 0; i < DNS_QUERY_QUEUE_SIZE; i++) {
if (dns_query_queue[i] && dns_query_queue[i]->unique_id == unique_id) {
query = dns_query_queue[i];
break;
}
}
if (query) {
if (query->count > 0) {
SocketAddress *addresses = new (std::nothrow) SocketAddress[query->count];
for (int i = 0; i < query->count; i++) {
addresses[i].set_addr(query->addrs[i]);
}
// Adds address to cache
nsapi_dns_cache_add(query->host, addresses, ttl);
nsapi_dns_query_async_resp(query, NSAPI_ERROR_OK, addresses);
nsapi_dns_cache_add(query->host, &(query->addrs[0]), query->ttl);
nsapi_error_t status = NSAPI_ERROR_OK;
if (query->addr_count > 0) {
status = query->count;
}
nsapi_dns_query_async_resp(query, status, addresses);
delete[] addresses;
} else {
nsapi_dns_query_async_resp(query, NSAPI_ERROR_DNS_FAILURE, NULL);
}
}
dns_mutex.unlock();
}
delete[] addrs;
}
}

View File

@ -63,6 +63,7 @@ nsapi_error_t nsapi_dns_add_server(nsapi_addr_t addr);
#else
typedef mbed::Callback<nsapi_error_t (int delay_ms, mbed::Callback<void()> user_cb)> call_in_callback_cb_t;
/** Query a domain name server for an IP address of a given hostname
*
@ -80,14 +81,15 @@ nsapi_error_t nsapi_dns_query(NetworkStack *stack, const char *host,
*
* @param stack Network stack as target for DNS query
* @param host Hostname to resolve
* @param callback Callback that is called for result*
* @param data Caller defined data returned in callback
* @param callback Callback that is called for result
* @param version IP version to resolve (defaults to NSAPI_IPv4)
* @return 0 on success, negative error code on failure
* NSAPI_ERROR_DNS_FAILURE indicates the host could not be found
* @return 0 on success, negative error code on failure or an unique id that
* represents the hostname translation operation and can be passed to
* cancel, NSAPI_ERROR_DNS_FAILURE indicates the host could not be found
*/
nsapi_error_t nsapi_dns_query_async(NetworkStack *stack, const char *host,
NetworkStack::hostbyname_cb_t callback, void *data, nsapi_version_t version = NSAPI_IPv4);
NetworkStack::hostbyname_cb_t callback, call_in_callback_cb_t call_in_cb,
nsapi_version_t version = NSAPI_IPv4);
/** Query a domain name server for an IP address of a given hostname (asynchronous)
*
@ -135,14 +137,15 @@ nsapi_size_or_error_t nsapi_dns_query_multiple(NetworkStack *stack, const char *
* @param stack Network stack as target for DNS query
* @param host Hostname to resolve
* @param callback Callback that is called for result
* @param data Caller defined data returned in callback
* @param addr_count Number of addresses allocated in the array
* @param version IP version to resolve (defaults to NSAPI_IPv4)
* @return 0 on success, negative error code on failure
* NSAPI_ERROR_DNS_FAILURE indicates the host could not be found
* @return 0 on success, negative error code on failure or an unique id that
represents the hostname translation operation and can be passed to
* cancel, NSAPI_ERROR_DNS_FAILURE indicates the host could not be found
*/
nsapi_size_or_error_t nsapi_dns_query_multiple_async(NetworkStack *stack, const char *host,
NetworkStack::hostbyname_cb_t callback, void *data, nsapi_size_t addr_count, nsapi_version_t version = NSAPI_IPv4);
NetworkStack::hostbyname_cb_t callback, nsapi_size_t addr_count,
call_in_callback_cb_t call_in_cb, nsapi_version_t version = NSAPI_IPv4);
/** Query a domain name server for multiple IP address of a given hostname
*
@ -176,6 +179,15 @@ nsapi_size_or_error_t nsapi_dns_query_multiple(S *stack, const char *host,
host, addr, addr_count, version);
}
/** Cancels asynchronous hostname translation
*
* When translation is cancelled, callback will not be called.
*
* @param id Unique id of the hostname translation operation
* @return 0 on success, negative error code on failure
*/
nsapi_error_t nsapi_dns_query_async_cancel(nsapi_error_t id);
/** Add a domain name server to list of servers to query
*
* @param addr Destination for the host address