From 1c01f5dda464452ce73917cd716fa28376347345 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=20Lepp=C3=A4nen?= Date: Thu, 3 May 2018 16:13:10 +0300 Subject: [PATCH] Corrected defects - Changed call_in/call methods of the stack to callback provided by the stack - Specified what are limitations for operations that are made in callback - Added pure virtual class DNS that defines DNS operations - Added cancel operation and unique ID to DNS request used in cancel - Added DNS configuration options to netsocket/mbed_lib.json for retries, response wait time and cache size - Changed host name to use dynamic memory in DNS query list and cache, set maximum length for the name to 255 bytes. - Added mutex to asynchronous DNS - Reworked retries: there is now total retry count and a server specific count - Ignores invalid incoming UDP socket messages (DNS header is not valid), and retries DNS query - Reworked DNS module asynchronous operation functions - Corrected other review issues (nothrow new, missing free, missing mutex unlock etc.) --- .../FEATURE_LWIP/lwip-interface/LWIPStack.cpp | 11 +- .../FEATURE_LWIP/lwip-interface/LWIPStack.h | 52 +- .../lwip-sys/arch/lwip_sys_arch.c | 10 +- .../lwip-interface/lwip-sys/arch/sys_arch.h | 2 +- .../lwip/src/api/lwip_api_lib.c | 4 + .../lwip/src/api/lwip_api_msg.c | 2 + .../nanostack-interface/Nanostack.cpp | 13 +- .../nanostack/nanostack-interface/Nanostack.h | 53 +- features/netsocket/DNS.h | 96 +++ features/netsocket/NetworkInterface.cpp | 9 +- features/netsocket/NetworkInterface.h | 38 +- features/netsocket/NetworkStack.cpp | 45 +- features/netsocket/NetworkStack.h | 69 ++- features/netsocket/OnboardNetworkStack.h | 23 - features/netsocket/mbed_lib.json | 18 +- features/netsocket/nsapi_dns.cpp | 577 ++++++++++-------- features/netsocket/nsapi_dns.h | 30 +- 17 files changed, 695 insertions(+), 357 deletions(-) create mode 100644 features/netsocket/DNS.h diff --git a/features/FEATURE_LWIP/lwip-interface/LWIPStack.cpp b/features/FEATURE_LWIP/lwip-interface/LWIPStack.cpp index ab57277591..b2c3913efc 100644 --- a/features/FEATURE_LWIP/lwip-interface/LWIPStack.cpp +++ b/features/FEATURE_LWIP/lwip-interface/LWIPStack.cpp @@ -207,11 +207,6 @@ void LWIP::tcpip_thread_callback(void *ptr) } } -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; @@ -229,6 +224,12 @@ nsapi_error_t LWIP::call_in(int delay, mbed::Callback func) return NSAPI_ERROR_OK; } +LWIP::call_in_callback_cb_t LWIP::get_call_in_callback() +{ + call_in_callback_cb_t cb(this, &LWIP::call_in); + return cb; +} + const char *LWIP::get_ip_address() { if (!default_interface) { diff --git a/features/FEATURE_LWIP/lwip-interface/LWIPStack.h b/features/FEATURE_LWIP/lwip-interface/LWIPStack.h index 9d23360d1c..aa77d4c754 100644 --- a/features/FEATURE_LWIP/lwip-interface/LWIPStack.h +++ b/features/FEATURE_LWIP/lwip-interface/LWIPStack.h @@ -209,27 +209,6 @@ public: */ virtual nsapi_error_t get_dns_server(int index, SocketAddress *address); - /** Call a callback - * - * Call a callback from the network stack context. If returns error - * callback will not be called. - * - * @param func Callback to be called - * @return 0 on success, negative error code on failure - */ - virtual nsapi_error_t call(mbed::Callback 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 @@ -438,6 +417,37 @@ protected: int optname, void *optval, unsigned *optlen); private: + /** Call in callback + * + * Callback is used to call the call in method of the network stack. + */ + typedef mbed::Callback user_cb)> call_in_callback_cb_t; + + /** Get a call in callback + * + * Get a call in callback from the network stack context. + * + * Callback should not take more than 10ms to execute, otherwise it might + * prevent underlying thread processing. A portable user of the callback + * should not make calls to network operations due to stack size limitations. + * The callback should not perform expensive operations such as socket recv/send + * calls or blocking operations. + * + * @return Call in callback + */ + virtual call_in_callback_cb_t get_call_in_callback(); + + /** Call a callback after a delay + * + * Call a callback from the network stack context after a delay. If function + * returns error callback will not be called. + * + * @param delay Delay in milliseconds + * @param func Callback to be called + * @return 0 on success, negative error code on failure + */ + nsapi_error_t call_in(int delay, mbed::Callback func); + struct mbed_lwip_socket { bool in_use; 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 a17f90e2dd..00dc6026f7 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 @@ -482,20 +482,14 @@ void sys_msleep(u32_t ms) { osThreadId_t lwip_tcpip_thread_id = 0; -bool sys_tcpip_thread_set(void) +void sys_tcpip_thread_set(void) { lwip_tcpip_thread_id = osThreadGetId(); } bool sys_tcpip_thread_check(void) { - osThreadId_t thread_id = osThreadGetId(); - - if (thread_id == lwip_tcpip_thread_id) { - return true; - } else { - return false; - } + return osThreadGetId() == lwip_tcpip_thread_id; } // Keep a pool of thread structures 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 f46f4bc514..85ba1bf058 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,7 +85,7 @@ typedef sys_thread_data_t* sys_thread_t; // === PROTECTION === typedef int sys_prot_t; -bool sys_tcpip_thread_set(void); +void sys_tcpip_thread_set(void); bool sys_tcpip_thread_check(void); #else 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 fee745c30b..45f86e647f 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 @@ -910,6 +910,8 @@ netconn_join_leave_group(struct netconn *conn, #endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */ #if LWIP_DNS +#if LWIP_FULL_DNS + /** * @ingroup netconn_common * Execute a DNS query, only one IP address is returned @@ -989,6 +991,8 @@ netconn_gethostbyname(const char *name, ip_addr_t *addr) API_VAR_FREE(MEMP_DNS_API_MSG, msg); return err; } + +#endif /* LWIP_FULL_DNS */ #endif /* LWIP_DNS*/ #if LWIP_NETCONN_SEM_PER_THREAD diff --git a/features/FEATURE_LWIP/lwip-interface/lwip/src/api/lwip_api_msg.c b/features/FEATURE_LWIP/lwip-interface/lwip/src/api/lwip_api_msg.c index dd99c1e01b..a63098cf60 100644 --- a/features/FEATURE_LWIP/lwip-interface/lwip/src/api/lwip_api_msg.c +++ b/features/FEATURE_LWIP/lwip-interface/lwip/src/api/lwip_api_msg.c @@ -1892,6 +1892,7 @@ lwip_netconn_do_join_leave_group(void *m) #endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */ #if LWIP_DNS +#if LWIP_FULL_DNS /** * Callback function that is called when DNS name is resolved * (or on timeout). A waiting application thread is waked up by @@ -1943,5 +1944,6 @@ lwip_netconn_do_gethostbyname(void *arg) } } #endif /* LWIP_DNS */ +#endif /* LWIP_FULL_DNS */ #endif /* LWIP_NETCONN */ diff --git a/features/nanostack/nanostack-interface/Nanostack.cpp b/features/nanostack/nanostack-interface/Nanostack.cpp index 243f21bd79..494b68946f 100644 --- a/features/nanostack/nanostack-interface/Nanostack.cpp +++ b/features/nanostack/nanostack-interface/Nanostack.cpp @@ -461,11 +461,6 @@ void Nanostack::call_event_tasklet_main(arm_event_s *event) } } -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) { @@ -494,10 +489,12 @@ nsapi_error_t Nanostack::call_in(int delay, mbed::Callback func) if (delay) { uint32_t ticks = eventOS_event_timer_ms_to_ticks(delay); if (!eventOS_event_send_in(&event, ticks)) { + delete cb; return NSAPI_ERROR_NO_MEMORY; } } else { if (eventOS_event_send(&event) < 0) { + delete cb; return NSAPI_ERROR_NO_MEMORY; } } @@ -505,6 +502,12 @@ nsapi_error_t Nanostack::call_in(int delay, mbed::Callback func) return NSAPI_ERROR_OK; } +Nanostack::call_in_callback_cb_t Nanostack::get_call_in_callback() +{ + call_in_callback_cb_t cb(this, &Nanostack::call_in); + return cb; +} + const char * Nanostack::get_ip_address() { NanostackLockGuard lock; diff --git a/features/nanostack/nanostack-interface/Nanostack.h b/features/nanostack/nanostack-interface/Nanostack.h index e9a4865c15..8bc3fb2c97 100644 --- a/features/nanostack/nanostack-interface/Nanostack.h +++ b/features/nanostack/nanostack-interface/Nanostack.h @@ -44,27 +44,6 @@ public: /* Local variant with stronger typing and manual address specification */ nsapi_error_t add_ethernet_interface(EMAC &emac, bool default_if, Nanostack::EthernetInterface **interface_out, const uint8_t *mac_addr = NULL); - /** Call a callback - * - * Call a callback from the network stack context. If returns error - * callback will not be called. - * - * @param func Callback to be called - * @return 0 on success, negative error code on failure - */ - virtual nsapi_error_t call(mbed::Callback 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(); @@ -266,6 +245,38 @@ protected: virtual nsapi_error_t getsockopt(void *handle, int level, int optname, void *optval, unsigned *optlen); private: + + /** Call in callback + * + * Callback is used to call the call in method of the network stack. + */ + typedef mbed::Callback user_cb)> call_in_callback_cb_t; + + /** Get a call in callback + * + * Get a call in callback from the network stack context. + * + * Callback should not take more than 10ms to execute, otherwise it might + * prevent underlying thread processing. A portable user of the callback + * should not make calls to network operations due to stack size limitations. + * The callback should not perform expensive operations such as socket recv/send + * calls or blocking operations. + * + * @return Call in callback + */ + virtual call_in_callback_cb_t get_call_in_callback(); + + /** Call a callback after a delay + * + * Call a callback from the network stack context after a delay. If function + * returns error callback will not be called. + * + * @param delay Delay in milliseconds + * @param func Callback to be called + * @return 0 on success, negative error code on failure + */ + nsapi_error_t call_in(int delay, mbed::Callback func); + struct nanostack_callback { mbed::Callback callback; }; diff --git a/features/netsocket/DNS.h b/features/netsocket/DNS.h new file mode 100644 index 0000000000..f033cc30e2 --- /dev/null +++ b/features/netsocket/DNS.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2018 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DNS_H +#define DNS_H + +class DNS { +public: + + /** Translates a hostname to an IP address with specific version + * + * The hostname may be either a domain name or an IP address. If the + * hostname is an IP address, no network transactions will be performed. + * + * If no stack-specific DNS resolution is provided, the hostname + * will be resolve using a UDP socket on the stack. + * + * @param host Hostname to resolve + * @param address Destination for the host SocketAddress + * @param version IP version of address to resolve, NSAPI_UNSPEC indicates + * version is chosen by the stack (defaults to NSAPI_UNSPEC) + * @return 0 on success, negative error code on failure + */ + virtual nsapi_error_t gethostbyname(const char *host, + SocketAddress *address, nsapi_version_t version = NSAPI_UNSPEC) = 0; + + /** Hostname translation callback (asynchronous) + * + * Callback will be called after DNS resolution completes or a failure occurs. + * + * Callback should not take more than 10ms to execute, otherwise it might + * prevent underlying thread processing. A portable user of the callback + * should not make calls to network operations due to stack size limitations. + * The callback should not perform expensive operations such as socket recv/send + * calls or blocking operations. + * + * @param status 0 on success, negative error code on failure + * @param address On success, destination for the host SocketAddress + */ + typedef mbed::Callback hostbyname_cb_t; + + /** Translates a hostname to an IP address (asynchronous) + * + * The hostname may be either a domain name or an IP address. If the + * hostname is an IP address, no network transactions will be performed. + * + * If no stack-specific DNS resolution is provided, the hostname + * will be resolve using a UDP socket on the stack. + * + * Call is non-blocking. Result of the DNS operation is returned by the callback. + * If this function returns failure, callback will not be called. In case result + * is success (IP address was found from DNS cache), callback will be called + * before function returns. + * + * @param host Hostname to resolve + * @param callback Callback that is called for result + * @param version IP version of address to resolve, NSAPI_UNSPEC indicates + * version is chosen by the stack (defaults to NSAPI_UNSPEC) + * @return 0 on success, negative error code on failure or an unique id that + * represents the hostname translation operation and can be passed to + * cancel + */ + virtual nsapi_error_t gethostbyname_async(const char *host, hostbyname_cb_t callback, + nsapi_version_t version = NSAPI_UNSPEC) = 0; + + /** Cancels asynchronous hostname translation + * + * When translation is cancelled, callback will not be called. + * + * @param id Unique id of the hostname translation operation + * @return 0 on success, negative error code on failure + */ + virtual nsapi_error_t gethostbyname_async_cancel(nsapi_error_t id) = 0; + + /** Add a domain name server to list of servers to query + * + * @param address Destination for the host address + * @return 0 on success, negative error code on failure + */ + virtual nsapi_error_t add_dns_server(const SocketAddress &address) = 0; +}; + +#endif diff --git a/features/netsocket/NetworkInterface.cpp b/features/netsocket/NetworkInterface.cpp index 3694f8e8e6..059d0e516a 100644 --- a/features/netsocket/NetworkInterface.cpp +++ b/features/netsocket/NetworkInterface.cpp @@ -60,9 +60,14 @@ nsapi_error_t NetworkInterface::gethostbyname(const char *name, SocketAddress *a return get_stack()->gethostbyname(name, address, version); } -nsapi_error_t NetworkInterface::gethostbyname_async(const char *host, hostbyname_cb_t callback, void *data, nsapi_version_t version) +nsapi_error_t NetworkInterface::gethostbyname_async(const char *host, hostbyname_cb_t callback, nsapi_version_t version) { - return get_stack()->gethostbyname_async(host, callback, data, version); + return get_stack()->gethostbyname_async(host, callback, version); +} + +nsapi_error_t NetworkInterface::gethostbyname_async_cancel(nsapi_error_t handle) +{ + return get_stack()->gethostbyname_async_cancel(handle); } nsapi_error_t NetworkInterface::add_dns_server(const SocketAddress &address) diff --git a/features/netsocket/NetworkInterface.h b/features/netsocket/NetworkInterface.h index 54552cda85..b021dc01ea 100644 --- a/features/netsocket/NetworkInterface.h +++ b/features/netsocket/NetworkInterface.h @@ -20,6 +20,8 @@ #include "netsocket/nsapi_types.h" #include "netsocket/SocketAddress.h" #include "Callback.h" +#include "DNS.h" + // Predeclared classes class NetworkStack; @@ -34,7 +36,7 @@ class EMACInterface; * Common interface that is shared between network devices * @addtogroup netsocket */ -class NetworkInterface { +class NetworkInterface: public DNS { public: @@ -118,8 +120,8 @@ public: * If no stack-specific DNS resolution is provided, the hostname * will be resolve using a UDP socket on the stack. * - * @param address Destination for the host SocketAddress * @param host Hostname to resolve + * @param address Destination for the host SocketAddress * @param version IP version of address to resolve, NSAPI_UNSPEC indicates * version is chosen by the stack (defaults to NSAPI_UNSPEC) * @return 0 on success, negative error code on failure @@ -129,14 +131,18 @@ public: /** Hostname translation callback (asynchronous) * - * Callback will be called after DNS resolution completes or a failure - * occurs. + * Callback will be called after DNS resolution completes or a failure occurs. + * + * Callback should not take more than 10ms to execute, otherwise it might + * prevent underlying thread processing. A portable user of the callback + * should not make calls to network operations due to stack size limitations. + * The callback should not perform expensive operations such as socket recv/send + * calls or blocking operations. * * @param status 0 on success, negative error code on failure * @param address On success, destination for the host SocketAddress - * @param data Caller defined data */ - typedef mbed::Callback hostbyname_cb_t; + typedef mbed::Callback hostbyname_cb_t; /** Translates a hostname to an IP address (asynchronous) * @@ -147,17 +153,29 @@ public: * will be resolve using a UDP socket on the stack. * * Call is non-blocking. Result of the DNS operation is returned by the callback. - * If this function returns failure, callback will not be called. + * If this function returns failure, callback will not be called. In case result + * is success (IP address was found from DNS cache), callback will be called + * before function returns. * * @param host Hostname to resolve * @param callback Callback that is called for result - * @param data Caller defined data returned in callback * @param version IP version of address to resolve, NSAPI_UNSPEC indicates * version is chosen by the stack (defaults to NSAPI_UNSPEC) + * @return 0 on success, negative error code on failure or an unique id that + * represents the hostname translation operation and can be passed to + * cancel + */ + virtual nsapi_error_t gethostbyname_async(const char *host, hostbyname_cb_t callback, + nsapi_version_t version = NSAPI_UNSPEC); + + /** Cancels asynchronous hostname translation + * + * When translation is cancelled, callback will not be called. + * + * @param id Unique id of the hostname translation operation * @return 0 on success, negative error code on failure */ - virtual nsapi_error_t gethostbyname_async(const char *host, hostbyname_cb_t callback, void *data, - nsapi_version_t version = NSAPI_UNSPEC); + virtual nsapi_error_t gethostbyname_async_cancel(nsapi_error_t id); /** Add a domain name server to list of servers to query * diff --git a/features/netsocket/NetworkStack.cpp b/features/netsocket/NetworkStack.cpp index 7a2233793d..89bbe4c7b6 100644 --- a/features/netsocket/NetworkStack.cpp +++ b/features/netsocket/NetworkStack.cpp @@ -49,7 +49,7 @@ nsapi_error_t NetworkStack::gethostbyname(const char *name, SocketAddress *addre return nsapi_dns_query(this, name, address, version); } -nsapi_error_t NetworkStack::gethostbyname_async(const char *name, hostbyname_cb_t callback, void *data, nsapi_version_t version) +nsapi_error_t NetworkStack::gethostbyname_async(const char *name, hostbyname_cb_t callback, nsapi_version_t version) { SocketAddress address; @@ -59,6 +59,7 @@ nsapi_error_t NetworkStack::gethostbyname_async(const char *name, hostbyname_cb_ return NSAPI_ERROR_DNS_FAILURE; } + callback(NSAPI_ERROR_OK, &address); return NSAPI_ERROR_OK; } @@ -71,7 +72,14 @@ nsapi_error_t NetworkStack::gethostbyname_async(const char *name, hostbyname_cb_ } } - return nsapi_dns_query_async(this, name, callback, data, version); + call_in_callback_cb_t call_in_cb = get_call_in_callback(); + + return nsapi_dns_query_async(this, name, callback, call_in_cb, version); +} + +nsapi_error_t NetworkStack::gethostbyname_async_cancel(nsapi_error_t handle) +{ + return nsapi_dns_query_async_cancel(handle); } nsapi_error_t NetworkStack::add_dns_server(const SocketAddress &address) @@ -104,6 +112,39 @@ nsapi_error_t NetworkStack::getsockopt(void *handle, int level, int optname, voi return NSAPI_ERROR_UNSUPPORTED; } +nsapi_error_t NetworkStack::call_in(int delay, mbed::Callback func) +{ + events::EventQueue *event_queue = mbed::mbed_event_queue(); + if (!event_queue) { + return NSAPI_ERROR_NO_MEMORY; + } + + if (delay > 0) { + if (event_queue->call_in(delay, func) == 0) { + return NSAPI_ERROR_NO_MEMORY; + } + } else { + if (event_queue->call(func) == 0) { + return NSAPI_ERROR_NO_MEMORY; + } + } + + return NSAPI_ERROR_OK; +} + +typedef mbed::Callback user_cb)> call_in_callback_cb_t; + +call_in_callback_cb_t NetworkStack::get_call_in_callback() +{ + events::EventQueue *event_queue = mbed::mbed_event_queue(); + if (!event_queue) { + return NULL; + } + + call_in_callback_cb_t cb(this, &NetworkStack::call_in); + return cb; +} + // NetworkStackWrapper class for encapsulating the raw nsapi_stack structure class NetworkStackWrapper : public NetworkStack diff --git a/features/netsocket/NetworkStack.h b/features/netsocket/NetworkStack.h index 2d4718ec5a..e29dce6ee1 100644 --- a/features/netsocket/NetworkStack.h +++ b/features/netsocket/NetworkStack.h @@ -21,6 +21,7 @@ #include "nsapi_types.h" #include "netsocket/SocketAddress.h" #include "netsocket/NetworkInterface.h" +#include "DNS.h" // Predeclared classes class OnboardNetworkStack; @@ -33,7 +34,7 @@ class OnboardNetworkStack; * for instantiating network sockets. * @addtogroup netsocket */ -class NetworkStack +class NetworkStack: public DNS { public: virtual ~NetworkStack() {}; @@ -64,14 +65,18 @@ public: /** Hostname translation callback (asynchronous) * - * Callback will be called after DNS resolution completes or a failure - * occurs. + * Callback will be called after DNS resolution completes or a failure occurs. + * + * Callback should not take more than 10ms to execute, otherwise it might + * prevent underlying thread processing. A portable user of the callback + * should not make calls to network operations due to stack size limitations. + * The callback should not perform expensive operations such as socket recv/send + * calls or blocking operations. * * @param status 0 on success, negative error code on failure * @param address On success, destination for the host SocketAddress - * @param data Caller defined data */ - typedef mbed::Callback hostbyname_cb_t; + typedef mbed::Callback hostbyname_cb_t; /** Translates a hostname to an IP address (asynchronous) * @@ -82,17 +87,29 @@ public: * will be resolve using a UDP socket on the stack. * * Call is non-blocking. Result of the DNS operation is returned by the callback. - * If this function returns failure, callback will not be called. + * If this function returns failure, callback will not be called. In case result + * is success (IP address was found from DNS cache), callback will be called + * before function returns. * * @param host Hostname to resolve * @param callback Callback that is called for result - * @param data Caller defined data returned in callback * @param version IP version of address to resolve, NSAPI_UNSPEC indicates * version is chosen by the stack (defaults to NSAPI_UNSPEC) + * @return 0 on success, negative error code on failure or an unique id that + * represents the hostname translation operation and can be passed to + * cancel + */ + virtual nsapi_error_t gethostbyname_async(const char *host, hostbyname_cb_t callback, + nsapi_version_t version = NSAPI_UNSPEC); + + /** Cancels asynchronous hostname translation + * + * When translation is cancelled, callback will not be called. + * + * @param id Unique id of the hostname translation operation * @return 0 on success, negative error code on failure */ - virtual nsapi_error_t gethostbyname_async(const char *host, hostbyname_cb_t callback, void *data, - nsapi_version_t version = NSAPI_UNSPEC); + virtual nsapi_error_t gethostbyname_async_cancel(nsapi_error_t id); /** Add a domain name server to list of servers to query * @@ -347,8 +364,40 @@ protected: */ virtual nsapi_error_t getsockopt(nsapi_socket_t handle, int level, int optname, void *optval, unsigned *optlen); -}; +private: + + /** Call in callback + * + * Callback is used to call the call in method of the network stack. + */ + typedef mbed::Callback user_cb)> call_in_callback_cb_t; + + /** Get a call in callback + * + * Get a call in callback from the network stack context. + * + * Callback should not take more than 10ms to execute, otherwise it might + * prevent underlying thread processing. A portable user of the callback + * should not make calls to network operations due to stack size limitations. + * The callback should not perform expensive operations such as socket recv/send + * calls or blocking operations. + * + * @return Call in callback + */ + virtual call_in_callback_cb_t get_call_in_callback(); + + /** Call a callback after a delay + * + * Call a callback from the network stack context after a delay. If function + * returns error callback will not be called. + * + * @param delay Delay in milliseconds + * @param func Callback to be called + * @return 0 on success, negative error code on failure + */ + virtual nsapi_error_t call_in(int delay, mbed::Callback func); +}; /** Convert a raw nsapi_stack_t object into a C++ NetworkStack object * diff --git a/features/netsocket/OnboardNetworkStack.h b/features/netsocket/OnboardNetworkStack.h index bead0def60..bccd664dbc 100644 --- a/features/netsocket/OnboardNetworkStack.h +++ b/features/netsocket/OnboardNetworkStack.h @@ -39,8 +39,6 @@ public: */ static OnboardNetworkStack &get_default_instance(); - virtual OnboardNetworkStack *onboardNetworkStack() { return this; } - /** Representation of a stack's view of an interface. * * Provides facilities required by a driver to implement the application @@ -123,27 +121,6 @@ public: virtual char *get_gateway(char *buf, nsapi_size_t buflen) = 0; }; - /** Call a callback - * - * Call a callback from the network stack context. If returns error - * callback will not be called. - * - * @param func Callback to be called - * @return 0 on success, negative error code on failure - */ - virtual nsapi_error_t call(mbed::Callback 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 * * Connects EMAC layer with the IP stack and initializes all the required infrastructure. diff --git a/features/netsocket/mbed_lib.json b/features/netsocket/mbed_lib.json index fb2c50ecae..9668cb2edf 100644 --- a/features/netsocket/mbed_lib.json +++ b/features/netsocket/mbed_lib.json @@ -2,6 +2,22 @@ "name": "nsapi", "config": { "present": 1, - "default-stack": "LWIP" + "default-stack": "LWIP", + "dns-response-wait-time": { + "help": "How long the DNS translator waits for a reply from a server", + "value": 5000 + }, + "dns-total-retries": { + "help": "Number of total DNS query retries that the DNS translator makes", + "value": 3 + }, + "dns-retries": { + "help": "Number of DNS query retries that the DNS translator makes per server", + "value": 0 + }, + "dns-cache-size": { + "help": "Number of cached host name resolutions", + "value": 3 + } } } diff --git a/features/netsocket/nsapi_dns.cpp b/features/netsocket/nsapi_dns.cpp index d4a9f8d11f..77bd7f2097 100644 --- a/features/netsocket/nsapi_dns.cpp +++ b/features/netsocket/nsapi_dns.cpp @@ -31,17 +31,32 @@ // DNS options #define DNS_BUFFER_SIZE 512 -#define DNS_TIMEOUT 5000 #define DNS_SERVERS_SIZE 5 #define DNS_RESPONSE_MIN_SIZE 12 #define DNS_MAX_TTL 604800 -#define DNS_CACHE_SIZE 3 #define DNS_STACK_SERVERS_NUM 5 #define DNS_QUERY_QUEUE_SIZE 5 +#define DNS_HOST_NAME_MAX_LEN 255 + +#ifndef MBED_CONF_NSAPI_DNS_RESPONSE_WAIT_TIME +#define MBED_CONF_NSAPI_DNS_RESPONSE_WAIT_TIME 5000 +#endif + +#ifndef MBED_CONF_NSAPI_DNS_TOTAL_RETRIES +#define MBED_CONF_NSAPI_DNS_TOTAL_RETRIES 3 +#endif + +#ifndef MBED_CONF_NSAPI_DNS_RETRIES +#define MBED_CONF_NSAPI_DNS_RETRIES 0 +#endif + +#ifndef MBED_CONF_NSAPI_DNS_CACHE_SIZE +#define MBED_CONF_NSAPI_DNS_CACHE_SIZE 3 +#endif struct DNS_CACHE { - SocketAddress address; - char host[128]; + nsapi_addr_t address; + char *host; uint64_t expires; /*!< time to live in milliseconds */ uint64_t accessed; /*!< last accessed */ }; @@ -49,33 +64,35 @@ struct DNS_CACHE { struct DNS_QUERY { int unique_id; NetworkStack *stack; - char host[128]; + char *host; NetworkStack::hostbyname_cb_t callback; - void *cb_data; + call_in_callback_cb_t call_in_cb; nsapi_size_t addr_count; nsapi_version_t version; UDPSocket *socket; + nsapi_addr_t *addrs; int dns_server; int retries; + int total_retries; int dns_message_id; + int count; + uint32_t ttl; }; typedef nsapi_error_t (*nsapi_dns_call_t)(mbed::Callback 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 void nsapi_dns_cache_add(const char *host, nsapi_addr_t *address, uint32_t ttl); +static nsapi_size_or_error_t nsapi_dns_cache_find(const char *host, nsapi_version_t version, nsapi_addr_t *address); -static nsapi_error_t nsapi_dns_get_server_addr(NetworkStack *stack, int *index, SocketAddress *dns_addr); +static nsapi_error_t nsapi_dns_get_server_addr(NetworkStack *stack, int *index, int *total_retries, SocketAddress *dns_addr); -static void nsapi_dns_query_async_create(DNS_QUERY *query); +static void nsapi_dns_query_async_create(void *ptr); +static nsapi_error_t nsapi_dns_query_async_delete(int unique_id); static void nsapi_dns_query_async_send(void *ptr); static void nsapi_dns_query_async_resp(DNS_QUERY *query, nsapi_error_t status, SocketAddress *address); static void nsapi_dns_query_async_socket_callback(NetworkStack *stack); -static void nsapi_dns_query_async_socket_callback_handle(NetworkStack *stack); - -static nsapi_error_t nsapi_dns_call_default(mbed::Callback func); -static nsapi_error_t nsapi_dns_call_in_default(int delay, mbed::Callback func); +static void nsapi_dns_query_async_response(void *ptr); static nsapi_addr_t dns_servers[DNS_SERVERS_SIZE] = { {NSAPI_IPv4, {8, 8, 8, 8}}, // Google @@ -87,13 +104,15 @@ static nsapi_addr_t dns_servers[DNS_SERVERS_SIZE] = { 0,0, 0,0, 0x1c,0x04, 0xb1,0x2f}}, }; -static DNS_CACHE *dns_cache[DNS_CACHE_SIZE]; +static DNS_CACHE *dns_cache[MBED_CONF_NSAPI_DNS_CACHE_SIZE]; static uint16_t dns_message_id = 0; -static int dns_unique_id = 0; +static int dns_unique_id = 1; static DNS_QUERY *dns_query_queue[DNS_QUERY_QUEUE_SIZE]; +// Protects cache shared between blocking and asynchronous calls static rtos::Mutex dns_cache_mutex; -static nsapi_dns_call_t dns_call = nsapi_dns_call_default; -static nsapi_dns_call_in_t dns_call_in = nsapi_dns_call_in_default; +// Protects from several threads running asynchronous DNS +static rtos::Mutex dns_mutex; +static nsapi_dns_call_in_t dns_call_in = 0; // DNS server configuration extern "C" nsapi_error_t nsapi_dns_add_server(nsapi_addr_t addr) @@ -199,7 +218,7 @@ static int dns_scan_response(const uint8_t *ptr, uint16_t exp_id, uint32_t *ttl, // verify header is response to query if (!(id == exp_id && qr && opcode == 0 && rcode == 0)) { - return 0; + return -1; } // skip questions @@ -273,7 +292,7 @@ static int dns_scan_response(const uint8_t *ptr, uint16_t exp_id, uint32_t *ttl, return count; } -static void nsapi_dns_cache_add(const char *host, SocketAddress *address, uint32_t ttl) +static void nsapi_dns_cache_add(const char *host, nsapi_addr_t *address, uint32_t ttl) { // RFC 1034: if TTL is zero, entry is not added to cache if (!ttl) { @@ -281,17 +300,17 @@ static void nsapi_dns_cache_add(const char *host, SocketAddress *address, uint32 } // Checks if already cached - if (nsapi_dns_cache_find(host, address->get_ip_version(), NULL) == NSAPI_ERROR_OK) { + if (nsapi_dns_cache_find(host, address->version, NULL) == NSAPI_ERROR_OK) { return; } dns_cache_mutex.lock(); int index = -1; - uint64_t accessed = -1; + uint64_t accessed = ~0; // Finds free or last accessed entry - for (int i = 0; i < DNS_CACHE_SIZE; i++) { + for (int i = 0; i < MBED_CONF_NSAPI_DNS_CACHE_SIZE; i++) { if (!dns_cache[i]) { index = i; break; @@ -302,17 +321,21 @@ static void nsapi_dns_cache_add(const char *host, SocketAddress *address, uint32 } if (index < 0) { + dns_cache_mutex.unlock(); return; } // Allocates in case entry is free, otherwise reuses if (!dns_cache[index]) { - dns_cache[index] = new DNS_CACHE; + dns_cache[index] = new (std::nothrow) DNS_CACHE; + } else { + free(dns_cache[index]->host); } if (dns_cache[index]) { dns_cache[index]->address = *address; - strncpy(dns_cache[index]->host, host, 127); + dns_cache[index]->host = (char *) malloc(strlen(host) + 1); + strcpy(dns_cache[index]->host, host); uint64_t ms_count = rtos::Kernel::get_ms_count(); dns_cache[index]->expires = ms_count + ttl * 1000; dns_cache[index]->accessed = ms_count; @@ -321,21 +344,22 @@ static void nsapi_dns_cache_add(const char *host, SocketAddress *address, uint32 dns_cache_mutex.unlock(); } -static nsapi_error_t nsapi_dns_cache_find(const char *host, nsapi_version_t version, SocketAddress *address) +static nsapi_error_t nsapi_dns_cache_find(const char *host, nsapi_version_t version, nsapi_addr_t *address) { nsapi_error_t ret_val = NSAPI_ERROR_NO_ADDRESS; dns_cache_mutex.lock(); - for (int i = 0; i < DNS_CACHE_SIZE; i++) { + for (int i = 0; i < MBED_CONF_NSAPI_DNS_CACHE_SIZE; i++) { if (dns_cache[i]) { uint64_t ms_count = rtos::Kernel::get_ms_count(); // Checks all entries for expired entries if (ms_count > dns_cache[i]->expires) { + free(dns_cache[i]->host); delete dns_cache[i]; dns_cache[i] = NULL; - } else if (((version == NSAPI_UNSPEC) || (version == dns_cache[i]->address.get_ip_version())) && - (strncmp(dns_cache[i]->host, host, 127) == 0)) { + } else if ((version == NSAPI_UNSPEC || version == dns_cache[i]->address.version) && + strcmp(dns_cache[i]->host, host) == 0) { if (address) { *address = dns_cache[i]->address; } @@ -350,14 +374,22 @@ static nsapi_error_t nsapi_dns_cache_find(const char *host, nsapi_version_t vers return ret_val; } -static nsapi_error_t nsapi_dns_get_server_addr(NetworkStack *stack, int *index, SocketAddress *dns_addr) +static nsapi_error_t nsapi_dns_get_server_addr(NetworkStack *stack, int *index, int *total_retries, SocketAddress *dns_addr) { bool dns_addr_set = false; - if (*index >= DNS_SERVERS_SIZE + DNS_STACK_SERVERS_NUM) { + if (*total_retries == 0) { return NSAPI_ERROR_NO_ADDRESS; } + if (*index >= DNS_SERVERS_SIZE + DNS_STACK_SERVERS_NUM) { + if (*total_retries) { + *index = 0; + } else { + return NSAPI_ERROR_NO_ADDRESS; + } + } + if (*index < DNS_STACK_SERVERS_NUM) { nsapi_error_t ret = stack->get_dns_server(*index, dns_addr); if (ret < 0) { @@ -382,14 +414,12 @@ static nsapi_size_or_error_t nsapi_dns_query_multiple(NetworkStack *stack, const { // check for valid host name int host_len = host ? strlen(host) : 0; - if (host_len > 128 || host_len == 0) { + if (host_len > DNS_HOST_NAME_MAX_LEN || host_len == 0) { return NSAPI_ERROR_PARAMETER; } // check cache - SocketAddress address; - if (nsapi_dns_cache_find(host, version, &address) == NSAPI_ERROR_OK) { - *addr = address.get_addr(); + if (nsapi_dns_cache_find(host, version, addr) == NSAPI_ERROR_OK) { return 1; } @@ -400,7 +430,7 @@ static nsapi_size_or_error_t nsapi_dns_query_multiple(NetworkStack *stack, const return err; } - socket.set_timeout(DNS_TIMEOUT); + socket.set_timeout(MBED_CONF_NSAPI_DNS_RESPONSE_WAIT_TIME); // create network packet uint8_t * const packet = (uint8_t *)malloc(DNS_BUFFER_SIZE); @@ -410,14 +440,15 @@ static nsapi_size_or_error_t nsapi_dns_query_multiple(NetworkStack *stack, const nsapi_size_or_error_t result = NSAPI_ERROR_DNS_FAILURE; - bool retry = false; + int retries = MBED_CONF_NSAPI_DNS_RETRIES; int index = 0; + int total_retries = MBED_CONF_NSAPI_DNS_TOTAL_RETRIES; // check against each dns server while (true) { SocketAddress dns_addr; - err = nsapi_dns_get_server_addr(stack, &index, &dns_addr); + err = nsapi_dns_get_server_addr(stack, &index, &total_retries, &dns_addr); if (err != NSAPI_ERROR_OK) { break; } @@ -429,20 +460,24 @@ static nsapi_size_or_error_t nsapi_dns_query_multiple(NetworkStack *stack, const // send may fail for various reasons, including wrong address type - move on if (err < 0) { // goes to next dns server - retry = false; + retries = MBED_CONF_NSAPI_DNS_RETRIES; index++; continue; } + if (total_retries) { + total_retries--; + } + // recv the response err = socket.recvfrom(NULL, packet, DNS_BUFFER_SIZE); if (err == NSAPI_ERROR_WOULD_BLOCK) { - if (!retry) { - // retries once - retry = true; + if (retries) { + // retries + retries--; } else { // goes to next dns server - retry = false; + retries = MBED_CONF_NSAPI_DNS_RETRIES; index++; } continue; @@ -453,13 +488,12 @@ static nsapi_size_or_error_t nsapi_dns_query_multiple(NetworkStack *stack, const const uint8_t *response = packet; uint32_t ttl; - int count = dns_scan_response(response, 1, &ttl, addr, addr_count); - if (count > 0) { - // Adds address to cache - SocketAddress address(*addr); - nsapi_dns_cache_add(host, &address, ttl); - - result = count; + int resp = dns_scan_response(response, 1, &ttl, addr, addr_count); + if (resp > 0) { + nsapi_dns_cache_add(host, addr, ttl); + result = resp; + } else if (resp < 0) { + continue; } /* The DNS response is final, no need to check other servers */ @@ -490,7 +524,7 @@ extern "C" nsapi_size_or_error_t nsapi_dns_query_multiple(nsapi_stack_t *stack, nsapi_size_or_error_t nsapi_dns_query_multiple(NetworkStack *stack, const char *host, SocketAddress *addresses, nsapi_size_t addr_count, nsapi_version_t version) { - nsapi_addr_t *addrs = new nsapi_addr_t[addr_count]; + nsapi_addr_t *addrs = new (std::nothrow) nsapi_addr_t[addr_count]; nsapi_size_or_error_t result = nsapi_dns_query_multiple(stack, host, addrs, addr_count, version); if (result > 0) { @@ -521,39 +555,10 @@ nsapi_error_t nsapi_dns_query(NetworkStack *stack, const char *host, } nsapi_error_t nsapi_dns_query_async(NetworkStack *stack, const char *host, - NetworkStack::hostbyname_cb_t callback, void *data, nsapi_version_t version) + NetworkStack::hostbyname_cb_t callback, call_in_callback_cb_t call_in_cb, + nsapi_version_t version) { - nsapi_size_or_error_t result = nsapi_dns_query_multiple_async(stack, host, callback, data, 1, version); - return (nsapi_error_t)((result > 0) ? 0 : result); -} - -static nsapi_error_t nsapi_dns_call_default(mbed::Callback 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; + return nsapi_dns_query_multiple_async(stack, host, callback, 0, call_in_cb, version); } void nsapi_dns_call_in_set(nsapi_dns_call_in_t callback) @@ -561,32 +566,19 @@ void nsapi_dns_call_in_set(nsapi_dns_call_in_t callback) dns_call_in = callback; } -static nsapi_error_t nsapi_dns_call(NetworkStack *stack, mbed::Callback func) +nsapi_error_t nsapi_dns_call_in(call_in_callback_cb_t cb, int delay, 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 { + if (dns_call_in) { dns_call_in(delay, func); + } else { + return cb(delay, func); } - return NSAPI_ERROR_OK; } nsapi_error_t nsapi_dns_query_multiple_async(NetworkStack *stack, const char *host, - NetworkStack::hostbyname_cb_t callback, void *data, nsapi_size_t addr_count, nsapi_version_t version) + NetworkStack::hostbyname_cb_t callback, nsapi_size_t addr_count, + call_in_callback_cb_t call_in_cb, nsapi_version_t version) { if (!stack) { return NSAPI_ERROR_PARAMETER; @@ -594,129 +586,87 @@ nsapi_error_t nsapi_dns_query_multiple_async(NetworkStack *stack, const char *ho // check for valid host name int host_len = host ? strlen(host) : 0; - if (host_len > 128 || host_len == 0) { + if (host_len > DNS_HOST_NAME_MAX_LEN || host_len == 0) { return NSAPI_ERROR_PARAMETER; } - DNS_QUERY *query = new DNS_QUERY; + nsapi_addr address; + if (nsapi_dns_cache_find(host, version, &address) == NSAPI_ERROR_OK) { + SocketAddress addr(address); + callback(NSAPI_ERROR_OK, &addr); + return NSAPI_ERROR_OK; + } - if (!query) { + dns_mutex.lock(); + + int index = -1; + + for (int i = 0; i < DNS_QUERY_QUEUE_SIZE; i++) { + if (!dns_query_queue[i]) { + index = i; + break; + } + } + + if (index < 0) { + dns_mutex.unlock(); return NSAPI_ERROR_NO_MEMORY; } - query->unique_id = 0; + DNS_QUERY *query = new (std::nothrow) DNS_QUERY; + + if (!query) { + dns_mutex.unlock(); + return NSAPI_ERROR_NO_MEMORY; + } + + query->host = (char *) malloc(host_len + 1); strcpy(query->host, host); query->callback = callback; - query->cb_data = data; + query->call_in_cb = call_in_cb; query->stack = stack; query->addr_count = addr_count; query->version = version; query->socket = NULL; + query->addrs = NULL; query->dns_server = 0; - query->retries = 2; + query->retries = MBED_CONF_NSAPI_DNS_RETRIES + 1; + query->total_retries = MBED_CONF_NSAPI_DNS_TOTAL_RETRIES; query->dns_message_id = -1; - if (nsapi_dns_call(stack, mbed::callback(nsapi_dns_query_async_create, query)) != NSAPI_ERROR_OK) { - delete query; - return NSAPI_ERROR_NO_MEMORY; - } - - return NSAPI_ERROR_IN_PROGRESS ; -} - -static void nsapi_dns_query_async_create(DNS_QUERY *query) -{ - SocketAddress address; - if (nsapi_dns_cache_find(query->host, query->version, &address) == NSAPI_ERROR_OK) { - nsapi_dns_query_async_resp(query, NSAPI_ERROR_OK, &address); - return; - } - - int index = -1; - - for (int i = 0; i < DNS_QUERY_QUEUE_SIZE; i++) { - if (dns_query_queue[i]) { - if (dns_query_queue[i]->stack == query->stack) { - query->socket = dns_query_queue[i]->socket; - } - } else if (index < 0) { - index = i; - } - } - - if (index < 0) { - nsapi_dns_query_async_resp(query, NSAPI_ERROR_NO_MEMORY, NULL); - return; - } - - UDPSocket *socket; - - if (query->socket) { - socket = query->socket; - } else { - socket = new UDPSocket; - if (!socket) { - nsapi_dns_query_async_resp(query, NSAPI_ERROR_NO_MEMORY, NULL); - return; - } - - int err = socket->open(query->stack); - if (err) { - delete socket; - nsapi_dns_query_async_resp(query, err, NULL); - return; - } - - socket->set_timeout(0); - socket->sigio(mbed::callback(nsapi_dns_query_async_socket_callback, query->stack)); - - query->socket = socket; - } - query->unique_id = dns_unique_id++; + if (query->unique_id > 0x7FFF) { + query->unique_id = 1; + } dns_query_queue[index] = query; - nsapi_dns_query_async_send(reinterpret_cast(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 (nsapi_dns_call_in(query->call_in_cb, 0, mbed::callback(nsapi_dns_query_async_create, reinterpret_cast(query->unique_id))) != NSAPI_ERROR_OK) { + delete query; + dns_mutex.unlock(); + return NSAPI_ERROR_NO_MEMORY; } - if (index < 0) { - return; - } + dns_mutex.unlock(); - if (close_socket) { - query->socket->close(); - delete query->socket; - } - - dns_query_queue[index] = NULL; - delete query; + return query->unique_id; } -static void nsapi_dns_query_async_resp(DNS_QUERY *query, nsapi_error_t status, SocketAddress *address) +nsapi_error_t nsapi_dns_query_async_cancel(nsapi_error_t id) { - query->callback(status, address, query->cb_data); - nsapi_dns_query_async_delete(query); + dns_mutex.lock(); + + nsapi_error_t ret = nsapi_dns_query_async_delete(id); + + dns_mutex.unlock(); + + return ret; } -static void nsapi_dns_query_async_send(void *ptr) +static void nsapi_dns_query_async_create(void *ptr) { + dns_mutex.lock(); + int unique_id = reinterpret_cast(ptr); DNS_QUERY *query = NULL; @@ -729,7 +679,114 @@ static void nsapi_dns_query_async_send(void *ptr) } if (!query) { - nsapi_dns_query_async_resp(query, NSAPI_ERROR_NO_MEMORY, NULL); + // Cancel has been called + dns_mutex.unlock(); + return; + } + + for (int i = 0; i < DNS_QUERY_QUEUE_SIZE; i++) { + if (dns_query_queue[i] && dns_query_queue[i] != query) { + if (dns_query_queue[i]->socket && dns_query_queue[i]->stack == query->stack) { + query->socket = dns_query_queue[i]->socket; + } + } + } + + UDPSocket *socket; + + if (query->socket) { + socket = query->socket; + } else { + socket = new (std::nothrow) UDPSocket; + if (!socket) { + nsapi_dns_query_async_resp(query, NSAPI_ERROR_NO_MEMORY, NULL); + dns_mutex.unlock(); + return; + } + + int err = socket->open(query->stack); + if (err) { + delete socket; + nsapi_dns_query_async_resp(query, err, NULL); + dns_mutex.unlock(); + return; + } + + socket->set_timeout(0); + socket->sigio(mbed::callback(nsapi_dns_query_async_socket_callback, query->stack)); + + query->socket = socket; + } + + dns_mutex.unlock(); + + nsapi_dns_query_async_send(reinterpret_cast(query->unique_id)); +} + +static nsapi_error_t nsapi_dns_query_async_delete(int unique_id) +{ + int index = -1; + + for (int i = 0; i < DNS_QUERY_QUEUE_SIZE; i++) { + if (dns_query_queue[i] && dns_query_queue[i]->unique_id == unique_id) { + index = i; + break; + } + } + + if (index < 0) { + return NSAPI_ERROR_DEVICE_ERROR; + } + + bool close_socket = true; + + for (int i = 0; i < DNS_QUERY_QUEUE_SIZE; i++) { + if (i != index && dns_query_queue[i] && dns_query_queue[i]->socket && + dns_query_queue[i]->stack == dns_query_queue[index]->stack) { + close_socket = false; + } + } + + if (close_socket && dns_query_queue[index]->socket) { + dns_query_queue[index]->socket->close(); + delete dns_query_queue[index]->socket; + } + + if (dns_query_queue[index]->addrs) { + delete[] dns_query_queue[index]->addrs; + } + + free(dns_query_queue[index]->host); + + delete dns_query_queue[index]; + dns_query_queue[index] = NULL; + return NSAPI_ERROR_OK; +} + +static void nsapi_dns_query_async_resp(DNS_QUERY *query, nsapi_error_t status, SocketAddress *address) +{ + query->callback(status, address); + nsapi_dns_query_async_delete(query->unique_id); +} + +static void nsapi_dns_query_async_send(void *ptr) +{ + dns_mutex.lock(); + + int unique_id = reinterpret_cast(ptr); + + DNS_QUERY *query = NULL; + + for (int i = 0; i < DNS_QUERY_QUEUE_SIZE; i++) { + if (dns_query_queue[i] && dns_query_queue[i]->unique_id == unique_id) { + query = dns_query_queue[i]; + break; + } + } + + if (!query) { + // Cancel has been called + dns_mutex.unlock(); return; } @@ -737,7 +794,7 @@ static void nsapi_dns_query_async_send(void *ptr) query->retries--; } else { query->dns_server++; - query->retries = 1; + query->retries = MBED_CONF_NSAPI_DNS_RETRIES; } query->dns_message_id = dns_message_id++; @@ -746,6 +803,7 @@ static void nsapi_dns_query_async_send(void *ptr) uint8_t *packet = (uint8_t *)malloc(DNS_BUFFER_SIZE); if (!packet) { nsapi_dns_query_async_resp(query, NSAPI_ERROR_NO_MEMORY, NULL); + dns_mutex.unlock(); return; } @@ -754,10 +812,11 @@ static void nsapi_dns_query_async_send(void *ptr) while (true) { SocketAddress dns_addr; - nsapi_size_or_error_t err = nsapi_dns_get_server_addr(query->stack, &(query->dns_server), &dns_addr); + nsapi_size_or_error_t err = nsapi_dns_get_server_addr(query->stack, &(query->dns_server), &(query->total_retries), &dns_addr); if (err != NSAPI_ERROR_OK) { nsapi_dns_query_async_resp(query, NSAPI_ERROR_DNS_FAILURE, NULL); free(packet); + dns_mutex.unlock(); return; } @@ -770,23 +829,26 @@ static void nsapi_dns_query_async_send(void *ptr) } } + if (query->total_retries) { + query->total_retries--; + } + free(packet); - if (nsapi_dns_call_in(query->stack, DNS_TIMEOUT, + if (nsapi_dns_call_in(query->call_in_cb, MBED_CONF_NSAPI_DNS_RESPONSE_WAIT_TIME, mbed::callback(nsapi_dns_query_async_send, reinterpret_cast(unique_id))) != NSAPI_ERROR_OK) { nsapi_dns_query_async_resp(query, NSAPI_ERROR_NO_MEMORY, NULL); } + + dns_mutex.unlock(); } static void nsapi_dns_query_async_socket_callback(NetworkStack *stack) -{ - nsapi_dns_call(stack, mbed::callback(nsapi_dns_query_async_socket_callback_handle, stack)); -} - -static void nsapi_dns_query_async_socket_callback_handle(NetworkStack *stack) { UDPSocket *socket = NULL; + dns_mutex.lock(); + for (int i = 0; i < DNS_QUERY_QUEUE_SIZE; i++) { if (dns_query_queue[i] && dns_query_queue[i]->stack == stack) { socket = dns_query_queue[i]->socket; @@ -798,58 +860,95 @@ static void nsapi_dns_query_async_socket_callback_handle(NetworkStack *stack) // create network packet uint8_t *packet = (uint8_t *)malloc(DNS_BUFFER_SIZE); if (!packet) { + dns_mutex.unlock(); return; } - // recv the response - nsapi_size_or_error_t size = socket->recvfrom(NULL, packet, DNS_BUFFER_SIZE); + while (true) { + // recv the response + nsapi_size_or_error_t size = socket->recvfrom(NULL, packet, DNS_BUFFER_SIZE); - if (size < DNS_RESPONSE_MIN_SIZE) { - free(packet); - return; - } - - // 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]; + if (size < DNS_RESPONSE_MIN_SIZE) { break; } + + // gets id from response to associate with correct query + uint16_t id = (packet[0] << 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) { + continue; + } + + int requested_count = 1; + if (query->addr_count > 1) { + requested_count = query->addr_count; + } + + query->addrs = new (std::nothrow) nsapi_addr_t[requested_count]; + + int resp = dns_scan_response(packet, id, &(query->ttl), query->addrs, requested_count); + + // Ignore invalid responses + if (resp < 0) { + delete[] query->addrs; + query->addrs = 0; + } else { + query->count = resp; + nsapi_dns_call_in(query->call_in_cb, 0, mbed::callback(nsapi_dns_query_async_response, reinterpret_cast(query->unique_id))); + } } - 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]; + dns_mutex.unlock(); +} - for (int i = 0; i < count; i++) { - addresses[i].set_addr(addrs[i]); +static void nsapi_dns_query_async_response(void *ptr) +{ + dns_mutex.lock(); + + int unique_id = reinterpret_cast(ptr); + + DNS_QUERY *query = NULL; + + for (int i = 0; i < DNS_QUERY_QUEUE_SIZE; i++) { + if (dns_query_queue[i] && dns_query_queue[i]->unique_id == unique_id) { + query = dns_query_queue[i]; + break; + } + } + + if (query) { + if (query->count > 0) { + SocketAddress *addresses = new (std::nothrow) SocketAddress[query->count]; + + for (int i = 0; i < query->count; i++) { + addresses[i].set_addr(query->addrs[i]); } // Adds address to cache - nsapi_dns_cache_add(query->host, addresses, ttl); - - nsapi_dns_query_async_resp(query, NSAPI_ERROR_OK, addresses); + nsapi_dns_cache_add(query->host, &(query->addrs[0]), query->ttl); + nsapi_error_t status = NSAPI_ERROR_OK; + if (query->addr_count > 0) { + status = query->count; + } + nsapi_dns_query_async_resp(query, status, addresses); delete[] addresses; } else { nsapi_dns_query_async_resp(query, NSAPI_ERROR_DNS_FAILURE, NULL); } - - delete[] addrs; } + dns_mutex.unlock(); } + diff --git a/features/netsocket/nsapi_dns.h b/features/netsocket/nsapi_dns.h index 3503d6eafa..8e790d7195 100644 --- a/features/netsocket/nsapi_dns.h +++ b/features/netsocket/nsapi_dns.h @@ -63,6 +63,7 @@ nsapi_error_t nsapi_dns_add_server(nsapi_addr_t addr); #else +typedef mbed::Callback user_cb)> call_in_callback_cb_t; /** Query a domain name server for an IP address of a given hostname * @@ -80,14 +81,15 @@ nsapi_error_t nsapi_dns_query(NetworkStack *stack, const char *host, * * @param stack Network stack as target for DNS query * @param host Hostname to resolve - * @param callback Callback that is called for result* - * @param data Caller defined data returned in callback + * @param callback Callback that is called for result * @param version IP version to resolve (defaults to NSAPI_IPv4) - * @return 0 on success, negative error code on failure - * NSAPI_ERROR_DNS_FAILURE indicates the host could not be found + * @return 0 on success, negative error code on failure or an unique id that + * represents the hostname translation operation and can be passed to + * cancel, NSAPI_ERROR_DNS_FAILURE indicates the host could not be found */ nsapi_error_t nsapi_dns_query_async(NetworkStack *stack, const char *host, - NetworkStack::hostbyname_cb_t callback, void *data, nsapi_version_t version = NSAPI_IPv4); + NetworkStack::hostbyname_cb_t callback, call_in_callback_cb_t call_in_cb, + nsapi_version_t version = NSAPI_IPv4); /** Query a domain name server for an IP address of a given hostname (asynchronous) * @@ -135,14 +137,15 @@ nsapi_size_or_error_t nsapi_dns_query_multiple(NetworkStack *stack, const char * * @param stack Network stack as target for DNS query * @param host Hostname to resolve * @param callback Callback that is called for result - * @param data Caller defined data returned in callback * @param addr_count Number of addresses allocated in the array * @param version IP version to resolve (defaults to NSAPI_IPv4) - * @return 0 on success, negative error code on failure - * NSAPI_ERROR_DNS_FAILURE indicates the host could not be found + * @return 0 on success, negative error code on failure or an unique id that + represents the hostname translation operation and can be passed to + * cancel, NSAPI_ERROR_DNS_FAILURE indicates the host could not be found */ nsapi_size_or_error_t nsapi_dns_query_multiple_async(NetworkStack *stack, const char *host, - NetworkStack::hostbyname_cb_t callback, void *data, nsapi_size_t addr_count, nsapi_version_t version = NSAPI_IPv4); + NetworkStack::hostbyname_cb_t callback, nsapi_size_t addr_count, + call_in_callback_cb_t call_in_cb, nsapi_version_t version = NSAPI_IPv4); /** Query a domain name server for multiple IP address of a given hostname * @@ -176,6 +179,15 @@ nsapi_size_or_error_t nsapi_dns_query_multiple(S *stack, const char *host, host, addr, addr_count, version); } +/** Cancels asynchronous hostname translation + * + * When translation is cancelled, callback will not be called. + * + * @param id Unique id of the hostname translation operation + * @return 0 on success, negative error code on failure + */ +nsapi_error_t nsapi_dns_query_async_cancel(nsapi_error_t id); + /** Add a domain name server to list of servers to query * * @param addr Destination for the host address