diff --git a/features/FEATURE_LWIP/lwip-interface/LWIPStack.cpp b/features/FEATURE_LWIP/lwip-interface/LWIPStack.cpp index a3b017f729..ab57277591 100644 --- a/features/FEATURE_LWIP/lwip-interface/LWIPStack.cpp +++ b/features/FEATURE_LWIP/lwip-interface/LWIPStack.cpp @@ -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(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(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 func) +{ + return call_in(0, func); +} + +nsapi_error_t LWIP::call_in(int delay, mbed::Callback 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); diff --git a/features/FEATURE_LWIP/lwip-interface/LWIPStack.h b/features/FEATURE_LWIP/lwip-interface/LWIPStack.h index 706474f3a3..9d23360d1c 100644 --- a/features/FEATURE_LWIP/lwip-interface/LWIPStack.h +++ b/features/FEATURE_LWIP/lwip-interface/LWIPStack.h @@ -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 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 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 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_ */ diff --git a/features/FEATURE_LWIP/lwip-interface/lwip-sys/arch/lwip_sys_arch.c b/features/FEATURE_LWIP/lwip-interface/lwip-sys/arch/lwip_sys_arch.c index f46d5e6133..a17f90e2dd 100644 --- a/features/FEATURE_LWIP/lwip-interface/lwip-sys/arch/lwip_sys_arch.c +++ b/features/FEATURE_LWIP/lwip-interface/lwip-sys/arch/lwip_sys_arch.c @@ -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]; diff --git a/features/FEATURE_LWIP/lwip-interface/lwip-sys/arch/sys_arch.h b/features/FEATURE_LWIP/lwip-interface/lwip-sys/arch/sys_arch.h index 57fb5cf60c..f46f4bc514 100644 --- a/features/FEATURE_LWIP/lwip-interface/lwip-sys/arch/sys_arch.h +++ b/features/FEATURE_LWIP/lwip-interface/lwip-sys/arch/sys_arch.h @@ -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" { diff --git a/features/FEATURE_LWIP/lwip-interface/lwip/src/api/lwip_api_lib.c b/features/FEATURE_LWIP/lwip-interface/lwip/src/api/lwip_api_lib.c index 3c1d6a6c27..fee745c30b 100644 --- a/features/FEATURE_LWIP/lwip-interface/lwip/src/api/lwip_api_lib.c +++ b/features/FEATURE_LWIP/lwip-interface/lwip/src/api/lwip_api_lib.c @@ -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; } /** diff --git a/features/FEATURE_LWIP/lwip-interface/lwip/src/core/lwip_dns.c b/features/FEATURE_LWIP/lwip-interface/lwip/src/core/lwip_dns.c index 12c6f16fe3..fe87133b7d 100644 --- a/features/FEATURE_LWIP/lwip-interface/lwip/src/core/lwip_dns.c +++ b/features/FEATURE_LWIP/lwip-interface/lwip/src/core/lwip_dns.c @@ -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 */ diff --git a/features/FEATURE_LWIP/lwip-interface/lwip/src/include/lwip/api.h b/features/FEATURE_LWIP/lwip-interface/lwip/src/include/lwip/api.h index 516bd163dd..3503a1c93d 100644 --- a/features/FEATURE_LWIP/lwip-interface/lwip/src/include/lwip/api.h +++ b/features/FEATURE_LWIP/lwip-interface/lwip/src/include/lwip/api.h @@ -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); diff --git a/features/FEATURE_LWIP/lwip-interface/lwip_tools.cpp b/features/FEATURE_LWIP/lwip-interface/lwip_tools.cpp index d7fdfc822b..0e7ee26aa4 100644 --- a/features/FEATURE_LWIP/lwip-interface/lwip_tools.cpp +++ b/features/FEATURE_LWIP/lwip-interface/lwip_tools.cpp @@ -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; } diff --git a/features/FEATURE_LWIP/lwip-interface/lwipopts.h b/features/FEATURE_LWIP/lwip-interface/lwipopts.h index d4bf8a9520..41b6943f1d 100644 --- a/features/FEATURE_LWIP/lwip-interface/lwipopts.h +++ b/features/FEATURE_LWIP/lwip-interface/lwipopts.h @@ -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 diff --git a/features/nanostack/nanostack-interface/Nanostack.cpp b/features/nanostack/nanostack-interface/Nanostack.cpp index b585e8fac4..243f21bd79 100644 --- a/features/nanostack/nanostack-interface/Nanostack.cpp +++ b/features/nanostack/nanostack-interface/Nanostack.cpp @@ -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(event->data_ptr); + cb->callback(); + delete cb; + } +} + +nsapi_error_t Nanostack::call(mbed::Callback func) +{ + return call_in(0, func); +} + +nsapi_error_t Nanostack::call_in(int delay, mbed::Callback 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; diff --git a/features/nanostack/nanostack-interface/Nanostack.h b/features/nanostack/nanostack-interface/Nanostack.h index da94e15865..e9a4865c15 100644 --- a/features/nanostack/nanostack-interface/Nanostack.h +++ b/features/nanostack/nanostack-interface/Nanostack.h @@ -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 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 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 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); diff --git a/features/netsocket/NetworkInterface.cpp b/features/netsocket/NetworkInterface.cpp index 67ef5c8712..3694f8e8e6 100644 --- a/features/netsocket/NetworkInterface.cpp +++ b/features/netsocket/NetworkInterface.cpp @@ -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); diff --git a/features/netsocket/NetworkInterface.h b/features/netsocket/NetworkInterface.h index dae2703ef7..54552cda85 100644 --- a/features/netsocket/NetworkInterface.h +++ b/features/netsocket/NetworkInterface.h @@ -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 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 diff --git a/features/netsocket/NetworkStack.cpp b/features/netsocket/NetworkStack.cpp index 515aa08290..7a2233793d 100644 --- a/features/netsocket/NetworkStack.cpp +++ b/features/netsocket/NetworkStack.cpp @@ -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; diff --git a/features/netsocket/NetworkStack.h b/features/netsocket/NetworkStack.h index 18cfeeacea..2d4718ec5a 100644 --- a/features/netsocket/NetworkStack.h +++ b/features/netsocket/NetworkStack.h @@ -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 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; diff --git a/features/netsocket/OnboardNetworkStack.h b/features/netsocket/OnboardNetworkStack.h index 8e4a6f7778..bead0def60 100644 --- a/features/netsocket/OnboardNetworkStack.h +++ b/features/netsocket/OnboardNetworkStack.h @@ -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 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 func) = 0; /** Register a network interface with the IP stack * diff --git a/features/netsocket/nsapi_dns.cpp b/features/netsocket/nsapi_dns.cpp index 0c270bb17c..d4a9f8d11f 100644 --- a/features/netsocket/nsapi_dns.cpp +++ b/features/netsocket/nsapi_dns.cpp @@ -19,6 +19,10 @@ #include #include #include +#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 func); +typedef nsapi_error_t (*nsapi_dns_call_in_t)(int delay, mbed::Callback 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 func); +static nsapi_error_t nsapi_dns_call_in_default(int delay, mbed::Callback 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 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 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 func) +{ + if (stack->onboardNetworkStack()) { + OnboardNetworkStack *onboard_stack = reinterpret_cast(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 func) +{ + if (stack->onboardNetworkStack()) { + OnboardNetworkStack *onboard_stack = reinterpret_cast(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(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(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(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; + } +} diff --git a/features/netsocket/nsapi_dns.h b/features/netsocket/nsapi_dns.h index 0575ae35f9..3503d6eafa 100644 --- a/features/netsocket/nsapi_dns.h +++ b/features/netsocket/nsapi_dns.h @@ -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