diff --git a/features/FEATURE_LWIP/lwip-interface/LWIPStack.cpp b/features/FEATURE_LWIP/lwip-interface/LWIPStack.cpp index b2c3913efc..751b4d4c90 100644 --- a/features/FEATURE_LWIP/lwip-interface/LWIPStack.cpp +++ b/features/FEATURE_LWIP/lwip-interface/LWIPStack.cpp @@ -209,7 +209,7 @@ void LWIP::tcpip_thread_callback(void *ptr) nsapi_error_t LWIP::call_in(int delay, mbed::Callback func) { - lwip_callback *cb = new lwip_callback; + lwip_callback *cb = new (std::nothrow) lwip_callback; if (!cb) { return NSAPI_ERROR_NO_MEMORY; } diff --git a/features/netsocket/NetworkStack.cpp b/features/netsocket/NetworkStack.cpp index a6e04882ce..038b0816eb 100644 --- a/features/netsocket/NetworkStack.cpp +++ b/features/netsocket/NetworkStack.cpp @@ -114,7 +114,7 @@ nsapi_error_t NetworkStack::getsockopt(void *handle, int level, int optname, voi nsapi_error_t NetworkStack::call_in(int delay, mbed::Callback func) { - events::EventQueue *event_queue = mbed::mbed_event_queue(); + static events::EventQueue *event_queue = mbed::mbed_event_queue(); if (!event_queue) { return NSAPI_ERROR_NO_MEMORY; diff --git a/features/netsocket/nsapi_dns.cpp b/features/netsocket/nsapi_dns.cpp index f51b76c462..3af6023e05 100644 --- a/features/netsocket/nsapi_dns.cpp +++ b/features/netsocket/nsapi_dns.cpp @@ -55,6 +55,12 @@ struct SOCKET_CB_DATA { NetworkStack *stack; }; +enum dns_state { + DNS_CREATED, /*!< created, not yet making query to network */ + DNS_INITIATED, /*!< making query to network */ + DNS_CANCELLED /*!< cancelled, callback will not be called */ +}; + struct DNS_QUERY { int unique_id; nsapi_error_t status; @@ -75,6 +81,7 @@ struct DNS_QUERY { uint8_t retries; uint8_t total_attempts; uint8_t count; + dns_state state; }; static void nsapi_dns_cache_add(const char *host, nsapi_addr_t *address, uint32_t ttl); @@ -85,10 +92,12 @@ static nsapi_error_t nsapi_dns_get_server_addr(NetworkStack *stack, uint8_t *ind 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_timeout(void); static void nsapi_dns_query_async_resp(DNS_QUERY *query, nsapi_error_t status, SocketAddress *address); static void nsapi_dns_query_async_socket_callback(void *ptr); static void nsapi_dns_query_async_socket_callback_handle(NetworkStack *stack); static void nsapi_dns_query_async_response(void *ptr); +static void nsapi_dns_query_async_initiate_next(void); static nsapi_addr_t dns_servers[DNS_SERVERS_SIZE] = { {NSAPI_IPv4, {8, 8, 8, 8}}, // Google @@ -109,6 +118,7 @@ static PlatformMutex dns_cache_mutex; // Protects from several threads running asynchronous DNS static PlatformMutex dns_mutex; static call_in_callback_cb_t dns_call_in = 0; +static bool dns_timer_running = false; // DNS server configuration extern "C" nsapi_error_t nsapi_dns_add_server(nsapi_addr_t addr) @@ -213,10 +223,14 @@ static int dns_scan_response(const uint8_t *ptr, uint16_t exp_id, uint32_t *ttl, dns_scan_word(p); // arcount // verify header is response to query - if (!(id == exp_id && qr && opcode == 0 && rcode == 0)) { + if (!(id == exp_id && qr && opcode == 0)) { return -1; } + if (rcode != 0) { + return 0; + } + // skip questions for (int i = 0; i < qdcount; i++) { while (true) { @@ -571,12 +585,12 @@ nsapi_error_t nsapi_dns_call_in(call_in_callback_cb_t cb, int delay, mbed::Callb return NSAPI_ERROR_OK; } -void nsapi_dns_query_async_timeout(void); - nsapi_value_or_error_t nsapi_dns_query_multiple_async(NetworkStack *stack, const char *host, NetworkStack::hostbyname_cb_t callback, nsapi_size_t addr_count, call_in_callback_cb_t call_in_cb, nsapi_version_t version) { + dns_mutex.lock(); + if (!stack) { return NSAPI_ERROR_PARAMETER; } @@ -584,18 +598,18 @@ nsapi_value_or_error_t nsapi_dns_query_multiple_async(NetworkStack *stack, const // check for valid host name int host_len = host ? strlen(host) : 0; if (host_len > DNS_HOST_NAME_MAX_LEN || host_len == 0) { + dns_mutex.unlock(); return NSAPI_ERROR_PARAMETER; } nsapi_addr address; if (nsapi_dns_cache_find(host, version, &address) == NSAPI_ERROR_OK) { SocketAddress addr(address); + dns_mutex.unlock(); callback(NSAPI_ERROR_OK, &addr); return NSAPI_ERROR_OK; } - dns_mutex.lock(); - int index = -1; for (int i = 0; i < DNS_QUERY_QUEUE_SIZE; i++) { @@ -617,9 +631,14 @@ nsapi_value_or_error_t nsapi_dns_query_multiple_async(NetworkStack *stack, const return NSAPI_ERROR_NO_MEMORY; } - query->host = new (std::nothrow) char[host_len + 1]; + query->host = new (std::nothrow) char[host_len + 1]; + if (!query->host) { + delete query; + dns_mutex.unlock(); + return NSAPI_ERROR_NO_MEMORY; + } strcpy(query->host, host); - query->status = NSAPI_ERROR_DEVICE_ERROR; + query->status = NSAPI_ERROR_TIMEOUT; query->callback = callback; query->call_in_cb = call_in_cb; query->stack = stack; @@ -634,6 +653,8 @@ nsapi_value_or_error_t nsapi_dns_query_multiple_async(NetworkStack *stack, const query->dns_message_id = 0; query->socket_timeout = 0; query->total_timeout = MBED_CONF_NSAPI_DNS_TOTAL_ATTEMPTS * MBED_CONF_NSAPI_DNS_RESPONSE_WAIT_TIME + 500; + query->count = 0; + query->state = DNS_CREATED; query->unique_id = dns_unique_id++; if (query->unique_id > 0x7FFF) { @@ -657,28 +678,52 @@ nsapi_value_or_error_t nsapi_dns_query_multiple_async(NetworkStack *stack, const // Add some overhead based on number of ongoing queries query->total_timeout += ongoing_queries * 500; - if (ongoing_queries == 0) { + if (!dns_timer_running) { if (nsapi_dns_call_in(query->call_in_cb, DNS_TIMER_TIMEOUT, mbed::callback(nsapi_dns_query_async_timeout)) != NSAPI_ERROR_OK) { delete query->host; delete query; dns_mutex.unlock(); return NSAPI_ERROR_NO_MEMORY; } - - 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->host; - delete query; - dns_mutex.unlock(); - return NSAPI_ERROR_NO_MEMORY; - } + dns_timer_running = true; } + // Initiates query + nsapi_dns_query_async_initiate_next(); + dns_mutex.unlock(); return query->unique_id; } -void nsapi_dns_query_async_timeout(void) +static void nsapi_dns_query_async_initiate_next(void) +{ + int id = INT32_MAX; + DNS_QUERY *query = NULL; + + // Trigger next query to start, find one that has been on queue longest + for (int i = 0; i < DNS_QUERY_QUEUE_SIZE; i++) { + if (dns_query_queue[i]) { + if (dns_query_queue[i]->state == DNS_CREATED) { + if (dns_query_queue[i]->unique_id <= id) { + query = dns_query_queue[i]; + id = dns_query_queue[i]->unique_id; + } + // If some query is already ongoing do not trigger + } else if (dns_query_queue[i]->state == DNS_INITIATED) { + query = NULL; + break; + } + } + } + + if (query) { + query->state = DNS_INITIATED; + nsapi_dns_call_in(query->call_in_cb, 0, mbed::callback(nsapi_dns_query_async_create, reinterpret_cast(query->unique_id))); + } +} + +static void nsapi_dns_query_async_timeout(void) { dns_mutex.lock(); @@ -686,13 +731,19 @@ void nsapi_dns_query_async_timeout(void) for (int i = 0; i < DNS_QUERY_QUEUE_SIZE; i++) { if (dns_query_queue[i]) { + if (dns_query_queue[i]->state == DNS_CANCELLED) { + // Delete cancelled + nsapi_dns_query_async_delete(dns_query_queue[i]->unique_id); + nsapi_dns_query_async_initiate_next(); + continue; + } if (dns_query_queue[i]->total_timeout > DNS_TIMER_TIMEOUT) { dns_query_queue[i]->total_timeout -= DNS_TIMER_TIMEOUT; } else { // If does not already have response, fails - if (query->status == NSAPI_ERROR_DEVICE_ERROR) { - query->socket_timeout = 0; + if (dns_query_queue[i]->status == NSAPI_ERROR_TIMEOUT) { + dns_query_queue[i]->socket_timeout = 0; nsapi_dns_call_in(dns_query_queue[i]->call_in_cb, 0, mbed::callback(nsapi_dns_query_async_response, reinterpret_cast(dns_query_queue[i]->unique_id))); } } @@ -717,6 +768,8 @@ void nsapi_dns_query_async_timeout(void) // Starts timer again if (query) { nsapi_dns_call_in(query->call_in_cb, DNS_TIMER_TIMEOUT, mbed::callback(nsapi_dns_query_async_timeout)); + } else { + dns_timer_running = false; } dns_mutex.unlock(); @@ -726,11 +779,28 @@ nsapi_error_t nsapi_dns_query_async_cancel(int id) { dns_mutex.lock(); - nsapi_error_t ret = nsapi_dns_query_async_delete(id); + 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 == id) { + query = dns_query_queue[i]; + break; + } + } + + if (!query || query->state == DNS_CANCELLED) { + dns_mutex.unlock(); + return NSAPI_ERROR_PARAMETER; + } + + // Mark the query as cancelled, deleted by timer handler + query->state = DNS_CANCELLED; + // Do not call callback + query->callback = 0; dns_mutex.unlock(); - return ret; + return NSAPI_ERROR_OK; } static void nsapi_dns_query_async_create(void *ptr) @@ -748,32 +818,21 @@ static void nsapi_dns_query_async_create(void *ptr) } } - if (!query) { + if (!query || query->state == DNS_CANCELLED) { // Cancel has been called dns_mutex.unlock(); return; } - bool ongoing = false; - for (int i = 0; i < DNS_QUERY_QUEUE_SIZE; i++) { if (dns_query_queue[i] && dns_query_queue[i] != query) { if (!query->socket && dns_query_queue[i]->socket && dns_query_queue[i]->stack == query->stack) { query->socket = dns_query_queue[i]->socket; query->socket_cb_data = dns_query_queue[i]->socket_cb_data; } - if (dns_query_queue[i]->dns_message_id != 0) { - ongoing = true; - } } } - if (ongoing) { - // If there is already operation ongoing exits - dns_mutex.unlock(); - return; - } - UDPSocket *socket; if (query->socket) { @@ -786,6 +845,7 @@ static void nsapi_dns_query_async_create(void *ptr) } int err = socket->open(query->stack); + if (err) { delete socket; nsapi_dns_query_async_resp(query, err, NULL); @@ -807,46 +867,49 @@ static void nsapi_dns_query_async_create(void *ptr) 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; + 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]; index = i; break; } } - if (index < 0) { - return NSAPI_ERROR_DEVICE_ERROR; + if (!query) { + return NSAPI_ERROR_PARAMETER; } 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) { + if (dns_query_queue[i] && dns_query_queue[i] != query && dns_query_queue[i]->socket && + dns_query_queue[i]->stack == query->stack) { close_socket = false; } } - if (close_socket && dns_query_queue[index]->socket) { - dns_query_queue[index]->socket->close(); - delete dns_query_queue[index]->socket; - delete dns_query_queue[index]->socket_cb_data; + if (close_socket && query->socket) { + query->socket->close(); + delete query->socket; + delete query->socket_cb_data; } - if (dns_query_queue[index]->addrs) { - delete[] dns_query_queue[index]->addrs; + if (query->addrs) { + delete[] query->addrs; } - delete dns_query_queue[index]->host; - - delete dns_query_queue[index]; + delete query->host; + delete query; dns_query_queue[index] = NULL; + return NSAPI_ERROR_OK; } @@ -854,27 +917,13 @@ static void nsapi_dns_query_async_resp(DNS_QUERY *query, nsapi_error_t status, S { NetworkStack::hostbyname_cb_t callback = query->callback; nsapi_dns_query_async_delete(query->unique_id); - - DNS_QUERY *next_query = NULL; - int unique_id = INT32_MAX; - - // Find one that has been on queue longest - for (int i = 0; i < DNS_QUERY_QUEUE_SIZE; i++) { - if (dns_query_queue[i]) { - if (dns_query_queue[i]->unique_id <= unique_id) { - next_query = dns_query_queue[i]; - unique_id = dns_query_queue[i]->unique_id; - } - } - } - - if (next_query) { - nsapi_dns_call_in(next_query->call_in_cb, 0, mbed::callback(nsapi_dns_query_async_create, reinterpret_cast(next_query->unique_id))); - } + nsapi_dns_query_async_initiate_next(); dns_mutex.unlock(); - callback(status, address); + if (callback) { + callback(status, address); + } } static void nsapi_dns_query_async_send(void *ptr) @@ -892,7 +941,7 @@ static void nsapi_dns_query_async_send(void *ptr) } } - if (!query) { + if (!query || query->state != DNS_INITIATED) { // Cancel has been called dns_mutex.unlock(); return; @@ -924,7 +973,7 @@ static void nsapi_dns_query_async_send(void *ptr) SocketAddress dns_addr; nsapi_size_or_error_t err = nsapi_dns_get_server_addr(query->stack, &(query->dns_server), &(query->total_attempts), &dns_addr); if (err != NSAPI_ERROR_OK) { - nsapi_dns_query_async_resp(query, NSAPI_ERROR_DNS_FAILURE, NULL); + nsapi_dns_query_async_resp(query, NSAPI_ERROR_TIMEOUT, NULL); free(packet); return; } @@ -999,7 +1048,7 @@ static void nsapi_dns_query_async_socket_callback_handle(NetworkStack *stack) } } - if (!query) { + if (!query || query->state != DNS_INITIATED) { continue; } @@ -1045,9 +1094,9 @@ static void nsapi_dns_query_async_response(void *ptr) } } - if (query) { + if (query && query->state == DNS_INITIATED) { SocketAddress *addresses = NULL; - nsapi_error_t status = query->status; //NSAPI_ERROR_OK; + nsapi_error_t status = query->status; if (query->count > 0) { addresses = new (std::nothrow) SocketAddress[query->count]; @@ -1066,10 +1115,8 @@ static void nsapi_dns_query_async_response(void *ptr) } nsapi_dns_query_async_resp(query, status, addresses); - delete[] addresses; } else { dns_mutex.unlock(); } } - diff --git a/features/netsocket/nsapi_types.h b/features/netsocket/nsapi_types.h index fe0ae8f96c..48fca1d011 100644 --- a/features/netsocket/nsapi_types.h +++ b/features/netsocket/nsapi_types.h @@ -54,6 +54,7 @@ enum nsapi_error { NSAPI_ERROR_CONNECTION_LOST = -3016, /*!< connection lost */ NSAPI_ERROR_CONNECTION_TIMEOUT = -3017, /*!< connection timed out */ NSAPI_ERROR_ADDRESS_IN_USE = -3018, /*!< Address already in use */ + NSAPI_ERROR_TIMEOUT = -3019, /*!< operation timed out */ };