diff --git a/components/mdns/include/mdns.h b/components/mdns/include/mdns.h index 22da2afabc..416740d137 100644 --- a/components/mdns/include/mdns.h +++ b/components/mdns/include/mdns.h @@ -114,6 +114,50 @@ void mdns_free(void); */ esp_err_t mdns_hostname_set(const char * hostname); +/** + * @brief Adds a hostname and address to be delegated + * A/AAAA queries will be replied for the hostname and + * services can be added to this host. + * + * @param hostname Hostname to add + * @param address_list The IP address list of the host + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_STATE mDNS is not running + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_ERR_NO_MEM memory error + * + */ +esp_err_t mdns_delegate_hostname_add(const char * hostname, const mdns_ip_addr_t *address_list); + +/** + * @brief Remove a delegated hostname + * All the services added to this host will also be removed. + * + * @param hostname Hostname to remove + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_STATE mDNS is not running + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_ERR_NO_MEM memory error + * + */ +esp_err_t mdns_delegate_hostname_remove(const char * hostname); + +/** + * @brief Query whether a hostname has been added + * + * @param hostname Hostname to query + * + * @return + * - true The hostname has been added. + * - false The hostname has not been added. + * + */ +bool mdns_hostname_exists(const char * hostname); + /** * @brief Set the default instance name for mDNS server * @@ -145,6 +189,41 @@ esp_err_t mdns_instance_name_set(const char * instance_name); */ esp_err_t mdns_service_add(const char * instance_name, const char * service_type, const char * proto, uint16_t port, mdns_txt_item_t txt[], size_t num_items); + +/** + * @brief Add service to mDNS server with a delegated hostname + * + * @param instance_name instance name to set. If NULL, + * global instance name or hostname will be used + * @param service_type service type (_http, _ftp, etc) + * @param proto service protocol (_tcp, _udp) + * @param hostname service hostname. If NULL, local hostname will be used. + * @param port service port + * @param txt string array of TXT data (eg. {{"var","val"},{"other","2"}}) + * @param num_items number of items in TXT data + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_ERR_NO_MEM memory error + * - ESP_FAIL failed to add service + */ +esp_err_t mdns_service_add_for_host(const char * instance_name, const char * service_type, const char * proto, + const char * hostname, uint16_t port, mdns_txt_item_t txt[], size_t num_items); + +/** + * @brief Check whether a service has been added. + * + * @param service_type service type (_http, _ftp, etc) + * @param proto service protocol (_tcp, _udp) + * @param hostname service hostname. If NULL, checks for the local hostname. + * + * @return + * - true Correspondding service has been added. + * - false Service not found. + */ +bool mdns_service_exists(const char * service_type, const char * proto, const char * hostname); + /** * @brief Remove service from mDNS server * @@ -159,6 +238,21 @@ esp_err_t mdns_service_add(const char * instance_name, const char * service_type */ esp_err_t mdns_service_remove(const char * service_type, const char * proto); +/** + * @brief Remove service from mDNS server with hostname + * + * @param service_type service type (_http, _ftp, etc) + * @param proto service protocol (_tcp, _udp) + * @param hostname service hostname. If NULL, local hostname will be used. + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_ERR_NOT_FOUND Service not found + * - ESP_ERR_NO_MEM memory error + */ +esp_err_t mdns_service_remove_for_host(const char * service_type, const char * proto, const char *hostname); + /** * @brief Set instance name for service * @@ -174,6 +268,23 @@ esp_err_t mdns_service_remove(const char * service_type, const char * proto); */ esp_err_t mdns_service_instance_name_set(const char * service_type, const char * proto, const char * instance_name); +/** + * @brief Set instance name for service with hostname + * + * @param service_type service type (_http, _ftp, etc) + * @param proto service protocol (_tcp, _udp) + * @param hostname service hostname. If NULL, local hostname will be used. + * @param instance_name instance name to set + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_ERR_NOT_FOUND Service not found + * - ESP_ERR_NO_MEM memory error + */ +esp_err_t mdns_service_instance_name_set_for_host(const char * service_type, const char * proto, const char * hostname, + const char * instance_name); + /** * @brief Set service port * @@ -189,6 +300,24 @@ esp_err_t mdns_service_instance_name_set(const char * service_type, const char * */ esp_err_t mdns_service_port_set(const char * service_type, const char * proto, uint16_t port); + +/** + * @brief Set service port with hostname + * + * @param service_type service type (_http, _ftp, etc) + * @param proto service protocol (_tcp, _udp) + * @param hostname service hostname. If NULL, local hostname will be used. + * @param port service port + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_ERR_NOT_FOUND Service not found + * - ESP_ERR_NO_MEM memory error + */ +esp_err_t mdns_service_port_set_for_host(const char * service_type, const char * proto, const char * hostname, + uint16_t port); + /** * @brief Replace all TXT items for service * @@ -205,6 +334,24 @@ esp_err_t mdns_service_port_set(const char * service_type, const char * proto, u */ esp_err_t mdns_service_txt_set(const char * service_type, const char * proto, mdns_txt_item_t txt[], uint8_t num_items); +/** + * @brief Replace all TXT items for service with hostname + * + * @param service_type service type (_http, _ftp, etc) + * @param proto service protocol (_tcp, _udp) + * @param hostname service hostname. If NULL, local hostname will be used. + * @param txt array of TXT data (eg. {{"var","val"},{"other","2"}}) + * @param num_items number of items in TXT data + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_ERR_NOT_FOUND Service not found + * - ESP_ERR_NO_MEM memory error + */ +esp_err_t mdns_service_txt_set_for_host(const char * service_type, const char * proto, const char * hostname, + mdns_txt_item_t txt[], uint8_t num_items); + /** * @brief Set/Add TXT item for service TXT record * @@ -221,6 +368,25 @@ esp_err_t mdns_service_txt_set(const char * service_type, const char * proto, md */ esp_err_t mdns_service_txt_item_set(const char * service_type, const char * proto, const char * key, const char * value); + +/** + * @brief Set/Add TXT item for service TXT record with hostname + * + * @param service_type service type (_http, _ftp, etc) + * @param proto service protocol (_tcp, _udp) + * @param hostname service hostname. If NULL, local hostname will be used. + * @param key the key that you want to add/update + * @param value the new value of the key + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_ERR_NOT_FOUND Service not found + * - ESP_ERR_NO_MEM memory error + */ +esp_err_t mdns_service_txt_item_set_for_host(const char * service_type, const char * proto, const char * hostname, + const char * key, const char * value); + /** * @brief Remove TXT item for service TXT record * @@ -236,6 +402,23 @@ esp_err_t mdns_service_txt_item_set(const char * service_type, const char * prot */ esp_err_t mdns_service_txt_item_remove(const char * service_type, const char * proto, const char * key); +/** + * @brief Remove TXT item for service TXT record with hostname + * + * @param service_type service type (_http, _ftp, etc) + * @param proto service protocol (_tcp, _udp) + * @param hostname service hostname. If NULL, local hostname will be used. + * @param key the key that you want to remove + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_ERR_NOT_FOUND Service not found + * - ESP_ERR_NO_MEM memory error + */ +esp_err_t mdns_service_txt_item_remove_for_host(const char * service_type, const char * proto, const char * hostname, + const char * key); + /** * @brief Remove and free all services from mDNS server * diff --git a/components/mdns/mdns.c b/components/mdns/mdns.c index af3ef03d79..fe2c2f85f1 100644 --- a/components/mdns/mdns.c +++ b/components/mdns/mdns.c @@ -31,6 +31,8 @@ static const char * MDNS_DEFAULT_DOMAIN = "local"; static const char * MDNS_SUB_STR = "_sub"; mdns_server_t * _mdns_server = NULL; +static mdns_host_item_t * _mdns_host_list = NULL; +static mdns_host_item_t _mdns_self_host; static const char *TAG = "MDNS"; @@ -43,6 +45,9 @@ static void _mdns_search_result_add_ip(mdns_search_once_t * search, const char * static void _mdns_search_result_add_srv(mdns_search_once_t * search, const char * hostname, uint16_t port, mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol); static void _mdns_search_result_add_txt(mdns_search_once_t * search, mdns_txt_item_t * txt, size_t txt_count, mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol); static mdns_result_t * _mdns_search_result_add_ptr(mdns_search_once_t * search, const char * instance, mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol); +static bool _mdns_append_host_list_in_services(mdns_out_answer_t ** destination, mdns_srv_item_t * services[], size_t services_len, bool flush, bool bye); +static bool _mdns_append_host_list(mdns_out_answer_t ** destination, bool flush, bool bye); +static void _mdns_remap_self_service_hostname(const char *old_hostname, const char *new_hostname); /* * @brief Internal collection of mdns supported interfaces @@ -143,19 +148,27 @@ static char * _mdns_mangle_name(char* in) { return ret; } +static bool _mdns_service_match(const mdns_service_t * srv, const char * service, const char * proto, + const char * hostname) +{ + return !strcasecmp(srv->service, service) && !strcasecmp(srv->proto, proto) && + (_str_null_or_empty(hostname) || !strcasecmp(srv->hostname, hostname)); +} + /** * @brief finds service from given service type * @param server the server * @param service service type to match * @param proto proto to match + * @param hostname hostname of the service (if non-null) * * @return the service item if found or NULL on error */ -static mdns_srv_item_t * _mdns_get_service_item(const char * service, const char * proto) +static mdns_srv_item_t * _mdns_get_service_item(const char * service, const char * proto, const char * hostname) { mdns_srv_item_t * s = _mdns_server->services; while (s) { - if (!strcasecmp(s->service->service, service) && !strcasecmp(s->service->proto, proto)) { + if (_mdns_service_match(s->service, service, proto, hostname)) { return s; } s = s->next; @@ -163,6 +176,21 @@ static mdns_srv_item_t * _mdns_get_service_item(const char * service, const char return NULL; } +static mdns_host_item_t * mdns_get_host_item(const char * hostname) +{ + if (strcasecmp(hostname, _mdns_server->hostname) == 0) { + return &_mdns_self_host; + } + mdns_host_item_t * host = _mdns_host_list; + while (host != NULL) { + if (strcasecmp(host->hostname, hostname) == 0) { + return host; + } + host = host->next; + } + return NULL; +} + static bool _mdns_can_add_more_services(void) { mdns_srv_item_t * s = _mdns_server->services; @@ -704,7 +732,11 @@ static uint16_t _mdns_append_srv_record(uint8_t * packet, uint16_t * index, mdns return 0; } - str[0] = _mdns_server->hostname; + if (service->hostname) { + str[0] = service->hostname; + } else { + str[0] = _mdns_server->hostname; + } str[1] = MDNS_DEFAULT_DOMAIN; if (_str_null_or_empty(str[0])) { @@ -726,25 +758,24 @@ static uint16_t _mdns_append_srv_record(uint8_t * packet, uint16_t * index, mdns * * @param packet MDNS packet * @param index offset in the packet - * @param server the server + * @param hostname the hostname address to add * @param ip the IP address to add * * @return length of added data: 0 on error or length on success */ -static uint16_t _mdns_append_a_record(uint8_t * packet, uint16_t * index, uint32_t ip, bool flush, bool bye) +static uint16_t _mdns_append_a_record(uint8_t * packet, uint16_t * index, const char * hostname, uint32_t ip, bool flush, bool bye) { const char * str[2]; uint16_t record_length = 0; uint8_t part_length; - str[0] = _mdns_server->hostname; + str[0] = hostname; str[1] = MDNS_DEFAULT_DOMAIN; if (_str_null_or_empty(str[0])) { return 0; } - part_length = _mdns_append_fqdn(packet, index, str, 2); if (!part_length) { return 0; @@ -778,17 +809,18 @@ static uint16_t _mdns_append_a_record(uint8_t * packet, uint16_t * index, uint32 * * @param packet MDNS packet * @param index offset in the packet + * @param hostname the hostname address to add * @param ipv6 the IPv6 address to add * * @return length of added data: 0 on error or length on success */ -static uint16_t _mdns_append_aaaa_record(uint8_t * packet, uint16_t * index, uint8_t * ipv6, bool flush, bool bye) +static uint16_t _mdns_append_aaaa_record(uint8_t * packet, uint16_t * index, const char * hostname, uint8_t * ipv6, bool flush, bool bye) { const char * str[2]; uint16_t record_length = 0; uint8_t part_length; - str[0] = _mdns_server->hostname; + str[0] = hostname; str[1] = MDNS_DEFAULT_DOMAIN; if (_str_null_or_empty(str[0])) { @@ -904,6 +936,32 @@ static bool _ipv6_address_is_zero(esp_ip6_addr_t ip6) } #endif +static uint8_t _mdns_append_host_answer(uint8_t * packet, uint16_t * index, mdns_host_item_t * host, + uint8_t address_type, bool flush, bool bye) +{ + mdns_ip_addr_t * addr = host->address_list; + uint8_t num_records = 0; + + while (addr != NULL) { + if (addr->addr.type == address_type) { + if (address_type == ESP_IPADDR_TYPE_V4 && + _mdns_append_a_record(packet, index, host->hostname, addr->addr.u_addr.ip4.addr, flush, bye) <= 0) { + break; + } +#if CONFIG_LWIP_IPV6 + if (address_type == ESP_IPADDR_TYPE_V6 && + _mdns_append_aaaa_record(packet, index, host->hostname, (uint8_t *)addr->addr.u_addr.ip6.addr, flush, + bye) <= 0) { + break; + } +#endif // CONFIG_LWIP_IPV6 + num_records++; + } + addr = addr->next; + } + return num_records; +} + /** * @brief Append answer to packet * @@ -930,54 +988,62 @@ static uint8_t _mdns_append_answer(uint8_t * packet, uint16_t * index, mdns_out_ } else if (answer->type == MDNS_TYPE_SDPTR) { return _mdns_append_sdptr_record(packet, index, answer->service, answer->flush, answer->bye) > 0; } else if (answer->type == MDNS_TYPE_A) { - esp_netif_ip_info_t if_ip_info; - if (!_mdns_server->interfaces[tcpip_if].pcbs[MDNS_IP_PROTOCOL_V4].pcb && _mdns_server->interfaces[tcpip_if].pcbs[MDNS_IP_PROTOCOL_V4].state != PCB_DUP) { - return 0; - } - if (esp_netif_get_ip_info(_mdns_get_esp_netif(tcpip_if), &if_ip_info)) { - return 0; - } - if (_mdns_append_a_record(packet, index, if_ip_info.ip.addr, answer->flush, answer->bye) <= 0) { - return 0; - } - if (!_mdns_if_is_dup(tcpip_if)) { + if (answer->host == &_mdns_self_host) { + esp_netif_ip_info_t if_ip_info; + if (!_mdns_server->interfaces[tcpip_if].pcbs[MDNS_IP_PROTOCOL_V4].pcb && _mdns_server->interfaces[tcpip_if].pcbs[MDNS_IP_PROTOCOL_V4].state != PCB_DUP) { + return 0; + } + if (esp_netif_get_ip_info(_mdns_get_esp_netif(tcpip_if), &if_ip_info)) { + return 0; + } + if (_mdns_append_a_record(packet, index, _mdns_server->hostname, if_ip_info.ip.addr, answer->flush, answer->bye) <= 0) { + return 0; + } + if (!_mdns_if_is_dup(tcpip_if)) { + return 1; + } + mdns_if_t other_if = _mdns_get_other_if (tcpip_if); + if (esp_netif_get_ip_info(_mdns_get_esp_netif(other_if), &if_ip_info)) { + return 1; + } + if (_mdns_append_a_record(packet, index, _mdns_server->hostname, if_ip_info.ip.addr, answer->flush, answer->bye) > 0) { + return 2; + } return 1; + } else if (answer->host != NULL) { + return _mdns_append_host_answer(packet, index, answer->host, ESP_IPADDR_TYPE_V4, answer->flush, answer->bye); } - mdns_if_t other_if = _mdns_get_other_if (tcpip_if); - if (esp_netif_get_ip_info(_mdns_get_esp_netif(other_if), &if_ip_info)) { - return 1; - } - if (_mdns_append_a_record(packet, index, if_ip_info.ip.addr, answer->flush, answer->bye) > 0) { - return 2; - } - return 1; } #if CONFIG_LWIP_IPV6 else if (answer->type == MDNS_TYPE_AAAA) { - struct esp_ip6_addr if_ip6; - if (!_mdns_server->interfaces[tcpip_if].pcbs[MDNS_IP_PROTOCOL_V6].pcb && _mdns_server->interfaces[tcpip_if].pcbs[MDNS_IP_PROTOCOL_V6].state != PCB_DUP) { - return 0; - } - if (esp_netif_get_ip6_linklocal(_mdns_get_esp_netif(tcpip_if), &if_ip6)) { - return 0; - } - if (_ipv6_address_is_zero(if_ip6)) { - return 0; - } - if (_mdns_append_aaaa_record(packet, index, (uint8_t*)if_ip6.addr, answer->flush, answer->bye) <= 0) { - return 0; - } - if (!_mdns_if_is_dup(tcpip_if)) { + if (answer->host == &_mdns_self_host) { + struct esp_ip6_addr if_ip6; + if (!_mdns_server->interfaces[tcpip_if].pcbs[MDNS_IP_PROTOCOL_V6].pcb && _mdns_server->interfaces[tcpip_if].pcbs[MDNS_IP_PROTOCOL_V6].state != PCB_DUP) { + return 0; + } + if (esp_netif_get_ip6_linklocal(_mdns_get_esp_netif(tcpip_if), &if_ip6)) { + return 0; + } + if (_ipv6_address_is_zero(if_ip6)) { + return 0; + } + if (_mdns_append_aaaa_record(packet, index, _mdns_server->hostname, (uint8_t*)if_ip6.addr, answer->flush, answer->bye) <= 0) { + return 0; + } + if (!_mdns_if_is_dup(tcpip_if)) { + return 1; + } + mdns_if_t other_if = _mdns_get_other_if (tcpip_if); + if (esp_netif_get_ip6_linklocal(_mdns_get_esp_netif(other_if), &if_ip6)) { + return 1; + } + if (_mdns_append_aaaa_record(packet, index, _mdns_server->hostname, (uint8_t*)if_ip6.addr, answer->flush, answer->bye) > 0) { + return 2; + } return 1; + } else if (answer->host != NULL) { + return _mdns_append_host_answer(packet, index, answer->host, ESP_IPADDR_TYPE_V6, answer->flush, answer->bye); } - mdns_if_t other_if = _mdns_get_other_if (tcpip_if); - if (esp_netif_get_ip6_linklocal(_mdns_get_esp_netif(other_if), &if_ip6)) { - return 1; - } - if (_mdns_append_aaaa_record(packet, index, (uint8_t*)if_ip6.addr, answer->flush, answer->bye) > 0) { - return 2; - } - return 1; } #endif return 0; @@ -1057,7 +1123,18 @@ static void _mdns_free_tx_packet(mdns_tx_packet_t * packet) if (!packet) { return; } - queueFree(mdns_out_question_t, packet->questions); + mdns_out_question_t * q = packet->questions; + while (q) { + mdns_out_question_t * next = q->next; + if (q->own_dynamic_memory) { + free((char *)q->host); + free((char *)q->service); + free((char *)q->proto); + free((char *)q->domain); + } + free(q); + q = next; + } queueFree(mdns_out_answer_t, packet->answers); queueFree(mdns_out_answer_t, packet->servers); queueFree(mdns_out_answer_t, packet->additional); @@ -1213,11 +1290,12 @@ static void _mdns_dealloc_answer(mdns_out_answer_t ** destination, uint16_t type /** * @brief Allocate new answer and add it to answer list (destination) */ -static bool _mdns_alloc_answer(mdns_out_answer_t ** destination, uint16_t type, mdns_service_t * service, bool flush, bool bye) +static bool _mdns_alloc_answer(mdns_out_answer_t ** destination, uint16_t type, mdns_service_t * service, + mdns_host_item_t * host, bool flush, bool bye) { mdns_out_answer_t * d = *destination; while (d) { - if (d->type == type && d->service == service) { + if (d->type == type && d->service == service && d->host == host) { return true; } d = d->next; @@ -1230,6 +1308,7 @@ static bool _mdns_alloc_answer(mdns_out_answer_t ** destination, uint16_t type, } a->type = type; a->service = service; + a->host = host; a->custom_service = NULL; a->bye = bye; a->flush = flush; @@ -1264,10 +1343,53 @@ static mdns_tx_packet_t * _mdns_alloc_packet_default(mdns_if_t tcpip_if, mdns_ip return packet; } +static bool _mdns_create_answer_from_service(mdns_tx_packet_t * packet, mdns_service_t * service, + mdns_parsed_question_t * question, bool shared, bool send_flush) +{ + mdns_host_item_t * host = mdns_get_host_item(service->hostname); + if (question->type == MDNS_TYPE_PTR || question->type == MDNS_TYPE_ANY) { + if (!_mdns_alloc_answer(&packet->answers, MDNS_TYPE_PTR, service, NULL, false, false) || + !_mdns_alloc_answer(&packet->answers, MDNS_TYPE_SRV, service, NULL, send_flush, false) || + !_mdns_alloc_answer(&packet->answers, MDNS_TYPE_TXT, service, NULL, send_flush, false) || + !_mdns_alloc_answer(shared ? &packet->additional : &packet->answers, MDNS_TYPE_A, service, host, send_flush, + false) || + !_mdns_alloc_answer(shared ? &packet->additional : &packet->answers, MDNS_TYPE_AAAA, service, host, + send_flush, false)) { + return false; + } + } else if (question->type == MDNS_TYPE_SRV) { + if (!_mdns_alloc_answer(&packet->answers, MDNS_TYPE_SRV, service, NULL, send_flush, false) || + !_mdns_alloc_answer(&packet->additional, MDNS_TYPE_A, service, host, send_flush, false) || + !_mdns_alloc_answer(&packet->additional, MDNS_TYPE_AAAA, service, host, send_flush, false)) { + return false; + } + } else if (question->type == MDNS_TYPE_TXT) { + if (!_mdns_alloc_answer(&packet->answers, MDNS_TYPE_TXT, service, NULL, send_flush, false)) { + return false; + } + } else if (question->type == MDNS_TYPE_SDPTR) { + shared = true; + if (!_mdns_alloc_answer(&packet->answers, MDNS_TYPE_SDPTR, service, NULL, false, false)) { + return false; + } + } + return true; +} + +static bool _mdns_create_answer_from_hostname(mdns_tx_packet_t * packet, const char * hostname, bool send_flush) +{ + mdns_host_item_t * host = mdns_get_host_item(hostname); + if (!_mdns_alloc_answer(&packet->answers, MDNS_TYPE_A, NULL, host, send_flush, false) || + !_mdns_alloc_answer(&packet->answers, MDNS_TYPE_AAAA, NULL, host, send_flush, false)) { + return false; + } + return true; +} + /** * @brief Create answer packet to questions from parsed packet */ -static void _mdns_create_answer_from_parsed_packet(mdns_parsed_packet_t * parsed_packet) +static void _mdns_create_answer_from_parsed_packet(mdns_parsed_packet_t *parsed_packet) { if (!parsed_packet->questions) { return; @@ -1275,7 +1397,7 @@ static void _mdns_create_answer_from_parsed_packet(mdns_parsed_packet_t * parsed bool send_flush = parsed_packet->src_port == MDNS_SERVICE_PORT; bool unicast = false; bool shared = false; - mdns_tx_packet_t * packet = _mdns_alloc_packet_default(parsed_packet->tcpip_if, parsed_packet->ip_protocol); + mdns_tx_packet_t *packet = _mdns_alloc_packet_default(parsed_packet->tcpip_if, parsed_packet->ip_protocol); if (!packet) { return; } @@ -1283,72 +1405,60 @@ static void _mdns_create_answer_from_parsed_packet(mdns_parsed_packet_t * parsed packet->distributed = parsed_packet->distributed; packet->id = parsed_packet->id; - mdns_parsed_question_t * q = parsed_packet->questions; + mdns_parsed_question_t *q = parsed_packet->questions; while (q) { - mdns_srv_item_t * service = NULL; + shared = q->type == MDNS_TYPE_PTR || q->type == MDNS_TYPE_SDPTR || !parsed_packet->probe; if (q->service && q->proto) { - service = _mdns_get_service_item(q->service, q->proto); - if (!service) { - continue; + mdns_srv_item_t *service = _mdns_server->services; + while (service) { + if (_mdns_service_match(service->service, q->service, q->proto, NULL)) { + if (!_mdns_create_answer_from_service(packet, service->service, q, shared, send_flush)) { + _mdns_free_tx_packet(packet); + return; + } + } + service = service->next; } - } - if (q->unicast) { - unicast = true; - } - if (service) { - if (q->type == MDNS_TYPE_PTR || q->type == MDNS_TYPE_ANY) { - if (q->type == MDNS_TYPE_PTR || !parsed_packet->probe) { - shared = true; - } - if (!_mdns_alloc_answer(&packet->answers, MDNS_TYPE_PTR, service->service, false, false) - || !_mdns_alloc_answer(&packet->answers, MDNS_TYPE_SRV, service->service, send_flush, false) - || !_mdns_alloc_answer(&packet->answers, MDNS_TYPE_TXT, service->service, send_flush, false) - || !_mdns_alloc_answer(shared?&packet->additional:&packet->answers, MDNS_TYPE_A, NULL, send_flush, false) - || !_mdns_alloc_answer(shared?&packet->additional:&packet->answers, MDNS_TYPE_AAAA, NULL, send_flush, false)) { - _mdns_free_tx_packet(packet); - return; - } - } else if (q->type == MDNS_TYPE_SRV) { - if (!_mdns_alloc_answer(&packet->answers, MDNS_TYPE_SRV, service->service, send_flush, false) - || !_mdns_alloc_answer(&packet->additional, MDNS_TYPE_A, NULL, send_flush, false) - || !_mdns_alloc_answer(&packet->additional, MDNS_TYPE_AAAA, NULL, send_flush, false)) { - _mdns_free_tx_packet(packet); - return; - } - } else if (q->type == MDNS_TYPE_TXT) { - if (!_mdns_alloc_answer(&packet->answers, MDNS_TYPE_TXT, service->service, send_flush, false)) { - _mdns_free_tx_packet(packet); - return; - } - } else if (q->type == MDNS_TYPE_SDPTR) { - shared = true; - if (!_mdns_alloc_answer(&packet->answers, MDNS_TYPE_SDPTR, service->service, false, false)) { - _mdns_free_tx_packet(packet); - return; - } - } - } else { - if (q->type == MDNS_TYPE_ANY || q->type == MDNS_TYPE_A || q->type == MDNS_TYPE_AAAA) { - if (!_mdns_alloc_answer(&packet->answers, MDNS_TYPE_A, NULL, send_flush, false) - || !_mdns_alloc_answer(&packet->answers, MDNS_TYPE_AAAA, NULL, send_flush, false)) { - _mdns_free_tx_packet(packet); - return; - } -#ifdef MDNS_REPEAT_QUERY_IN_RESPONSE - mdns_out_question_t * out_question = malloc(sizeof(mdns_out_question_t)); - if (out_question == NULL) { - HOOK_MALLOC_FAILED; - _mdns_free_tx_packet(packet); - return; - } - memcpy(out_question, q, sizeof(mdns_out_question_t)); - out_question->next = NULL; - queueToEnd(mdns_out_question_t, packet->questions, out_question); -#endif // MDNS_REPEAT_QUERY_IN_RESPONSE - } else if (!_mdns_alloc_answer(&packet->answers, q->type, NULL, send_flush, false)) { + } else if (q->type == MDNS_TYPE_A || q->type == MDNS_TYPE_AAAA) { + if (!_mdns_create_answer_from_hostname(packet, q->host, send_flush)) { _mdns_free_tx_packet(packet); return; } + } else if (q->type == MDNS_TYPE_ANY) { + if (!_mdns_append_host_list(&packet->answers, send_flush, false)) { + _mdns_free_tx_packet(packet); + return; + } + } else if (!_mdns_alloc_answer(&packet->answers, q->type, NULL, NULL, send_flush, false)) { + _mdns_free_tx_packet(packet); + return; + } + +#ifdef MDNS_REPEAT_QUERY_IN_RESPONSE + if (q->type == MDNS_TYPE_ANY || q->type == MDNS_TYPE_A || q->type == MDNS_TYPE_AAAA) { + mdns_out_question_t * out_question = malloc(sizeof(mdns_out_question_t)); + if (out_question == NULL) { + HOOK_MALLOC_FAILED; + _mdns_free_tx_packet(packet); + return; + } + out_question->type = q->type; + out_question->unicast = q->unicast; + out_question->host = q->host; + q->host = NULL; + out_question->service = q->service; + q->service = NULL; + out_question->proto = q->proto; + q->proto = NULL; + out_question->domain = q->domain; + q->domain = NULL; + out_question->next = NULL; + out_question->own_dynamic_memory = true; + queueToEnd(mdns_out_question_t, packet->questions, out_question); + } +#endif // MDNS_REPEAT_QUERY_IN_RESPONSE + if (q->unicast) { + unicast = true; } q = q->next; } @@ -1384,6 +1494,92 @@ static bool _mdns_question_exists(mdns_out_question_t * needle, mdns_out_questio return false; } +static bool _mdns_append_host(mdns_out_answer_t ** destination, mdns_host_item_t * host, bool flush, bool bye) +{ + if (!_mdns_alloc_answer(destination, MDNS_TYPE_A, NULL, host, flush, bye)) { + return false; + } + if (!_mdns_alloc_answer(destination, MDNS_TYPE_AAAA, NULL, host, flush, bye)) { + return false; + } + return true; +} + +static bool _mdns_append_host_list_in_services(mdns_out_answer_t ** destination, mdns_srv_item_t * services[], + size_t services_len, bool flush, bool bye) +{ + if (services == NULL) { + mdns_host_item_t * host = mdns_get_host_item(_mdns_server->hostname); + if (host != NULL) { + return _mdns_append_host(destination, host, flush, bye); + } + return true; + } + for (size_t i = 0; i < services_len; i++) { + mdns_host_item_t *host = mdns_get_host_item(services[i]->service->hostname); + if (!_mdns_append_host(destination, host, flush, bye)) { + return false; + } + } + return true; +} + +static bool _mdns_append_host_list(mdns_out_answer_t ** destination, bool flush, bool bye) +{ + if (!_str_null_or_empty(_mdns_server->hostname)) { + mdns_host_item_t * self_host = mdns_get_host_item(_mdns_server->hostname); + if (!_mdns_append_host(destination, self_host, flush, bye)) { + return false; + } + } + mdns_host_item_t * host = _mdns_host_list; + while (host != NULL) { + host = host->next; + if (!_mdns_append_host(destination, host, flush, bye)) { + return false; + } + } + return true; +} + +static bool _mdns_append_host_question(mdns_out_question_t **questions, const char *hostname, bool unicast) +{ + mdns_out_question_t *q = (mdns_out_question_t *)malloc(sizeof(mdns_out_question_t)); + if (!q) { + HOOK_MALLOC_FAILED; + return false; + } + q->next = NULL; + q->unicast = unicast; + q->type = MDNS_TYPE_ANY; + q->host = hostname; + q->service = NULL; + q->proto = NULL; + q->domain = MDNS_DEFAULT_DOMAIN; + q->own_dynamic_memory = false; + if (_mdns_question_exists(q, *questions)) { + free(q); + } else { + queueToEnd(mdns_out_question_t, *questions, q); + } + return true; +} + +static bool _mdns_append_host_questions_for_services(mdns_out_question_t **questions, mdns_srv_item_t *services[], + size_t len, bool unicast) +{ + if (!_str_null_or_empty(_mdns_server->hostname) && + !_mdns_append_host_question(questions, _mdns_server->hostname, unicast)) { + return false; + } + for (size_t i = 0; i < len; i++) { + if (!_mdns_append_host_question(questions, services[i]->service->hostname, unicast)) { + return false; + } + } + return true; +} + /** * @brief Create probe packet for particular services on particular PCB */ @@ -1409,6 +1605,7 @@ static mdns_tx_packet_t * _mdns_create_probe_packet(mdns_if_t tcpip_if, mdns_ip_ q->service = services[i]->service->service; q->proto = services[i]->service->proto; q->domain = MDNS_DEFAULT_DOMAIN; + q->own_dynamic_memory = false; if (!q->host || _mdns_question_exists(q, packet->questions)) { free(q); continue; @@ -1416,44 +1613,21 @@ static mdns_tx_packet_t * _mdns_create_probe_packet(mdns_if_t tcpip_if, mdns_ip_ queueToEnd(mdns_out_question_t, packet->questions, q); } - if (!q->host || !_mdns_alloc_answer(&packet->servers, MDNS_TYPE_SRV, services[i]->service, false, false)) { + if (!q->host || !_mdns_alloc_answer(&packet->servers, MDNS_TYPE_SRV, services[i]->service, NULL, false, false)) { _mdns_free_tx_packet(packet); return NULL; } } - if (include_ip && !_str_null_or_empty(_mdns_server->hostname)) { - mdns_out_question_t * q = (mdns_out_question_t *)malloc(sizeof(mdns_out_question_t)); - if (!q) { - HOOK_MALLOC_FAILED; + if (include_ip) { + if (!_mdns_append_host_questions_for_services(&packet->questions, services, len, first)) { _mdns_free_tx_packet(packet); return NULL; } - q->next = NULL; - q->unicast = first; - q->type = MDNS_TYPE_ANY; - q->host = _mdns_server->hostname; - q->service = NULL; - q->proto = NULL; - q->domain = MDNS_DEFAULT_DOMAIN; - if (_mdns_question_exists(q, packet->questions)) { - free(q); - } else { - queueToEnd(mdns_out_question_t, packet->questions, q); - } - if (_mdns_server->interfaces[tcpip_if].pcbs[MDNS_IP_PROTOCOL_V4].pcb) { - if (!_mdns_alloc_answer(&packet->servers, MDNS_TYPE_A, NULL, false, false)) { - _mdns_free_tx_packet(packet); - return NULL; - } - } - - if (_mdns_server->interfaces[tcpip_if].pcbs[MDNS_IP_PROTOCOL_V6].pcb) { - if (!_mdns_alloc_answer(&packet->servers, MDNS_TYPE_AAAA, NULL, false, false)) { - _mdns_free_tx_packet(packet); - return NULL; - } + if (!_mdns_append_host_list_in_services(&packet->servers, services, len, false, false)) { + _mdns_free_tx_packet(packet); + return NULL; } } @@ -1473,17 +1647,16 @@ static mdns_tx_packet_t * _mdns_create_announce_packet(mdns_if_t tcpip_if, mdns_ uint8_t i; for (i=0; ianswers, MDNS_TYPE_SDPTR, services[i]->service, false, false) - || !_mdns_alloc_answer(&packet->answers, MDNS_TYPE_PTR, services[i]->service, false, false) - || !_mdns_alloc_answer(&packet->answers, MDNS_TYPE_SRV, services[i]->service, true, false) - || !_mdns_alloc_answer(&packet->answers, MDNS_TYPE_TXT, services[i]->service, true, false)) { + if (!_mdns_alloc_answer(&packet->answers, MDNS_TYPE_SDPTR, services[i]->service, NULL, false, false) + || !_mdns_alloc_answer(&packet->answers, MDNS_TYPE_PTR, services[i]->service, NULL, false, false) + || !_mdns_alloc_answer(&packet->answers, MDNS_TYPE_SRV, services[i]->service, NULL, true, false) + || !_mdns_alloc_answer(&packet->answers, MDNS_TYPE_TXT, services[i]->service, NULL, true, false)) { _mdns_free_tx_packet(packet); return NULL; } } if (include_ip) { - if (!_mdns_alloc_answer(&packet->answers, MDNS_TYPE_A, NULL, true, false) - || !_mdns_alloc_answer(&packet->answers, MDNS_TYPE_AAAA, NULL, true, false)) { + if (!_mdns_append_host_list_in_services(&packet->servers, services, len, true, false)) { _mdns_free_tx_packet(packet); return NULL; } @@ -1506,16 +1679,16 @@ static mdns_tx_packet_t * _mdns_create_announce_from_probe(mdns_tx_packet_t * pr mdns_out_answer_t * s = probe->servers; while (s) { if (s->type == MDNS_TYPE_SRV) { - if (!_mdns_alloc_answer(&packet->answers, MDNS_TYPE_SDPTR, s->service, false, false) - || !_mdns_alloc_answer(&packet->answers, MDNS_TYPE_PTR, s->service, false, false) - || !_mdns_alloc_answer(&packet->answers, MDNS_TYPE_SRV, s->service, true, false) - || !_mdns_alloc_answer(&packet->answers, MDNS_TYPE_TXT, s->service, true, false)) { + if (!_mdns_alloc_answer(&packet->answers, MDNS_TYPE_SDPTR, s->service, NULL, false, false) + || !_mdns_alloc_answer(&packet->answers, MDNS_TYPE_PTR, s->service, NULL, false, false) + || !_mdns_alloc_answer(&packet->answers, MDNS_TYPE_SRV, s->service, NULL, true, false) + || !_mdns_alloc_answer(&packet->answers, MDNS_TYPE_TXT, s->service, NULL, true, false)) { _mdns_free_tx_packet(packet); return NULL; } } else if (s->type == MDNS_TYPE_A || s->type == MDNS_TYPE_AAAA) { - if (!_mdns_alloc_answer(&packet->answers, s->type, NULL, true, false)) { + if (!_mdns_alloc_answer(&packet->answers, s->type, NULL, s->host, true, false)) { _mdns_free_tx_packet(packet); return NULL; } @@ -1538,14 +1711,13 @@ static void _mdns_pcb_send_bye(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protoco packet->flags = MDNS_FLAGS_AUTHORITATIVE; size_t i; for (i=0; ianswers, MDNS_TYPE_PTR, services[i]->service, true, true)) { + if (!_mdns_alloc_answer(&packet->answers, MDNS_TYPE_PTR, services[i]->service, NULL, true, true)) { _mdns_free_tx_packet(packet); return; } } - if (include_ip && (!_mdns_alloc_answer(&packet->answers, MDNS_TYPE_A, NULL, true, true) || !_mdns_alloc_answer(&packet->answers, MDNS_TYPE_AAAA, NULL, true, true))) { - _mdns_free_tx_packet(packet); - return; + if (include_ip) { + _mdns_append_host_list_in_services(&packet->answers, services, len, true, true); } _mdns_dispatch_tx_packet(packet); _mdns_free_tx_packet(packet); @@ -1701,18 +1873,17 @@ static void _mdns_announce_pcb(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protoco mdns_tx_packet_t * p = _mdns_get_next_pcb_packet(tcpip_if, ip_protocol); if (p) { for (i=0; ianswers, MDNS_TYPE_SDPTR, services[i]->service, false, false) - || !_mdns_alloc_answer(&p->answers, MDNS_TYPE_PTR, services[i]->service, false, false) - || !_mdns_alloc_answer(&p->answers, MDNS_TYPE_SRV, services[i]->service, true, false) - || !_mdns_alloc_answer(&p->answers, MDNS_TYPE_TXT, services[i]->service, true, false)) { + if (!_mdns_alloc_answer(&p->answers, MDNS_TYPE_SDPTR, services[i]->service, NULL, false, false) + || !_mdns_alloc_answer(&p->answers, MDNS_TYPE_PTR, services[i]->service, NULL, false, false) + || !_mdns_alloc_answer(&p->answers, MDNS_TYPE_SRV, services[i]->service, NULL, true, false) + || !_mdns_alloc_answer(&p->answers, MDNS_TYPE_TXT, services[i]->service, NULL, true, false)) { break; } } if (include_ip) { _mdns_dealloc_answer(&p->additional, MDNS_TYPE_A, NULL); _mdns_dealloc_answer(&p->additional, MDNS_TYPE_AAAA, NULL); - _mdns_alloc_answer(&p->answers, MDNS_TYPE_A, NULL, true, false); - _mdns_alloc_answer(&p->answers, MDNS_TYPE_AAAA, NULL, true, false); + _mdns_append_host_list_in_services(&p->answers, services, len, true, false); } _pcb->state = PCB_ANNOUNCE_1; } @@ -1723,7 +1894,7 @@ static void _mdns_announce_pcb(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protoco } _pcb->state = PCB_ANNOUNCE_1; - mdns_tx_packet_t * p = _mdns_create_announce_packet(tcpip_if, ip_protocol, services, len, include_ip); + mdns_tx_packet_t * p = _mdns_create_announce_packet(tcpip_if, ip_protocol, services, len, include_ip); if (p) { _mdns_schedule_tx_packet(p, 0); } @@ -1930,7 +2101,9 @@ static void _mdns_free_linked_txt(mdns_txt_linked_item_t *txt) * * @return pointer to the service or NULL on error */ -static mdns_service_t * _mdns_create_service(const char * service, const char * proto, uint16_t port, const char * instance, size_t num_items, mdns_txt_item_t txt[]) +static mdns_service_t * _mdns_create_service(const char * service, const char * proto, const char * hostname, + uint16_t port, const char * instance, size_t num_items, + mdns_txt_item_t txt[]) { mdns_service_t * s = (mdns_service_t *)malloc(sizeof(mdns_service_t)); if (!s) { @@ -1950,6 +2123,16 @@ static mdns_service_t * _mdns_create_service(const char * service, const char * s->txt = new_txt; s->port = port; + if (hostname) { + s->hostname = strndup(hostname, MDNS_NAME_BUF_LEN - 1); + if (!s->hostname) { + free(s); + return NULL; + } + } else { + s->hostname = NULL; + } + s->service = strndup(service, MDNS_NAME_BUF_LEN - 1); if (!s->service) { free(s); @@ -2090,6 +2273,7 @@ static void _mdns_free_service(mdns_service_t * service) free((char *)service->instance); free((char *)service->service); free((char *)service->proto); + free((char *)service->hostname); while (service->txt) { mdns_txt_linked_item_t * s = service->txt; service->txt = service->txt->next; @@ -2310,6 +2494,116 @@ static int _mdns_check_aaaa_collision(esp_ip6_addr_t * ip, mdns_if_t tcpip_if) } #endif +static bool _hostname_is_ours(const char * hostname) +{ + if (strcasecmp(hostname, _mdns_server->hostname) == 0) { + return true; + } + mdns_host_item_t * host = _mdns_host_list; + while (host != NULL) { + if (strcasecmp(hostname, host->hostname) == 0) { + return true; + } + host = host->next; + } + return false; +} + +static bool _mdns_delegate_hostname_add(const char * hostname, mdns_ip_addr_t * address_list) +{ + if (_hostname_is_ours(hostname)) { + return true; + } + + mdns_host_item_t * host = (mdns_host_item_t *)malloc(sizeof(mdns_host_item_t)); + + if (host == NULL) { + return false; + } + host->address_list = address_list; + host->hostname = hostname; + host->next = _mdns_host_list; + _mdns_host_list = host; + return true; +} + +static void free_address_list(mdns_ip_addr_t * address_list) +{ + while (address_list != NULL) { + mdns_ip_addr_t * next = address_list->next; + free(address_list); + address_list = next; + } +} + +static mdns_ip_addr_t * copy_address_list(const mdns_ip_addr_t * address_list) +{ + mdns_ip_addr_t * head = NULL; + mdns_ip_addr_t * tail = NULL; + while (address_list != NULL) { + mdns_ip_addr_t * addr = (mdns_ip_addr_t *)malloc(sizeof(mdns_ip_addr_t)); + if (addr == NULL) { + free_address_list(head); + return NULL; + } + addr->addr = address_list->addr; + addr->next = NULL; + if (head == NULL) { + head = addr; + tail = addr; + } else { + tail->next = addr; + tail = tail->next; + } + address_list = address_list->next; + } + return head; +} + +static bool _mdns_delegate_hostname_remove(const char * hostname) +{ + mdns_srv_item_t * srv = _mdns_server->services; + mdns_srv_item_t * prev_srv = NULL; + while (srv) { + if (strcasecmp(srv->service->hostname, hostname) == 0) { + mdns_srv_item_t * to_free = srv; + _mdns_send_bye(&srv, 1, false); + _mdns_remove_scheduled_service_packets(srv->service); + if (prev_srv == NULL) { + _mdns_server->services = srv->next; + srv = srv->next; + } else { + prev_srv->next = srv->next; + srv = srv->next; + } + _mdns_free_service(to_free->service); + free(to_free); + } else { + prev_srv = srv; + srv = srv->next; + } + } + mdns_host_item_t * host = _mdns_host_list; + mdns_host_item_t * prev_host = NULL; + while (host != NULL) { + if (strcasecmp(hostname, host->hostname) == 0) { + if (prev_host == NULL) { + _mdns_host_list = host->next; + } else { + prev_host->next = host->next; + } + free_address_list(host->address_list); + free((char *)host->hostname); + free(host); + break; + } else { + prev_host = host; + host = host->next; + } + } + return true; +} + /** * @brief Check if parsed name is discovery */ @@ -2338,7 +2632,7 @@ static bool _mdns_name_is_ours(mdns_name_t * name) if (_str_null_or_empty(name->service) && _str_null_or_empty(name->proto)) { if (!_str_null_or_empty(name->host) && !_str_null_or_empty(_mdns_server->hostname) - && strcasecmp(name->host, _mdns_server->hostname) == 0) + && _hostname_is_ours(name->host)) { return true; } @@ -2351,7 +2645,7 @@ static bool _mdns_name_is_ours(mdns_name_t * name) } //find the service - mdns_srv_item_t * service = _mdns_get_service_item(name->service, name->proto); + mdns_srv_item_t * service = _mdns_get_service_item(name->service, name->proto, NULL); if (!service) { return false; } @@ -2829,7 +3123,7 @@ void mdns_parse_packet(mdns_rx_packet_t * packet) } else if (!name->sub && _mdns_name_is_ours(name)) { ours = true; if (name->service && name->service[0] && name->proto && name->proto[0]) { - service = _mdns_get_service_item(name->service, name->proto); + service = _mdns_get_service_item(name->service, name->proto, NULL); } } else { if (!parsed_packet->authoritative || record_type == MDNS_NS) { @@ -2847,7 +3141,7 @@ void mdns_parse_packet(mdns_rx_packet_t * packet) _mdns_search_result_add_ptr(search_result, name->host, packet->tcpip_if, packet->ip_protocol); } else if ((discovery || ours) && !name->sub && _mdns_name_is_ours(name)) { if (discovery) { - service = _mdns_get_service_item(name->service, name->proto); + service = _mdns_get_service_item(name->service, name->proto, NULL); _mdns_remove_parsed_question(parsed_packet, MDNS_TYPE_SDPTR, service); } else if (parsed_packet->questions && !parsed_packet->probe) { _mdns_remove_parsed_question(parsed_packet, type, service); @@ -2933,8 +3227,10 @@ void mdns_parse_packet(mdns_rx_packet_t * packet) } else { char * new_host = _mdns_mangle_name((char *)_mdns_server->hostname); if (new_host) { + _mdns_remap_self_service_hostname(_mdns_server->hostname, new_host); free((char *)_mdns_server->hostname); _mdns_server->hostname = new_host; + _mdns_self_host.hostname = new_host; } _mdns_restart_all_pcbs(); } @@ -3039,8 +3335,10 @@ void mdns_parse_packet(mdns_rx_packet_t * packet) _mdns_server->interfaces[packet->tcpip_if].pcbs[packet->ip_protocol].failed_probes++; char * new_host = _mdns_mangle_name((char *)_mdns_server->hostname); if (new_host) { + _mdns_remap_self_service_hostname(_mdns_server->hostname, new_host); free((char *)_mdns_server->hostname); _mdns_server->hostname = new_host; + _mdns_self_host.hostname = new_host; } _mdns_restart_all_pcbs(); } @@ -3087,8 +3385,10 @@ void mdns_parse_packet(mdns_rx_packet_t * packet) _mdns_server->interfaces[packet->tcpip_if].pcbs[packet->ip_protocol].failed_probes++; char * new_host = _mdns_mangle_name((char *)_mdns_server->hostname); if (new_host) { + _mdns_remap_self_service_hostname(_mdns_server->hostname, new_host); free((char *)_mdns_server->hostname); _mdns_server->hostname = new_host; + _mdns_self_host.hostname = new_host; } _mdns_restart_all_pcbs(); } @@ -3117,10 +3417,18 @@ clear_rx_packet: while (parsed_packet->questions) { mdns_parsed_question_t * question = parsed_packet->questions; parsed_packet->questions = parsed_packet->questions->next; - free(question->host); - free(question->service); - free(question->proto); - free(question->domain); + if (question->host) { + free(question->host); + } + if (question->service) { + free(question->service); + } + if (question->proto) { + free(question->proto); + } + if (question->domain) { + free(question->domain); + } free(question); } free(parsed_packet); @@ -3650,6 +3958,7 @@ static mdns_tx_packet_t * _mdns_create_search_packet(mdns_search_once_t * search q->service = search->service; q->proto = search->proto; q->domain = MDNS_DEFAULT_DOMAIN; + q->own_dynamic_memory = false; queueToEnd(mdns_out_question_t, packet->questions, q); if (search->type == MDNS_TYPE_PTR) { @@ -3784,6 +4093,19 @@ static void _mdns_tx_handle_packet(mdns_tx_packet_t * p) } } +static void _mdns_remap_self_service_hostname(const char * old_hostname, const char * new_hostname) +{ + mdns_srv_item_t * service = _mdns_server->services; + + while (service) { + if (strcmp(service->service->hostname, old_hostname) == 0) { + free((char *)service->service->hostname); + service->service->hostname = strdup(new_hostname); + } + service = service->next; + } +} + /** * @brief Free action data */ @@ -3791,7 +4113,7 @@ static void _mdns_free_action(mdns_action_t * action) { switch(action->type) { case ACTION_HOSTNAME_SET: - free(action->data.hostname); + free(action->data.hostname_set.hostname); break; case ACTION_INSTANCE_SET: free(action->data.instance); @@ -3851,10 +4173,12 @@ static void _mdns_execute_action(mdns_action_t * action) break; case ACTION_HOSTNAME_SET: _mdns_send_bye_all_pcbs_no_instance(true); + _mdns_remap_self_service_hostname(_mdns_server->hostname, action->data.hostname_set.hostname); free((char*)_mdns_server->hostname); - _mdns_server->hostname = action->data.hostname; + _mdns_server->hostname = action->data.hostname_set.hostname; + _mdns_self_host.hostname = action->data.hostname_set.hostname; _mdns_restart_all_pcbs(); - + xTaskNotifyGive(action->data.hostname_set.calling_task); break; case ACTION_INSTANCE_SET: _mdns_send_bye_all_pcbs_no_instance(false); @@ -3867,7 +4191,6 @@ static void _mdns_execute_action(mdns_action_t * action) action->data.srv_add.service->next = _mdns_server->services; _mdns_server->services = action->data.srv_add.service; _mdns_probe_all_pcbs(&action->data.srv_add.service, 1, false, false); - break; case ACTION_SERVICE_INSTANCE_SET: if (action->data.srv_instance.service->service->instance) { @@ -4018,6 +4341,14 @@ static void _mdns_execute_action(mdns_action_t * action) pbuf_free(action->data.rx_handle.packet->pb); free(action->data.rx_handle.packet); break; + case ACTION_DELEGATE_HOSTNAME_ADD: + _mdns_delegate_hostname_add(action->data.delegate_hostname.hostname, + action->data.delegate_hostname.address_list); + break; + case ACTION_DELEGATE_HOSTNAME_REMOVE: + _mdns_delegate_hostname_remove(action->data.delegate_hostname.hostname); + free((char *)action->data.delegate_hostname.hostname); + break; default: break; } @@ -4421,7 +4752,39 @@ esp_err_t mdns_hostname_set(const char * hostname) return ESP_ERR_NO_MEM; } action->type = ACTION_HOSTNAME_SET; - action->data.hostname = new_hostname; + action->data.hostname_set.hostname = new_hostname; + action->data.hostname_set.calling_task = xTaskGetCurrentTaskHandle(); + if (xQueueSend(_mdns_server->action_queue, &action, (portTickType)0) != pdPASS) { + free(new_hostname); + free(action); + return ESP_ERR_NO_MEM; + } + xTaskNotifyWait(0, 0, NULL, portMAX_DELAY); + return ERR_OK; +} + +esp_err_t mdns_delegate_hostname_add(const char * hostname, const mdns_ip_addr_t * address_list) +{ + if (!_mdns_server) { + return ESP_ERR_INVALID_STATE; + } + if (_str_null_or_empty(hostname) || strlen(hostname) > (MDNS_NAME_BUF_LEN - 1) || address_list == NULL) { + return ESP_ERR_INVALID_ARG; + } + char * new_hostname = strndup(hostname, MDNS_NAME_BUF_LEN - 1); + if (!new_hostname) { + return ESP_ERR_NO_MEM; + } + + mdns_action_t * action = (mdns_action_t *)malloc(sizeof(mdns_action_t)); + if (!action) { + HOOK_MALLOC_FAILED; + free(new_hostname); + return ESP_ERR_NO_MEM; + } + action->type = ACTION_DELEGATE_HOSTNAME_ADD; + action->data.delegate_hostname.hostname = new_hostname; + action->data.delegate_hostname.address_list = copy_address_list(address_list); if (xQueueSend(_mdns_server->action_queue, &action, (portTickType)0) != pdPASS) { free(new_hostname); free(action); @@ -4430,10 +4793,44 @@ esp_err_t mdns_hostname_set(const char * hostname) return ERR_OK; } +esp_err_t mdns_delegate_hostname_remove(const char * hostname) +{ + if (!_mdns_server) { + return ESP_ERR_INVALID_STATE; + } + if (_str_null_or_empty(hostname) || strlen(hostname) > (MDNS_NAME_BUF_LEN - 1)) { + return ESP_ERR_INVALID_ARG; + } + char * new_hostname = strndup(hostname, MDNS_NAME_BUF_LEN - 1); + if (!new_hostname) { + return ESP_ERR_NO_MEM; + } + + mdns_action_t * action = (mdns_action_t *)malloc(sizeof(mdns_action_t)); + if (!action) { + HOOK_MALLOC_FAILED; + free(new_hostname); + return ESP_ERR_NO_MEM; + } + action->type = ACTION_DELEGATE_HOSTNAME_REMOVE; + action->data.delegate_hostname.hostname = new_hostname; + if (xQueueSend(_mdns_server->action_queue, &action, (portTickType)0) != pdPASS) { + free(new_hostname); + free(action); + return ESP_ERR_NO_MEM; + } + return ERR_OK; +} + +bool mdns_hostname_exists(const char * hostname) +{ + return _hostname_is_ours(hostname); +} + esp_err_t mdns_instance_name_set(const char * instance) { if (!_mdns_server) { - return ESP_ERR_INVALID_ARG; + return ESP_ERR_INVALID_STATE; } if (_str_null_or_empty(instance) || strlen(instance) > (MDNS_NAME_BUF_LEN - 1)) { return ESP_ERR_INVALID_ARG; @@ -4463,9 +4860,11 @@ esp_err_t mdns_instance_name_set(const char * instance) * MDNS SERVICES * */ -esp_err_t mdns_service_add(const char * instance, const char * service, const char * proto, uint16_t port, mdns_txt_item_t txt[], size_t num_items) +esp_err_t mdns_service_add_for_host(const char * instance, const char * service, const char * proto, + const char * hostname, uint16_t port, mdns_txt_item_t txt[], size_t num_items) { - if (!_mdns_server || _str_null_or_empty(service) || _str_null_or_empty(proto) || !port) { + if (!_mdns_server || _str_null_or_empty(service) || _str_null_or_empty(proto) || _str_null_or_empty(hostname) || + !port) { return ESP_ERR_INVALID_ARG; } @@ -4473,12 +4872,12 @@ esp_err_t mdns_service_add(const char * instance, const char * service, const ch return ESP_ERR_NO_MEM; } - mdns_srv_item_t * item = _mdns_get_service_item(service, proto); + mdns_srv_item_t * item = _mdns_get_service_item(service, proto, hostname); if (item) { return ESP_ERR_INVALID_ARG; } - mdns_service_t * s = _mdns_create_service(service, proto, port, instance, num_items, txt); + mdns_service_t * s = _mdns_create_service(service, proto, hostname, port, instance, num_items, txt); if (!s) { return ESP_ERR_NO_MEM; } @@ -4511,8 +4910,7 @@ esp_err_t mdns_service_add(const char * instance, const char * service, const ch size_t start = xTaskGetTickCount(); size_t timeout_ticks = pdMS_TO_TICKS(MDNS_SERVICE_ADD_TIMEOUT_MS); - while (_mdns_get_service_item(service, proto) == NULL) - { + while (_mdns_get_service_item(service, proto, hostname) == NULL) { uint32_t expired = xTaskGetTickCount() - start; if (expired >= timeout_ticks) { return ESP_FAIL; // Timeout @@ -4523,12 +4921,26 @@ esp_err_t mdns_service_add(const char * instance, const char * service, const ch return ESP_OK; } -esp_err_t mdns_service_port_set(const char * service, const char * proto, uint16_t port) +esp_err_t mdns_service_add(const char * instance, const char * service, const char * proto, uint16_t port, + mdns_txt_item_t txt[], size_t num_items) +{ + if (!_mdns_server || _str_null_or_empty(_mdns_server->hostname)) { + return ESP_ERR_INVALID_STATE; + } + return mdns_service_add_for_host(instance, service, proto, _mdns_server->hostname, port, txt, num_items); +} + +bool mdns_service_exists(const char * service_type, const char * proto, const char * hostname) +{ + return _mdns_get_service_item(service_type, proto, hostname) != NULL; +} + +esp_err_t mdns_service_port_set_for_host(const char * service, const char * proto, const char * hostname, uint16_t port) { if (!_mdns_server || !_mdns_server->services || _str_null_or_empty(service) || _str_null_or_empty(proto) || !port) { return ESP_ERR_INVALID_ARG; } - mdns_srv_item_t * s = _mdns_get_service_item(service, proto); + mdns_srv_item_t * s = _mdns_get_service_item(service, proto, hostname); if (!s) { return ESP_ERR_NOT_FOUND; } @@ -4548,12 +4960,21 @@ esp_err_t mdns_service_port_set(const char * service, const char * proto, uint16 return ESP_OK; } -esp_err_t mdns_service_txt_set(const char * service, const char * proto, mdns_txt_item_t txt[], uint8_t num_items) +esp_err_t mdns_service_port_set(const char * service, const char * proto, uint16_t port) +{ + if (!_mdns_server) { + return ESP_ERR_INVALID_STATE; + } + return mdns_service_port_set_for_host(service, proto, _mdns_server->hostname, port); +} + +esp_err_t mdns_service_txt_set_for_host(const char * service, const char * proto, const char * hostname, + mdns_txt_item_t txt[], uint8_t num_items) { if (!_mdns_server || !_mdns_server->services || _str_null_or_empty(service) || _str_null_or_empty(proto) || (num_items && txt == NULL)) { return ESP_ERR_INVALID_ARG; } - mdns_srv_item_t * s = _mdns_get_service_item(service, proto); + mdns_srv_item_t * s = _mdns_get_service_item(service, proto, hostname); if (!s) { return ESP_ERR_NOT_FOUND; } @@ -4584,13 +5005,21 @@ esp_err_t mdns_service_txt_set(const char * service, const char * proto, mdns_tx return ESP_OK; } +esp_err_t mdns_service_txt_set(const char * service, const char * proto, mdns_txt_item_t txt[], uint8_t num_items) +{ + if (!_mdns_server) { + return ESP_ERR_INVALID_STATE; + } + return mdns_service_txt_set_for_host(service, proto, _mdns_server->hostname, txt, num_items); +} -esp_err_t mdns_service_txt_item_set(const char * service, const char * proto, const char * key, const char * value) +esp_err_t mdns_service_txt_item_set_for_host(const char * service, const char * proto, const char * hostname, + const char * key, const char * value) { if (!_mdns_server || !_mdns_server->services || _str_null_or_empty(service) || _str_null_or_empty(proto) || _str_null_or_empty(key) || !value) { return ESP_ERR_INVALID_ARG; } - mdns_srv_item_t * s = _mdns_get_service_item(service, proto); + mdns_srv_item_t * s = _mdns_get_service_item(service, proto, hostname); if (!s) { return ESP_ERR_NOT_FOUND; } @@ -4622,12 +5051,21 @@ esp_err_t mdns_service_txt_item_set(const char * service, const char * proto, co return ESP_OK; } -esp_err_t mdns_service_txt_item_remove(const char * service, const char * proto, const char * key) +esp_err_t mdns_service_txt_item_set(const char * service, const char * proto, const char * key, const char * value) +{ + if (!_mdns_server) { + return ESP_ERR_INVALID_STATE; + } + return mdns_service_txt_item_set_for_host(service, proto, _mdns_server->hostname, key, value); +} + +esp_err_t mdns_service_txt_item_remove_for_host(const char * service, const char * proto, const char * hostname, + const char * key) { if (!_mdns_server || !_mdns_server->services || _str_null_or_empty(service) || _str_null_or_empty(proto) || _str_null_or_empty(key)) { return ESP_ERR_INVALID_ARG; } - mdns_srv_item_t * s = _mdns_get_service_item(service, proto); + mdns_srv_item_t * s = _mdns_get_service_item(service, proto, hostname); if (!s) { return ESP_ERR_NOT_FOUND; } @@ -4652,7 +5090,16 @@ esp_err_t mdns_service_txt_item_remove(const char * service, const char * proto, return ESP_OK; } -esp_err_t mdns_service_instance_name_set(const char * service, const char * proto, const char * instance) +esp_err_t mdns_service_txt_item_remove(const char * service, const char * proto, const char * key) +{ + if (!_mdns_server) { + return ESP_ERR_INVALID_STATE; + } + return mdns_service_txt_item_remove_for_host(service, proto, _mdns_server->hostname, key); +} + +esp_err_t mdns_service_instance_name_set_for_host(const char * service, const char * proto, const char * hostname, + const char * instance) { if (!_mdns_server || !_mdns_server->services || _str_null_or_empty(service) || _str_null_or_empty(proto)) { return ESP_ERR_INVALID_ARG; @@ -4660,7 +5107,7 @@ esp_err_t mdns_service_instance_name_set(const char * service, const char * prot if (_str_null_or_empty(instance) || strlen(instance) > (MDNS_NAME_BUF_LEN - 1)) { return ESP_ERR_INVALID_ARG; } - mdns_srv_item_t * s = _mdns_get_service_item(service, proto); + mdns_srv_item_t * s = _mdns_get_service_item(service, proto, hostname); if (!s) { return ESP_ERR_NOT_FOUND; } @@ -4686,12 +5133,20 @@ esp_err_t mdns_service_instance_name_set(const char * service, const char * prot return ESP_OK; } -esp_err_t mdns_service_remove(const char * service, const char * proto) +esp_err_t mdns_service_instance_name_set(const char * service, const char * proto, const char * instance) +{ + if (!_mdns_server) { + return ESP_ERR_INVALID_STATE; + } + return mdns_service_instance_name_set_for_host(service, proto, _mdns_server->hostname, instance); +} + +esp_err_t mdns_service_remove_for_host(const char * service, const char * proto, const char * hostname) { if (!_mdns_server || !_mdns_server->services || _str_null_or_empty(service) || _str_null_or_empty(proto)) { return ESP_ERR_INVALID_ARG; } - mdns_srv_item_t * s = _mdns_get_service_item(service, proto); + mdns_srv_item_t * s = _mdns_get_service_item(service, proto, hostname); if (!s) { return ESP_ERR_NOT_FOUND; } @@ -4710,6 +5165,14 @@ esp_err_t mdns_service_remove(const char * service, const char * proto) return ESP_OK; } +esp_err_t mdns_service_remove(const char * service_type, const char * proto) +{ + if (!_mdns_server) { + return ESP_ERR_INVALID_STATE; + } + return mdns_service_remove_for_host(service_type, proto, _mdns_server->hostname); +} + esp_err_t mdns_service_remove_all(void) { if (!_mdns_server) { @@ -4936,7 +5399,7 @@ void mdns_debug_packet(const uint8_t * data, size_t len) header.answers = 0; header.additional = 0; header.servers = 0; - _mdns_dbg_printf("ERROR: %s:%u\n", __FILE__, __LINE__); + _mdns_dbg_printf("ERROR: parse header questions\n"); break; } @@ -4984,7 +5447,7 @@ void mdns_debug_packet(const uint8_t * data, size_t len) content = _mdns_parse_fqdn(data, content, name); if (!content) { - _mdns_dbg_printf("ERROR: %s:%u\n", __FILE__, __LINE__); + _mdns_dbg_printf("ERROR: parse mdns records\n"); break; } @@ -4998,7 +5461,7 @@ void mdns_debug_packet(const uint8_t * data, size_t len) content = data_ptr + data_len; if (content > (data + len)) { - _mdns_dbg_printf("ERROR: %s:%u\n", __FILE__, __LINE__); + _mdns_dbg_printf("ERROR: content length overflow\n"); break; } @@ -5051,13 +5514,13 @@ void mdns_debug_packet(const uint8_t * data, size_t len) _mdns_dbg_printf("[%u] ", data_len); if (type == MDNS_TYPE_PTR) { if (!_mdns_parse_fqdn(data, data_ptr, name)) { - _mdns_dbg_printf("ERROR: %s:%u\n", __FILE__, __LINE__); + _mdns_dbg_printf("ERROR: parse PTR\n"); continue; } _mdns_dbg_printf("%s.%s.%s.%s.\n", name->host, name->service, name->proto, name->domain); } else if (type == MDNS_TYPE_SRV) { if (!_mdns_parse_fqdn(data, data_ptr + MDNS_SRV_FQDN_OFFSET, name)) { - _mdns_dbg_printf("ERROR: %s:%u\n", __FILE__, __LINE__); + _mdns_dbg_printf("ERROR: parse SRV\n"); continue; } uint16_t priority = _mdns_read_u16(data_ptr, MDNS_SRV_PRIORITY_OFFSET); @@ -5069,7 +5532,7 @@ void mdns_debug_packet(const uint8_t * data, size_t len) while (i < data_len) { uint8_t partLen = data_ptr[i++]; if ((i+partLen) > data_len) { - _mdns_dbg_printf("ERROR: %s:%u\n", __FILE__, __LINE__); + _mdns_dbg_printf("ERROR: parse TXT\n"); break; } char txt[partLen+1]; diff --git a/components/mdns/private_include/mdns_private.h b/components/mdns/private_include/mdns_private.h index 9ed0dcfbd1..3851c4694c 100644 --- a/components/mdns/private_include/mdns_private.h +++ b/components/mdns/private_include/mdns_private.h @@ -17,6 +17,9 @@ #include "esp_event_base.h" #include "esp_task.h" #include "esp_timer.h" +#include "esp_netif_ip_addr.h" +#include "freertos/FreeRTOS.h" +#include "mdns.h" //#define MDNS_ENABLE_DEBUG @@ -182,6 +185,8 @@ typedef enum { ACTION_TX_HANDLE, ACTION_RX_HANDLE, ACTION_TASK_STOP, + ACTION_DELEGATE_HOSTNAME_ADD, + ACTION_DELEGATE_HOSTNAME_REMOVE, ACTION_MAX } mdns_action_type_t; @@ -210,7 +215,7 @@ typedef struct { } mdns_header_t; typedef struct { - char host[MDNS_NAME_BUF_LEN]; + char host[MDNS_NAME_BUF_LEN]; // hostname for A/AAAA records, instance name for SRV records char service[MDNS_NAME_BUF_LEN]; char proto[MDNS_NAME_BUF_LEN]; char domain[MDNS_NAME_BUF_LEN]; @@ -280,6 +285,7 @@ typedef struct { const char * instance; const char * service; const char * proto; + const char * hostname; uint16_t priority; uint16_t weight; uint16_t port; @@ -299,14 +305,22 @@ typedef struct mdns_out_question_s { const char * service; const char * proto; const char * domain; + bool own_dynamic_memory; } mdns_out_question_t; +typedef struct mdns_host_item_t { + const char * hostname; + mdns_ip_addr_t *address_list; + struct mdns_host_item_t *next; +} mdns_host_item_t; + typedef struct mdns_out_answer_s { struct mdns_out_answer_s * next; uint16_t type; uint8_t bye; uint8_t flush; mdns_service_t * service; + mdns_host_item_t* host; const char * custom_instance; const char * custom_service; const char * custom_proto; @@ -380,7 +394,10 @@ typedef struct mdns_server_s { typedef struct { mdns_action_type_t type; union { - char * hostname; + struct { + char * hostname; + xTaskHandle calling_task; + } hostname_set; char * instance; struct { esp_event_base_t event_base; @@ -423,6 +440,10 @@ typedef struct { struct { mdns_rx_packet_t * packet; } rx_handle; + struct { + const char * hostname; + mdns_ip_addr_t *address_list; + } delegate_hostname; } data; } mdns_action_t; diff --git a/components/mdns/test/test_mdns.c b/components/mdns/test/test_mdns.c index 9dc9fe090a..abdad0e63e 100644 --- a/components/mdns/test/test_mdns.c +++ b/components/mdns/test/test_mdns.c @@ -4,6 +4,7 @@ #define MDNS_HOSTNAME "test-hostname" +#define MDNS_DELEGATE_HOSTNAME "delegate-hostname" #define MDNS_INSTANCE "test-instance" #define MDNS_SERVICE_NAME "_http" #define MDNS_SERVICE_PROTO "_tcp" @@ -42,6 +43,10 @@ TEST_CASE("mdns api return expected err-code and do not leak memory", "[mdns][le { mdns_txt_item_t serviceTxtData[CONFIG_MDNS_MAX_SERVICES] = { {NULL, NULL}, }; + mdns_ip_addr_t addr; + addr.addr.type = ESP_IPADDR_TYPE_V4; + addr.addr.u_addr.ip4.addr = esp_ip4addr_aton("127.0.0.1"); + addr.next = NULL; for (int i=0; i #include "freertos/FreeRTOS.h" #include "freertos/task.h" +#include "esp_netif_ip_addr.h" #include "esp_system.h" #include "esp_event.h" #include "esp_log.h" @@ -23,8 +24,8 @@ #define EXAMPLE_MDNS_INSTANCE CONFIG_MDNS_INSTANCE #define EXAMPLE_BUTTON_GPIO 0 -static const char *TAG = "mdns-test"; -static char* generate_hostname(void); +static const char * TAG = "mdns-test"; +static char * generate_hostname(void); #if CONFIG_MDNS_RESOLVE_TEST_SERVICES == 1 static void query_mdns_host_with_gethostbyname(char * host); @@ -33,7 +34,8 @@ static void query_mdns_host_with_getaddrinfo(char * host); static void initialise_mdns(void) { - char* hostname = generate_hostname(); + char * hostname = generate_hostname(); + //initialize mDNS ESP_ERROR_CHECK( mdns_init() ); //set mDNS hostname (required if you want to advertise services) @@ -44,13 +46,32 @@ static void initialise_mdns(void) //structure with TXT records mdns_txt_item_t serviceTxtData[3] = { - {"board","esp32"}, - {"u","user"}, - {"p","password"} + {"board", "esp32"}, + {"u", "user"}, + {"p", "password"} }; //initialize service ESP_ERROR_CHECK( mdns_service_add("ESP32-WebServer", "_http", "_tcp", 80, serviceTxtData, 3) ); + +#if CONFIG_MDNS_PUBLISH_DELEGATE_HOST + char *delegated_hostname; + if (-1 == asprintf(&delegated_hostname, "%s-delegated", hostname)) { + abort(); + } + + mdns_ip_addr_t addr4, addr6; + esp_netif_str_to_ip4("10.0.0.1", &addr4.addr.u_addr.ip4); + addr4.addr.type = ESP_IPADDR_TYPE_V4; + esp_netif_str_to_ip6("fd11:22::1", &addr6.addr.u_addr.ip6); + addr6.addr.type = ESP_IPADDR_TYPE_V6; + addr4.next = &addr6; + addr6.next = NULL; + ESP_ERROR_CHECK( mdns_delegate_hostname_add(delegated_hostname, &addr4) ); + ESP_ERROR_CHECK( mdns_service_add_for_host("test0", "_http", "_tcp", delegated_hostname, 1234, serviceTxtData, 3) ); + free(delegated_hostname); +#endif // CONFIG_MDNS_PUBLISH_DELEGATE_HOST + //add another TXT item ESP_ERROR_CHECK( mdns_service_txt_item_set("_http", "_tcp", "path", "/foobar") ); //change TXT item value @@ -174,7 +195,7 @@ static void mdns_example_task(void *pvParameters) query_mdns_host_with_getaddrinfo("tinytester-lwip.local"); #endif - while(1) { + while (1) { check_button(); vTaskDelay(50 / portTICK_PERIOD_MS); } diff --git a/examples/protocols/mdns/mdns_example_test.py b/examples/protocols/mdns/mdns_example_test.py index 201acb569c..5bbae2111d 100644 --- a/examples/protocols/mdns/mdns_example_test.py +++ b/examples/protocols/mdns/mdns_example_test.py @@ -1,5 +1,6 @@ import os import re +import select import socket import struct import subprocess @@ -10,15 +11,17 @@ import dpkt import dpkt.dns import ttfw_idf from tiny_test_fw import DUT +from tiny_test_fw.Utility import console_log stop_mdns_server = Event() esp_answered = Event() +esp_delegated_answered = Event() def get_dns_query_for_esp(esp_host): dns = dpkt.dns.DNS(b'\x00\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x01') dns.qd[0].name = esp_host + u'.local' - print('Created query for esp host: {} '.format(dns.__repr__())) + console_log('Created query for esp host: {} '.format(dns.__repr__())) return dns.pack() @@ -32,7 +35,7 @@ def get_dns_answer_to_mdns(tester_host): arr.name = tester_host arr.ip = socket.inet_aton('127.0.0.1') dns. an.append(arr) - print('Created answer to mdns query: {} '.format(dns.__repr__())) + console_log('Created answer to mdns query: {} '.format(dns.__repr__())) return dns.pack() @@ -56,31 +59,42 @@ def mdns_server(esp_host): MCAST_GRP = '224.0.0.251' TESTER_NAME = u'tinytester.local' TESTER_NAME_LWIP = u'tinytester-lwip.local' + QUERY_TIMEOUT = 0.2 sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) + sock.setblocking(False) sock.bind((UDP_IP,UDP_PORT)) mreq = struct.pack('4sl', socket.inet_aton(MCAST_GRP), socket.INADDR_ANY) sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) - sock.settimeout(30) + last_query_timepoint = time.time() while not stop_mdns_server.is_set(): try: - if not esp_answered.is_set(): + current_time = time.time() + if not esp_answered.is_set() and current_time - last_query_timepoint > QUERY_TIMEOUT: sock.sendto(get_dns_query_for_esp(esp_host), (MCAST_GRP,UDP_PORT)) - time.sleep(0.2) + sock.sendto(get_dns_query_for_esp(esp_host + '-delegated'), (MCAST_GRP,UDP_PORT)) + last_query_timepoint = current_time + timeout = max(0, QUERY_TIMEOUT - (current_time - last_query_timepoint)) + read_socks, _, _ = select.select([sock], [], [], timeout) + if not read_socks: + continue data, addr = sock.recvfrom(1024) dns = dpkt.dns.DNS(data) if len(dns.qd) > 0 and dns.qd[0].type == dpkt.dns.DNS_A: if dns.qd[0].name == TESTER_NAME: - print('Received query: {} '.format(dns.__repr__())) + console_log('Received query: {} '.format(dns.__repr__())) sock.sendto(get_dns_answer_to_mdns(TESTER_NAME), (MCAST_GRP,UDP_PORT)) elif dns.qd[0].name == TESTER_NAME_LWIP: - print('Received query: {} '.format(dns.__repr__())) + console_log('Received query: {} '.format(dns.__repr__())) sock.sendto(get_dns_answer_to_mdns_lwip(TESTER_NAME_LWIP, dns.id), addr) if len(dns.an) > 0 and dns.an[0].type == dpkt.dns.DNS_A: if dns.an[0].name == esp_host + u'.local': - print('Received answer to esp32-mdns query: {}'.format(dns.__repr__())) + console_log('Received answer to esp32-mdns query: {}'.format(dns.__repr__())) esp_answered.set() + if dns.an[0].name == esp_host + u'-delegated.local': + console_log('Received answer to esp32-mdns-delegate query: {}'.format(dns.__repr__())) + esp_delegated_answered.set() except socket.timeout: break except dpkt.UnpackError: @@ -111,7 +125,7 @@ def test_examples_protocol_mdns(env, extra_data): thread1.start() try: ip_address = dut1.expect(re.compile(r' sta ip: ([^,]+),'), timeout=30)[0] - print('Connected to AP with IP: {}'.format(ip_address)) + console_log('Connected to AP with IP: {}'.format(ip_address)) except DUT.ExpectTimeout: stop_mdns_server.set() thread1.join() @@ -120,6 +134,8 @@ def test_examples_protocol_mdns(env, extra_data): # 3. check the mdns name is accessible if not esp_answered.wait(timeout=30): raise ValueError('Test has failed: did not receive mdns answer within timeout') + if not esp_delegated_answered.wait(timeout=30): + raise ValueError('Test has failed: did not receive mdns answer for delegated host within timeout') # 4. check DUT output if mdns advertized host is resolved dut1.expect(re.compile(r'mdns-test: Query A: tinytester.local resolved to: 127.0.0.1'), timeout=30) dut1.expect(re.compile(r'mdns-test: gethostbyname: tinytester-lwip.local resolved to: 127.0.0.1'), timeout=30) @@ -127,7 +143,7 @@ def test_examples_protocol_mdns(env, extra_data): # 5. check the DUT answers to `dig` command dig_output = subprocess.check_output(['dig', '+short', '-p', '5353', '@224.0.0.251', '{}.local'.format(specific_host)]) - print('Resolving {} using "dig" succeeded with:\n{}'.format(specific_host, dig_output)) + console_log('Resolving {} using "dig" succeeded with:\n{}'.format(specific_host, dig_output)) if not ip_address.encode('utf-8') in dig_output: raise ValueError('Test has failed: Incorrectly resolved DUT hostname using dig' "Output should've contained DUT's IP address:{}".format(ip_address)) diff --git a/examples/protocols/mdns/sdkconfig.ci b/examples/protocols/mdns/sdkconfig.ci index 42c55b8c0a..0587c0624e 100644 --- a/examples/protocols/mdns/sdkconfig.ci +++ b/examples/protocols/mdns/sdkconfig.ci @@ -1,3 +1,4 @@ CONFIG_MDNS_RESOLVE_TEST_SERVICES=y CONFIG_MDNS_ADD_MAC_TO_HOSTNAME=y +CONFIG_MDNS_PUBLISH_DELEGATE_HOST=y CONFIG_LWIP_DNS_SUPPORT_MDNS_QUERIES=y