Added non-blocking DNS functionality to network interface

- Added non-blocking DNS interface to network interface and
  network stack.
- Added caching of DNS replies.
- Added a network stack function to get DNS addresses from stack.
- Added call and call_in hooks to onboard network stack to
  allow calling functions from onboard stack context.
- Added support to call and call_in functions to LWIP and
  Nanostack.
- Disabled LWIP DNS translator with the exception of DNS
  address storage used in DNS address get.
pull/6847/head
Mika Leppänen 2018-04-20 15:29:46 +03:00 committed by Kevin Bracey
parent bad530ab0d
commit b7e8400c2c
18 changed files with 994 additions and 117 deletions

View File

@ -33,6 +33,7 @@
#include "lwip/dns.h"
#include "lwip/udp.h"
#include "lwip/lwip_errno.h"
#include "lwip-sys/arch/sys_arch.h"
#include "LWIPStack.h"
@ -47,10 +48,10 @@ void LWIP::socket_callback(struct netconn *nc, enum netconn_evt eh, u16_t len)
return;
}
sys_prot_t prot = sys_arch_protect();
LWIP &lwip = LWIP::get_instance();
lwip.adaptation.lock();
for (int i = 0; i < MEMP_NUM_NETCONN; i++) {
if (lwip.arena[i].in_use
&& lwip.arena[i].conn == nc
@ -59,7 +60,7 @@ void LWIP::socket_callback(struct netconn *nc, enum netconn_evt eh, u16_t len)
}
}
sys_arch_unprotect(prot);
lwip.adaptation.unlock();
}
#if !LWIP_IPV4 || !LWIP_IPV6
@ -149,6 +150,7 @@ void LWIP::tcpip_init_irq(void *eh)
{
LWIP *lwip = static_cast<LWIP *>(eh);
lwip->tcpip_inited.release();
sys_tcpip_thread_set();
}
/* LWIP network stack implementation */
@ -173,80 +175,84 @@ LWIP::LWIP()
arena_init();
}
nsapi_error_t LWIP::gethostbyname(const char *host, SocketAddress *address, nsapi_version_t version)
nsapi_error_t LWIP::get_dns_server(int index, SocketAddress *address)
{
ip_addr_t lwip_addr;
int dns_entries = 0;
#if LWIP_IPV4 && LWIP_IPV6
u8_t addr_type;
if (version == NSAPI_UNSPEC) {
const ip_addr_t *ip_addr = NULL;
if (default_interface) {
ip_addr = get_ip_addr(true, &default_interface->netif);
}
// Prefer IPv6
if (IP_IS_V6(ip_addr)) {
// If IPv4 is available use it as backup
if (get_ipv4_addr(&default_interface->netif)) {
addr_type = NETCONN_DNS_IPV6_IPV4;
} else {
addr_type = NETCONN_DNS_IPV6;
}
// Prefer IPv4
} else {
// If IPv6 is available use it as backup
if (get_ipv6_addr(&default_interface->netif)) {
addr_type = NETCONN_DNS_IPV4_IPV6;
} else {
addr_type = NETCONN_DNS_IPV4;
for (int i = 0; i < DNS_MAX_SERVERS; i++) {
const ip_addr_t *ip_addr = dns_getserver(i);
if (!ip_addr_isany(ip_addr)) {
if (index == dns_entries) {
nsapi_addr_t addr;
convert_lwip_addr_to_mbed(&addr, ip_addr);
address->set_addr(addr);
return NSAPI_ERROR_OK;
}
dns_entries++;
}
} else if (version == NSAPI_IPv4) {
addr_type = NETCONN_DNS_IPV4;
} else if (version == NSAPI_IPv6) {
addr_type = NETCONN_DNS_IPV6;
} else {
return NSAPI_ERROR_DNS_FAILURE;
}
err_t err = netconn_gethostbyname_addrtype(host, &lwip_addr, addr_type);
#elif LWIP_IPV4
if (version != NSAPI_IPv4 && version != NSAPI_UNSPEC) {
return NSAPI_ERROR_DNS_FAILURE;
}
err_t err = netconn_gethostbyname(host, &lwip_addr);
#elif LWIP_IPV6
if (version != NSAPI_IPv6 && version != NSAPI_UNSPEC) {
return NSAPI_ERROR_DNS_FAILURE;
}
err_t err = netconn_gethostbyname(host, &lwip_addr);
#endif
if (err != ERR_OK) {
return NSAPI_ERROR_DNS_FAILURE;
}
nsapi_addr_t addr;
convert_lwip_addr_to_mbed(&addr, &lwip_addr);
address->set_addr(addr);
return 0;
return NSAPI_ERROR_NO_ADDRESS;
}
nsapi_error_t LWIP::add_dns_server(const SocketAddress &address)
void LWIP::tcpip_thread_callback(void *ptr)
{
// Shift all dns servers down to give precedence to new server
for (int i = DNS_MAX_SERVERS-1; i > 0; i--) {
dns_setserver(i, dns_getserver(i-1));
lwip_callback *cb = static_cast<lwip_callback *>(ptr);
if (cb->delay) {
sys_timeout(cb->delay, LWIP::tcpip_thread_callback, ptr);
cb->delay = 0;
} else {
cb->callback();
delete cb;
}
}
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;
if (!cb) {
return NSAPI_ERROR_NO_MEMORY;
}
nsapi_addr_t addr = address.get_addr();
ip_addr_t ip_addr;
if (!convert_mbed_addr_to_lwip(&ip_addr, &addr)) {
return NSAPI_ERROR_PARAMETER;
cb->delay = delay;
cb->callback = func;
if (tcpip_callback_with_block(LWIP::tcpip_thread_callback, cb, 1) != ERR_OK) {
return NSAPI_ERROR_NO_MEMORY;
}
dns_setserver(0, &ip_addr);
return 0;
return NSAPI_ERROR_OK;
}
const char *LWIP::get_ip_address()
{
if (!default_interface) {
return NULL;
}
const ip_addr_t *addr = get_ip_addr(true, &default_interface->netif);
if (!addr) {
return NULL;
}
#if LWIP_IPV6
if (IP_IS_V6(addr)) {
return ip6addr_ntoa_r(ip_2_ip6(addr), ip_address, sizeof(ip_address));
}
#endif
#if LWIP_IPV4
if (IP_IS_V4(addr)) {
return ip4addr_ntoa_r(ip_2_ip4(addr), ip_address, sizeof(ip_address));
}
#endif
#if LWIP_IPV6 && LWIP_IPV4
return NULL;
#endif
}
nsapi_error_t LWIP::socket_open(nsapi_socket_t *handle, nsapi_protocol_t proto)
@ -439,6 +445,7 @@ nsapi_size_or_error_t LWIP::socket_sendto(nsapi_socket_t handle, const SocketAdd
}
struct netbuf *buf = netbuf_new();
err_t err = netbuf_ref(buf, data, (u16_t)size);
if (err != ERR_OK) {
netbuf_free(buf);
@ -588,7 +595,7 @@ nsapi_error_t LWIP::setsockopt(nsapi_socket_t handle, int level, int optname, co
member_pair_index = next_free_multicast_member(s, 0);
sys_prot_t prot = sys_arch_protect();
adaptation.lock();
#if LWIP_IPV4
if (IP_IS_V4(&if_addr)) {
@ -601,7 +608,7 @@ nsapi_error_t LWIP::setsockopt(nsapi_socket_t handle, int level, int optname, co
}
#endif
sys_arch_unprotect(prot);
adaptation.unlock();
if (igmp_err == ERR_OK) {
set_multicast_member_registry_bit(s, member_pair_index);
@ -616,7 +623,7 @@ nsapi_error_t LWIP::setsockopt(nsapi_socket_t handle, int level, int optname, co
clear_multicast_member_registry_bit(s, member_pair_index);
s->multicast_memberships_count--;
sys_prot_t prot = sys_arch_protect();
adaptation.lock();
#if LWIP_IPV4
if (IP_IS_V4(&if_addr)) {
@ -629,7 +636,7 @@ nsapi_error_t LWIP::setsockopt(nsapi_socket_t handle, int level, int optname, co
}
#endif
sys_arch_unprotect(prot);
adaptation.unlock();
}
return err_remap(igmp_err);

View File

@ -198,29 +198,44 @@ public:
*/
nsapi_error_t _add_ppp_interface(void *pcb, bool default_if, LWIP::Interface **interface_out);
/** Translates a hostname to an IP address with specific version
/** Get a domain name server from a list of servers to query
*
* 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);
/** Add a domain name server to list of servers to query
* Returns a DNS server address for a index. If returns error no more
* DNS servers to read.
*
* @param index Index of the DNS server, starts from zero
* @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);
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
* or null if not yet connected
*/
virtual const char *get_ip_address();
protected:
LWIP();
@ -439,6 +454,11 @@ private:
uint32_t multicast_memberships_registry;
};
struct lwip_callback {
unsigned int delay;
mbed::Callback<void()> callback;
};
static nsapi_error_t err_remap(err_t err);
static bool is_local_addr(const ip_addr_t *ip_addr);
static const ip_addr_t *get_ip_addr(bool any_addr, const struct netif *netif);
@ -475,9 +495,14 @@ private:
static void socket_callback(struct netconn *nc, enum netconn_evt eh, u16_t len);
static void tcpip_init_irq(void *handle);
static void tcpip_thread_callback(void *ptr);
char ip_address[40];
rtos::Semaphore tcpip_inited;
Interface *default_interface;
LWIPMemoryManager memory_manager;
osThreadId tcpip_thread_id;
rtos::Mutex adaptation;
};
#endif /* LWIPSTACK_H_ */

View File

@ -480,6 +480,24 @@ void sys_msleep(u32_t ms) {
osDelay(ms);
}
osThreadId_t lwip_tcpip_thread_id = 0;
bool 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;
}
}
// Keep a pool of thread structures
static int thread_pool_index = 0;
static sys_thread_data_t thread_pool[SYS_THREAD_POOL_N];

View File

@ -85,6 +85,9 @@ typedef sys_thread_data_t* sys_thread_t;
// === PROTECTION ===
typedef int sys_prot_t;
bool sys_tcpip_thread_set(void);
bool sys_tcpip_thread_check(void);
#else
#ifdef __cplusplus
extern "C" {

View File

@ -102,11 +102,18 @@ netconn_apimsg(tcpip_callback_fn fn, struct api_msg *apimsg)
apimsg->op_completed_sem = LWIP_NETCONN_THREAD_SEM_GET();
#endif /* LWIP_NETCONN_SEM_PER_THREAD */
err = tcpip_send_msg_wait_sem(fn, apimsg, LWIP_API_MSG_SEM(apimsg));
if (err == ERR_OK) {
return apimsg->err;
if (sys_tcpip_thread_check()) {
fn(apimsg);
return ERR_OK;
} else {
err = tcpip_send_msg_wait_sem(fn, apimsg, LWIP_API_MSG_SEM(apimsg));
if (err == ERR_OK) {
return apimsg->err;
}
return err;
}
return err;
}
/**

View File

@ -283,16 +283,18 @@ static void dns_init_local(void);
static err_t dns_lookup_local(const char *hostname, ip_addr_t *addr LWIP_DNS_ADDRTYPE_ARG(u8_t dns_addrtype));
#endif /* DNS_LOCAL_HOSTLIST */
#if LWIP_FULL_DNS
/* forward declarations */
static void dns_recv(void *s, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port);
static void dns_check_entries(void);
static void dns_call_found(u8_t idx, ip_addr_t* addr);
#endif
/*-----------------------------------------------------------------------------
* Globals
*----------------------------------------------------------------------------*/
#if LWIP_FULL_DNS
/* DNS variables */
static struct udp_pcb *dns_pcbs[DNS_MAX_SOURCE_PORTS];
#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0)
@ -301,6 +303,7 @@ static u8_t dns_last_pcb_idx;
static u8_t dns_seqno;
static struct dns_table_entry dns_table[DNS_TABLE_SIZE];
static struct dns_req_entry dns_requests[DNS_MAX_REQUESTS];
#endif
static ip_addr_t dns_servers[DNS_MAX_SERVERS];
#if LWIP_IPV4
@ -324,6 +327,7 @@ dns_init(void)
dns_setserver(0, &dnsserver);
#endif /* DNS_SERVER_ADDRESS */
#if LWIP_FULL_DNS
LWIP_ASSERT("sanity check SIZEOF_DNS_QUERY",
sizeof(struct dns_query) == SIZEOF_DNS_QUERY);
LWIP_ASSERT("sanity check SIZEOF_DNS_ANSWER",
@ -351,6 +355,7 @@ dns_init(void)
#if DNS_LOCAL_HOSTLIST
dns_init_local();
#endif
#endif /* LWIP_FULL_DNS */
}
/**
@ -397,10 +402,14 @@ dns_getserver(u8_t numdns)
void
dns_tmr(void)
{
#if LWIP_FULL_DNS
LWIP_DEBUGF(DNS_DEBUG, ("dns_tmr: dns_check_entries\n"));
dns_check_entries();
#endif
}
#if LWIP_FULL_DNS
#if DNS_LOCAL_HOSTLIST
static void
dns_init_local(void)
@ -1570,4 +1579,6 @@ dns_gethostbyname_addrtype(const char *hostname, ip_addr_t *addr, dns_found_call
LWIP_DNS_ISMDNS_ARG(is_mdns));
}
#endif /* LWIP_FULL_DNS */
#endif /* LWIP_DNS */

View File

@ -290,8 +290,8 @@ struct netconn {
/** @ingroup netconn_common
* Create new netconn connection
* @param t @ref netconn_type */
#define netconn_new(t) netconn_new_with_proto_and_callback(t, 0, NULL)
#define netconn_new_with_callback(t, c) netconn_new_with_proto_and_callback(t, 0, c)
#define netconn_new(t) netconn_new_with_proto_and_callback(t, 0, NULL)
#define netconn_new_with_callback(t, c) netconn_new_with_proto_and_callback(t, 0, c)
struct netconn *netconn_new_with_proto_and_callback(enum netconn_type t, u8_t proto,
netconn_callback callback);
err_t netconn_delete(struct netconn *conn);

View File

@ -157,19 +157,22 @@ void LWIP::arena_init(void)
struct LWIP::mbed_lwip_socket *LWIP::arena_alloc()
{
sys_prot_t prot = sys_arch_protect();
LWIP &lwip = LWIP::get_instance();
lwip.adaptation.lock();
for (int i = 0; i < MEMP_NUM_NETCONN; i++) {
if (!arena[i].in_use) {
struct mbed_lwip_socket *s = &arena[i];
memset(s, 0, sizeof(*s));
s->in_use = true;
sys_arch_unprotect(prot);
lwip.adaptation.unlock();
return s;
}
}
sys_arch_unprotect(prot);
lwip.adaptation.unlock();
return 0;
}

View File

@ -213,6 +213,8 @@
#endif
#define LWIP_DNS 1
// Only DNS address storage is enabled
#define LWIP_FULL_DNS 0
#define LWIP_SOCKET 0
#define SO_REUSE 1

View File

@ -25,6 +25,7 @@
#include "ns_address.h"
#include "nsdynmemLIB.h"
#include "eventOS_scheduler.h"
#include "eventOS_event_timer.h"
#include "randLIB.h"
#include "ip6string.h"
@ -57,6 +58,7 @@ enum socket_mode_t {
SOCKET_MODE_LISTENING, // Socket is listening for connections
};
#define CALL_EVENT 0x12
class NanostackSocket {
public:
@ -444,6 +446,65 @@ void NanostackSocket::event_connection_reset(socket_callback_t *sock_cb)
close();
}
Nanostack::Nanostack()
: call_event_tasklet(-1)
{
}
void Nanostack::call_event_tasklet_main(arm_event_s *event)
{
if (event->event_id == CALL_EVENT) {
nanostack_callback *cb = static_cast<nanostack_callback *>(event->data_ptr);
cb->callback();
delete cb;
}
}
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) {
call_event_tasklet = eventOS_event_handler_create(&call_event_tasklet_main, 0);
if (call_event_tasklet < 0) {
return NSAPI_ERROR_NO_MEMORY;
}
}
nanostack_callback *cb = new nanostack_callback;
if (!cb) {
return NSAPI_ERROR_NO_MEMORY;
}
cb->callback = func;
arm_event_s event;
event.sender = call_event_tasklet,
event.event_id = CALL_EVENT,
event.receiver = call_event_tasklet,
event.data_ptr = cb;
event.event_type = APPLICATION_EVENT;
event.priority = ARM_LIB_LOW_PRIORITY_EVENT;
if (delay) {
uint32_t ticks = eventOS_event_timer_ms_to_ticks(delay);
if (!eventOS_event_send_in(&event, ticks)) {
return NSAPI_ERROR_NO_MEMORY;
}
} else {
if (eventOS_event_send(&event) < 0) {
return NSAPI_ERROR_NO_MEMORY;
}
}
return NSAPI_ERROR_OK;
}
const char * Nanostack::get_ip_address()
{
NanostackLockGuard lock;

View File

@ -23,6 +23,7 @@
#include "NanostackMemoryManager.h"
#include "MeshInterface.h"
#include "mesh_interface_types.h"
#include "eventOS_event.h"
struct ns_address;
@ -43,8 +44,31 @@ 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();
/** Get the local IP address
*
* @return Null-terminated representation of the local IP address
@ -242,9 +266,15 @@ protected:
virtual nsapi_error_t getsockopt(void *handle, int level, int optname, void *optval, unsigned *optlen);
private:
struct nanostack_callback {
mbed::Callback<void()> callback;
};
nsapi_size_or_error_t do_sendto(void *handle, const struct ns_address *address, const void *data, nsapi_size_t size);
static void call_event_tasklet_main(arm_event_s *event);
char text_ip_address[40];
NanostackMemoryManager memory_manager;
int8_t call_event_tasklet;
};
nsapi_error_t map_mesh_error(mesh_error_t err);

View File

@ -60,6 +60,11 @@ 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)
{
return get_stack()->gethostbyname_async(host, callback, data, version);
}
nsapi_error_t NetworkInterface::add_dns_server(const SocketAddress &address)
{
return get_stack()->add_dns_server(address);

View File

@ -127,6 +127,38 @@ public:
virtual nsapi_error_t gethostbyname(const char *host,
SocketAddress *address, nsapi_version_t version = NSAPI_UNSPEC);
/** Hostname translation callback (asynchronous)
*
* Callback will be called after DNS resolution completes or a failure
* occurs.
*
* @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;
/** 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.
*
* @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
*/
virtual nsapi_error_t gethostbyname_async(const char *host, hostbyname_cb_t callback, void *data,
nsapi_version_t version = NSAPI_UNSPEC);
/** Add a domain name server to list of servers to query
*
* @param address Destination for the host address

View File

@ -49,11 +49,41 @@ 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)
{
SocketAddress address;
// check for simple ip addresses
if (address.set_ip_address(name)) {
if (version != NSAPI_UNSPEC && address.get_ip_version() != version) {
return NSAPI_ERROR_DNS_FAILURE;
}
return NSAPI_ERROR_OK;
}
// if the version is unspecified, try to guess the version from the
// ip address of the underlying stack
if (version == NSAPI_UNSPEC) {
SocketAddress testaddress;
if (testaddress.set_ip_address(this->get_ip_address())) {
version = testaddress.get_ip_version();
}
}
return nsapi_dns_query_async(this, name, callback, data, version);
}
nsapi_error_t NetworkStack::add_dns_server(const SocketAddress &address)
{
return nsapi_dns_add_server(address);
}
nsapi_error_t NetworkStack::get_dns_server(int index, SocketAddress *address)
{
return NSAPI_ERROR_UNSUPPORTED;
}
nsapi_error_t NetworkStack::setstackopt(int level, int optname, const void *optval, unsigned optlen)
{
return NSAPI_ERROR_UNSUPPORTED;

View File

@ -22,6 +22,8 @@
#include "netsocket/SocketAddress.h"
#include "netsocket/NetworkInterface.h"
// Predeclared classes
class OnboardNetworkStack;
/** NetworkStack class
*
@ -37,13 +39,10 @@ public:
virtual ~NetworkStack() {};
/** Get the local IP address
* @deprecated
*
* @return Null-terminated representation of the local IP address
* or null if not yet connected
*/
MBED_DEPRECATED_SINCE("mbed-os-5.7",
"Use NetworkInterface::get_ip_address()")
virtual const char *get_ip_address();
/** Translates a hostname to an IP address with specific version
@ -63,6 +62,38 @@ public:
virtual nsapi_error_t gethostbyname(const char *host,
SocketAddress *address, nsapi_version_t version = NSAPI_UNSPEC);
/** Hostname translation callback (asynchronous)
*
* Callback will be called after DNS resolution completes or a failure
* occurs.
*
* @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;
/** 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.
*
* @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
*/
virtual nsapi_error_t gethostbyname_async(const char *host, hostbyname_cb_t callback, void *data,
nsapi_version_t version = NSAPI_UNSPEC);
/** Add a domain name server to list of servers to query
*
* @param address Destination for the host address
@ -70,6 +101,17 @@ public:
*/
virtual nsapi_error_t add_dns_server(const SocketAddress &address);
/** Get a domain name server from a list of servers to query
*
* Returns a DNS server address for a index. If returns error no more
* DNS servers to read.
*
* @param index Index of the DNS server, starts from zero
* @param address Destination for the host address
* @return 0 on success, negative error code on failure
*/
virtual nsapi_error_t get_dns_server(int index, SocketAddress *address);
/* Set stack options
*
* setstackopt allows an application to pass stack-specific options
@ -101,6 +143,9 @@ public:
*/
virtual nsapi_error_t getstackopt(int level, int optname, void *optval, unsigned *optlen);
/** Dynamic downcast to a OnboardNetworkStack */
virtual OnboardNetworkStack *onboardNetworkStack() { return 0; }
protected:
friend class Socket;
friend class UDPSocket;

View File

@ -39,6 +39,8 @@ 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
@ -121,6 +123,26 @@ 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
*

View File

@ -19,6 +19,10 @@
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "mbed_shared_queues.h"
#include "EventQueue.h"
#include "OnboardNetworkStack.h"
#include "Kernel.h"
#define CLASS_IN 1
@ -29,8 +33,51 @@
#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
nsapi_addr_t dns_servers[DNS_SERVERS_SIZE] = {
struct DNS_CACHE {
SocketAddress address;
char host[128];
uint64_t expires; /*!< time to live in milliseconds */
uint64_t accessed; /*!< last accessed */
};
struct DNS_QUERY {
int unique_id;
NetworkStack *stack;
char host[128];
NetworkStack::hostbyname_cb_t callback;
void *cb_data;
nsapi_size_t addr_count;
nsapi_version_t version;
UDPSocket *socket;
int dns_server;
int retries;
int dns_message_id;
};
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 nsapi_error_t nsapi_dns_get_server_addr(NetworkStack *stack, int *index, SocketAddress *dns_addr);
static void nsapi_dns_query_async_create(DNS_QUERY *query);
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 nsapi_addr_t dns_servers[DNS_SERVERS_SIZE] = {
{NSAPI_IPv4, {8, 8, 8, 8}}, // Google
{NSAPI_IPv4, {209, 244, 0, 3}}, // Level 3
{NSAPI_IPv4, {84, 200, 69, 80}}, // DNS.WATCH
@ -40,6 +87,14 @@ 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 uint16_t dns_message_id = 0;
static int dns_unique_id = 0;
static DNS_QUERY *dns_query_queue[DNS_QUERY_QUEUE_SIZE];
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;
// DNS server configuration
extern "C" nsapi_error_t nsapi_dns_add_server(nsapi_addr_t addr)
{
@ -83,11 +138,23 @@ static uint16_t dns_scan_word(const uint8_t **p)
return (a << 8) | b;
}
static void dns_append_question(uint8_t **p, const char *host, nsapi_version_t version)
static uint32_t dns_scan_word32(const uint8_t **p)
{
uint32_t value = dns_scan_byte(p) << 24;
value |= dns_scan_byte(p) << 16;
value |= dns_scan_byte(p) << 8;
value |= dns_scan_byte(p);
return value;
}
static int dns_append_question(uint8_t *ptr, uint16_t id, const char *host, nsapi_version_t version)
{
uint8_t *s_ptr = ptr;
uint8_t **p = &ptr;
// fill the header
dns_append_word(p, 1); // id = 1
dns_append_word(p, id); // id = 1
dns_append_word(p, 0x0100); // flags = recursion required
dns_append_word(p, 1); // qdcount = 1
dns_append_word(p, 0); // ancount = 0
@ -110,10 +177,14 @@ static void dns_append_question(uint8_t **p, const char *host, nsapi_version_t v
dns_append_word(p, RR_AAAA); // qtype = ipv6
}
dns_append_word(p, CLASS_IN);
return *p - s_ptr;
}
static int dns_scan_response(const uint8_t **p, nsapi_addr_t *addr, unsigned addr_count)
static int dns_scan_response(const uint8_t *ptr, uint16_t exp_id, uint32_t *ttl, nsapi_addr_t *addr, unsigned addr_count)
{
const uint8_t **p = &ptr;
// scan header
uint16_t id = dns_scan_word(p);
uint16_t flags = dns_scan_word(p);
@ -127,7 +198,7 @@ static int dns_scan_response(const uint8_t **p, nsapi_addr_t *addr, unsigned add
dns_scan_word(p); // arcount
// verify header is response to query
if (!(id == 1 && qr && opcode == 0 && rcode == 0)) {
if (!(id == exp_id && qr && opcode == 0 && rcode == 0)) {
return 0;
}
@ -162,10 +233,18 @@ static int dns_scan_response(const uint8_t **p, nsapi_addr_t *addr, unsigned add
*p += len;
}
uint16_t rtype = dns_scan_word(p); // rtype
uint16_t rclass = dns_scan_word(p); // rclass
*p += 4; // ttl
uint16_t rdlength = dns_scan_word(p); // rdlength
uint16_t rtype = dns_scan_word(p); // rtype
uint16_t rclass = dns_scan_word(p); // rclass
uint32_t ttl_val = dns_scan_word32(p); // ttl
uint16_t rdlength = dns_scan_word(p); // rdlength
if (i == 0) {
// Is interested only on first address that is stored to cache
if (ttl_val > DNS_MAX_TTL) {
ttl_val = DNS_MAX_TTL;
}
*ttl = ttl_val;
}
if (rtype == RR_A && rclass == CLASS_IN && rdlength == NSAPI_IPv4_BYTES) {
// accept A record
@ -194,6 +273,109 @@ static int dns_scan_response(const uint8_t **p, nsapi_addr_t *addr, unsigned add
return count;
}
static void nsapi_dns_cache_add(const char *host, SocketAddress *address, uint32_t ttl)
{
// RFC 1034: if TTL is zero, entry is not added to cache
if (!ttl) {
return;
}
// Checks if already cached
if (nsapi_dns_cache_find(host, address->get_ip_version(), NULL) == NSAPI_ERROR_OK) {
return;
}
dns_cache_mutex.lock();
int index = -1;
uint64_t accessed = -1;
// Finds free or last accessed entry
for (int i = 0; i < DNS_CACHE_SIZE; i++) {
if (!dns_cache[i]) {
index = i;
break;
} else if (dns_cache[i]->accessed <= accessed) {
accessed = dns_cache[i]->accessed;
index = i;
}
}
if (index < 0) {
return;
}
// Allocates in case entry is free, otherwise reuses
if (!dns_cache[index]) {
dns_cache[index] = new DNS_CACHE;
}
if (dns_cache[index]) {
dns_cache[index]->address = *address;
strncpy(dns_cache[index]->host, host, 127);
uint64_t ms_count = rtos::Kernel::get_ms_count();
dns_cache[index]->expires = ms_count + ttl * 1000;
dns_cache[index]->accessed = ms_count;
}
dns_cache_mutex.unlock();
}
static nsapi_error_t nsapi_dns_cache_find(const char *host, nsapi_version_t version, SocketAddress *address)
{
nsapi_error_t ret_val = NSAPI_ERROR_NO_ADDRESS;
dns_cache_mutex.lock();
for (int i = 0; i < 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) {
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)) {
if (address) {
*address = dns_cache[i]->address;
}
dns_cache[i]->accessed = ms_count;
ret_val = NSAPI_ERROR_OK;
}
}
}
dns_cache_mutex.unlock();
return ret_val;
}
static nsapi_error_t nsapi_dns_get_server_addr(NetworkStack *stack, int *index, SocketAddress *dns_addr)
{
bool dns_addr_set = false;
if (*index >= DNS_SERVERS_SIZE + DNS_STACK_SERVERS_NUM) {
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) {
*index = DNS_STACK_SERVERS_NUM;
} else {
dns_addr_set = true;
}
}
if (!dns_addr_set) {
dns_addr->set_addr(dns_servers[*index - DNS_STACK_SERVERS_NUM]);
}
dns_addr->set_port(53);
return NSAPI_ERROR_OK;
}
// core query function
static nsapi_size_or_error_t nsapi_dns_query_multiple(NetworkStack *stack, const char *host,
nsapi_addr_t *addr, unsigned addr_count, nsapi_version_t version)
@ -204,6 +386,13 @@ static nsapi_size_or_error_t nsapi_dns_query_multiple(NetworkStack *stack, const
return NSAPI_ERROR_PARAMETER;
}
// check cache
SocketAddress address;
if (nsapi_dns_cache_find(host, version, &address) == NSAPI_ERROR_OK) {
*addr = address.get_addr();
return 1;
}
// create a udp socket
UDPSocket socket;
int err = socket.open(stack);
@ -221,21 +410,41 @@ static nsapi_size_or_error_t nsapi_dns_query_multiple(NetworkStack *stack, const
nsapi_size_or_error_t result = NSAPI_ERROR_DNS_FAILURE;
// check against each dns server
for (unsigned i = 0; i < DNS_SERVERS_SIZE; i++) {
// send the question
uint8_t *question = packet;
dns_append_question(&question, host, version);
bool retry = false;
err = socket.sendto(SocketAddress(dns_servers[i], 53), packet, question - packet);
int index = 0;
// check against each dns server
while (true) {
SocketAddress dns_addr;
err = nsapi_dns_get_server_addr(stack, &index, &dns_addr);
if (err != NSAPI_ERROR_OK) {
break;
}
// send the question
int len = dns_append_question(packet, 1, host, version);
err = socket.sendto(dns_addr, packet, len);
// send may fail for various reasons, including wrong address type - move on
if (err < 0) {
// goes to next dns server
retry = false;
index++;
continue;
}
// recv the response
err = socket.recvfrom(NULL, packet, DNS_BUFFER_SIZE);
if (err == NSAPI_ERROR_WOULD_BLOCK) {
if (!retry) {
// retries once
retry = true;
} else {
// goes to next dns server
retry = false;
index++;
}
continue;
} else if (err < 0) {
result = err;
@ -243,8 +452,13 @@ static nsapi_size_or_error_t nsapi_dns_query_multiple(NetworkStack *stack, const
}
const uint8_t *response = packet;
int count = dns_scan_response(&response, addr, addr_count);
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;
}
@ -305,3 +519,337 @@ nsapi_error_t nsapi_dns_query(NetworkStack *stack, const char *host,
address->set_addr(addr);
return (nsapi_error_t)((result > 0) ? 0 : result);
}
nsapi_error_t nsapi_dns_query_async(NetworkStack *stack, const char *host,
NetworkStack::hostbyname_cb_t callback, void *data, 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;
}
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)
{
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 {
dns_call_in(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)
{
if (!stack) {
return NSAPI_ERROR_PARAMETER;
}
// check for valid host name
int host_len = host ? strlen(host) : 0;
if (host_len > 128 || host_len == 0) {
return NSAPI_ERROR_PARAMETER;
}
DNS_QUERY *query = new DNS_QUERY;
if (!query) {
return NSAPI_ERROR_NO_MEMORY;
}
query->unique_id = 0;
strcpy(query->host, host);
query->callback = callback;
query->cb_data = data;
query->stack = stack;
query->addr_count = addr_count;
query->version = version;
query->socket = NULL;
query->dns_server = 0;
query->retries = 2;
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++;
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;
delete query;
}
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);
}
static void nsapi_dns_query_async_send(void *ptr)
{
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) {
nsapi_dns_query_async_resp(query, NSAPI_ERROR_NO_MEMORY, NULL);
return;
}
if (query->retries) {
query->retries--;
} else {
query->dns_server++;
query->retries = 1;
}
query->dns_message_id = dns_message_id++;
// create network packet
uint8_t *packet = (uint8_t *)malloc(DNS_BUFFER_SIZE);
if (!packet) {
nsapi_dns_query_async_resp(query, NSAPI_ERROR_NO_MEMORY, NULL);
return;
}
// send the question
int len = dns_append_question(packet, query->dns_message_id, query->host, query->version);
while (true) {
SocketAddress dns_addr;
nsapi_size_or_error_t err = nsapi_dns_get_server_addr(query->stack, &(query->dns_server), &dns_addr);
if (err != NSAPI_ERROR_OK) {
nsapi_dns_query_async_resp(query, NSAPI_ERROR_DNS_FAILURE, NULL);
free(packet);
return;
}
err = query->socket->sendto(dns_addr, packet, len);
if (err < 0) {
query->dns_server++;
} else {
break;
}
}
free(packet);
if (nsapi_dns_call_in(query->stack, DNS_TIMEOUT,
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);
}
}
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;
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;
break;
}
}
if (socket) {
// create network packet
uint8_t *packet = (uint8_t *)malloc(DNS_BUFFER_SIZE);
if (!packet) {
return;
}
// 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;
}
// gets id from response to associate with correct query
uint16_t id = (*packet << 8) | *(packet + 1);
DNS_QUERY *query = NULL;
for (int i = 0; i < DNS_QUERY_QUEUE_SIZE; i++) {
if (dns_query_queue[i] && dns_query_queue[i]->dns_message_id == id) {
query = dns_query_queue[i];
break;
}
}
if (!query) {
free(packet);
return;
}
nsapi_addr_t *addrs = new nsapi_addr_t[query->addr_count];
uint32_t ttl;
int count = dns_scan_response((const uint8_t *) packet, id, &ttl, addrs, query->addr_count);
free(packet);
if (count > 0) {
SocketAddress *addresses = new SocketAddress[count];
for (int i = 0; i < count; i++) {
addresses[i].set_addr(addrs[i]);
}
// Adds address to cache
nsapi_dns_cache_add(query->host, addresses, ttl);
nsapi_dns_query_async_resp(query, NSAPI_ERROR_OK, addresses);
delete[] addresses;
} else {
nsapi_dns_query_async_resp(query, NSAPI_ERROR_DNS_FAILURE, NULL);
}
delete[] addrs;
}
}

View File

@ -77,6 +77,19 @@ nsapi_error_t nsapi_dns_query(NetworkStack *stack, const char *host,
SocketAddress *addr, nsapi_version_t version = NSAPI_IPv4);
/** Query a domain name server for an IP address of a given hostname
*
* @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 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
*/
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);
/** Query a domain name server for an IP address of a given hostname (asynchronous)
*
* @param stack Network stack as target for DNS query
* @param host Hostname to resolve
@ -117,6 +130,20 @@ nsapi_error_t nsapi_dns_query(S *stack, const char *host,
nsapi_size_or_error_t nsapi_dns_query_multiple(NetworkStack *stack, const char *host,
SocketAddress *addr, nsapi_size_t addr_count, nsapi_version_t version = NSAPI_IPv4);
/** Query a domain name server for an IP address of a given hostname (asynchronous)
*
* @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
*/
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);
/** Query a domain name server for multiple IP address of a given hostname
*
* @param stack Network stack as target for DNS query
@ -130,6 +157,7 @@ nsapi_size_or_error_t nsapi_dns_query_multiple(NetworkStack *stack, const char *
extern "C" nsapi_size_or_error_t nsapi_dns_query_multiple(nsapi_stack_t *stack, const char *host,
nsapi_addr_t *addr, nsapi_size_t addr_count, nsapi_version_t version = NSAPI_IPv4);
/** Query a domain name server for multiple IP address of a given hostname
*
* @param stack Network stack as target for DNS query