From d81482d699232b22f4a5cbee2a76199a5285dadb Mon Sep 17 00:00:00 2001 From: David Cermak Date: Thu, 3 Jun 2021 16:56:12 +0200 Subject: [PATCH] mdns: Add asynchronous query API Closes https://github.com/espressif/esp-idf/issues/7090 --- components/mdns/include/mdns.h | 48 ++++++++++++++++++ components/mdns/mdns.c | 46 +++++++++++++++++ .../mdns/test_afl_fuzz_host/esp32_compat.h | 4 +- .../protocols/mdns/main/mdns_example_main.c | 49 +++++++++++++++++++ 4 files changed, 145 insertions(+), 2 deletions(-) diff --git a/components/mdns/include/mdns.h b/components/mdns/include/mdns.h index 29c22b022a..7089eabe6a 100644 --- a/components/mdns/include/mdns.h +++ b/components/mdns/include/mdns.h @@ -30,6 +30,11 @@ extern "C" { #define MDNS_TYPE_NSEC 0x002F #define MDNS_TYPE_ANY 0x00FF +/** + * @brief Asynchronous query handle + */ +typedef struct mdns_search_once_s mdns_search_once_t; + /** * @brief mDNS enum to specify the ip_protocol type */ @@ -477,6 +482,49 @@ esp_err_t mdns_service_txt_item_remove_for_host(const char * service_type, const */ esp_err_t mdns_service_remove_all(void); +/** + * @brief Deletes the finished query. Call this only after the search has ended! + * + * @param search pointer to search object + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_STATE search has not finished + * - ESP_ERR_INVALID_ARG pointer to search object is NULL + */ +esp_err_t mdns_query_async_delete(mdns_search_once_t* search); + +/** + * @brief Get results from search pointer. Results available as a pointer to the output parameter. + * Pointer to search object has to be deleted via `mdns_query_async_delete` once the query has finished. + * The results although have to be freed manually. + * + * @param search pointer to search object + * @param timeout time in milliseconds to wait for answers + * @param results pointer to the results of the query + * + * @return + * True if search has finished before or at timeout + * False if search timeout is over + */ +bool mdns_query_async_get_results(mdns_search_once_t* search, uint32_t timeout, mdns_result_t ** results); + +/** + * @brief Query mDNS for host or service asynchronousely. + * Search has to be tested for progress and deleted manually! + * + * @param name service instance or host name (NULL for PTR queries) + * @param service_type service type (_http, _arduino, _ftp etc.) (NULL for host queries) + * @param proto service protocol (_tcp, _udp, etc.) (NULL for host queries) + * @param type type of query (MDNS_TYPE_*) + * @param timeout time in milliseconds during which mDNS query is active + * @param max_results maximum results to be collected + * + * @return mdns_search_once_s pointer to new search object if query initiated successfully. + * NULL otherwise. + */ +mdns_search_once_t* mdns_query_async_new(const char * name, const char * service_type, const char * proto, uint16_t type, uint32_t timeout, size_t max_results); + /** * @brief Query mDNS for host or service * All following query methods are derived from this one diff --git a/components/mdns/mdns.c b/components/mdns/mdns.c index e55f8c1945..3e239e0663 100644 --- a/components/mdns/mdns.c +++ b/components/mdns/mdns.c @@ -5292,6 +5292,52 @@ void mdns_query_results_free(mdns_result_t * results) } } +esp_err_t mdns_query_async_delete(mdns_search_once_t* search) +{ + if (!search) { + return ESP_ERR_INVALID_ARG; + } + if (search->state != SEARCH_OFF) { + return ESP_ERR_INVALID_STATE; + } + + MDNS_SERVICE_LOCK(); + _mdns_search_free(search); + MDNS_SERVICE_UNLOCK(); + + return ESP_OK; +} + +bool mdns_query_async_get_results(mdns_search_once_t* search, uint32_t timeout, mdns_result_t ** results) +{ + if (xSemaphoreTake(search->done_semaphore, pdMS_TO_TICKS(timeout)) == pdTRUE) { + *results = search->result; + return true; + } + return false; +} + +mdns_search_once_t* mdns_query_async_new(const char * name, const char * service, const char * proto, uint16_t type, uint32_t timeout, size_t max_results) +{ + mdns_search_once_t *search = NULL; + + if (!_mdns_server || !timeout || _str_null_or_empty(service) != _str_null_or_empty(proto)) { + return NULL; + } + + search = _mdns_search_init(name, service, proto, type, timeout, max_results); + if (!search) { + return NULL; + } + + if (_mdns_send_search_action(ACTION_SEARCH_ADD, search)) { + _mdns_search_free(search); + return NULL; + } + + return search; +} + esp_err_t mdns_query(const char * name, const char * service, const char * proto, uint16_t type, uint32_t timeout, size_t max_results, mdns_result_t ** results) { mdns_search_once_t * search = NULL; diff --git a/components/mdns/test_afl_fuzz_host/esp32_compat.h b/components/mdns/test_afl_fuzz_host/esp32_compat.h index 2c980baaf0..984f4096b7 100644 --- a/components/mdns/test_afl_fuzz_host/esp32_compat.h +++ b/components/mdns/test_afl_fuzz_host/esp32_compat.h @@ -63,10 +63,10 @@ #define pdMS_TO_TICKS(a) a #define portTICK_RATE_MS 10 -#define xSemaphoreTake(s,d) #define xTaskDelete(a) -#define vTaskDelete(a) free(a) +#define vTaskDelete(a) free(a) #define xSemaphoreGive(s) +#define xSemaphoreTake(s,d) true #define xQueueCreateMutex(s) #define _mdns_pcb_init(a,b) true #define _mdns_pcb_deinit(a,b) true diff --git a/examples/protocols/mdns/main/mdns_example_main.c b/examples/protocols/mdns/main/mdns_example_main.c index ff026fd625..fc5b450374 100644 --- a/examples/protocols/mdns/main/mdns_example_main.c +++ b/examples/protocols/mdns/main/mdns_example_main.c @@ -138,6 +138,54 @@ static void query_mdns_service(const char * service_name, const char * proto) mdns_query_results_free(results); } +static bool check_and_print_result(mdns_search_once_t *search) +{ + // Check if any result is available + mdns_result_t * result = NULL; + if (!mdns_query_async_get_results(search, 0, &result)) { + return false; + } + + if (!result) { // search timeout, but no result + return true; + } + + // If yes, print the result + mdns_ip_addr_t * a = result->addr; + while (a) { + if(a->addr.type == ESP_IPADDR_TYPE_V6){ + printf(" AAAA: " IPV6STR "\n", IPV62STR(a->addr.u_addr.ip6)); + } else { + printf(" A : " IPSTR "\n", IP2STR(&(a->addr.u_addr.ip4))); + } + a = a->next; + } + // and free the result + mdns_query_results_free(result); + return true; +} + +static void query_mdns_hosts_async(const char * host_name) +{ + ESP_LOGI(TAG, "Query both A and AAA: %s.local", host_name); + + mdns_search_once_t *s_a = mdns_query_async_new(host_name, NULL, NULL, MDNS_TYPE_A, 1000, 1); + mdns_query_async_delete(s_a); + mdns_search_once_t *s_aaaa = mdns_query_async_new(host_name, NULL, NULL, MDNS_TYPE_AAAA, 1000, 1); + while (s_a || s_aaaa) { + if (s_a && check_and_print_result(s_a)) { + ESP_LOGI(TAG, "Query A %s.local finished", host_name); + mdns_query_async_delete(s_a); + s_a = NULL; + } + if (s_aaaa && check_and_print_result(s_aaaa)) { + ESP_LOGI(TAG, "Query AAAA %s.local finished", host_name); + mdns_query_async_delete(s_aaaa); + s_aaaa = NULL; + } + } +} + static void query_mdns_host(const char * host_name) { ESP_LOGI(TAG, "Query A: %s.local", host_name); @@ -174,6 +222,7 @@ static void check_button(void) static bool old_level = true; bool new_level = gpio_get_level(EXAMPLE_BUTTON_GPIO); if (!new_level && old_level) { + query_mdns_hosts_async("esp32-mdns"); query_mdns_host("esp32"); query_mdns_service("_arduino", "_tcp"); query_mdns_service("_http", "_tcp");