Merge branch 'feature/mdns-subtype' into 'master'

mdns: support service subtype

Closes IDFGH-3560

See merge request espressif/esp-idf!15467
This commit is contained in:
Guo Jia Cheng 2021-10-25 07:50:49 +00:00
commit a928411ab3
5 changed files with 226 additions and 39 deletions

View File

@ -1,16 +1,8 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// 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.
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ESP_MDNS_H_
#define ESP_MDNS_H_
@ -500,6 +492,24 @@ esp_err_t mdns_service_txt_item_remove(const char * service_type, const char * p
esp_err_t mdns_service_txt_item_remove_for_host(const char * service_type, const char * proto, const char * hostname,
const char * key);
/**
* @brief Add subtype for service.
*
* @param instance_name instance name. If NULL, will find the first service with the same service type and protocol.
* @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 subtype The subtype to add.
*
* @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_subtype_add_for_host(const char *instance_name, const char *service_type, const char *proto,
const char *hostname, const char *subtype);
/**
* @brief Remove and free all services from mDNS server
*

View File

@ -15,6 +15,10 @@
void mdns_debug_packet(const uint8_t * data, size_t len);
#endif
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0]))
#endif
// Internal size of IPv6 address is defined here as size of AAAA record in mdns packet
// since the ip6_addr_t is defined in lwip and depends on using IPv6 zones
#define _MDNS_SIZEOF_IP6_ADDR (MDNS_ANSWER_AAAA_SIZE)
@ -174,6 +178,24 @@ static mdns_srv_item_t * _mdns_get_service_item(const char * service, const char
return NULL;
}
static mdns_srv_item_t * _mdns_get_service_item_subtype(const char *subtype, const char * service, const char * proto)
{
mdns_srv_item_t * s = _mdns_server->services;
while (s) {
if (_mdns_service_match(s->service, service, proto, NULL)) {
mdns_subtype_t *subtype_item = s->service->subtype;
while(subtype_item) {
if (!strcasecmp(subtype_item->subtype, subtype)) {
return s;
}
subtype_item = subtype_item->next;
}
}
s = s->next;
}
return NULL;
}
static mdns_host_item_t * mdns_get_host_item(const char * hostname)
{
if (hostname == NULL || strcasecmp(hostname, _mdns_server->hostname) == 0) {
@ -605,6 +627,54 @@ static uint16_t _mdns_append_ptr_record(uint8_t * packet, uint16_t * index, cons
return record_length;
}
/**
* @brief appends PTR record for a subtype to a packet, incrementing the index
*
* @param packet MDNS packet
* @param index offset in the packet
* @param instance the service instance name
* @param subtype the service subtype
* @param proto the service protocol
* @param flush whether to set the flush flag
* @param bye whether to set the bye flag
*
* @return length of added data: 0 on error or length on success
*/
static uint16_t _mdns_append_subtype_ptr_record(uint8_t *packet, uint16_t *index, const char *instance,
const char *subtype, const char *service, const char *proto, bool flush,
bool bye)
{
const char *subtype_str[5] = {subtype, MDNS_SUB_STR, service, proto, MDNS_DEFAULT_DOMAIN};
const char *instance_str[4] = {instance, service, proto, MDNS_DEFAULT_DOMAIN};
uint16_t record_length = 0;
uint8_t part_length;
if (service == NULL) {
return 0;
}
part_length = _mdns_append_fqdn(packet, index, subtype_str, ARRAY_SIZE(subtype_str));
if (!part_length) {
return 0;
}
record_length += part_length;
part_length = _mdns_append_type(packet, index, MDNS_ANSWER_PTR, false, bye ? 0 : MDNS_ANSWER_PTR_TTL);
if (!part_length) {
return 0;
}
record_length += part_length;
uint16_t data_len_location = *index - 2;
part_length = _mdns_append_fqdn(packet, index, instance_str, ARRAY_SIZE(instance_str));
if (!part_length) {
return 0;
}
_mdns_set_u16(packet, data_len_location, part_length);
record_length += part_length;
return record_length;
}
/**
* @brief appends DNS-SD PTR record for service to a packet, incrementing the index
*
@ -1010,6 +1080,33 @@ static uint8_t _mdns_append_host_answer(uint8_t * packet, uint16_t * index, mdns
return num_records;
}
/**
* @brief Append PTR answers to packet
*
* @return number of answers added to the packet
*/
static uint8_t _mdns_append_service_ptr_answers(uint8_t *packet, uint16_t *index, mdns_service_t *service, bool flush,
bool bye)
{
uint8_t appended_answers = 0;
if (_mdns_append_ptr_record(packet, index, _mdns_get_service_instance_name(service), service->service,
service->proto, flush, bye) <= 0) {
return appended_answers;
}
mdns_subtype_t *subtype = service->subtype;
while (subtype) {
appended_answers +=
(_mdns_append_subtype_ptr_record(packet, index, _mdns_get_service_instance_name(service), subtype->subtype,
service->service, service->proto, flush, bye) > 0);
subtype = subtype->next;
}
return appended_answers;
}
/**
* @brief Append answer to packet
*
@ -1018,12 +1115,8 @@ static uint8_t _mdns_append_host_answer(uint8_t * packet, uint16_t * index, mdns
static uint8_t _mdns_append_answer(uint8_t * packet, uint16_t * index, mdns_out_answer_t * answer, mdns_if_t tcpip_if)
{
if (answer->type == MDNS_TYPE_PTR) {
if (answer->service) {
return _mdns_append_ptr_record(packet, index,
_mdns_get_service_instance_name(answer->service),
answer->service->service, answer->service->proto,
answer->flush, answer->bye) > 0;
return _mdns_append_service_ptr_answers(packet, index, answer->service, answer->flush, answer->bye);
} else {
return _mdns_append_ptr_record(packet, index,
answer->custom_instance, answer->custom_service, answer->custom_proto,
@ -1435,6 +1528,24 @@ static bool _mdns_create_answer_from_hostname(mdns_tx_packet_t * packet, const c
return true;
}
static bool _mdns_service_match_ptr_question(const mdns_service_t *service, const mdns_parsed_question_t *question)
{
if (!_mdns_service_match(service, question->service, question->proto, NULL)) {
return false;
}
if (question->sub) {
mdns_subtype_t *subtype = service->subtype;
while (subtype) {
if (!strcasecmp(subtype->subtype, question->host)) {
return true;
}
subtype = subtype->next;
}
return false;
}
return true;
}
/**
* @brief Create answer packet to questions from parsed packet
*/
@ -1466,7 +1577,7 @@ static void _mdns_create_answer_from_parsed_packet(mdns_parsed_packet_t *parsed_
} else if (q->service && q->proto) {
mdns_srv_item_t *service = _mdns_server->services;
while (service) {
if (_mdns_service_match(service->service, q->service, q->proto, NULL)) {
if (_mdns_service_match_ptr_question(service->service, q)) {
if (!_mdns_create_answer_from_service(packet, service->service, q, shared, send_flush)) {
_mdns_free_tx_packet(packet);
return;
@ -2179,6 +2290,7 @@ static mdns_service_t * _mdns_create_service(const char * service, const char *
s->instance = instance?strndup(instance, MDNS_NAME_BUF_LEN - 1):NULL;
s->txt = new_txt;
s->port = port;
s->subtype = NULL;
if (hostname) {
s->hostname = strndup(hostname, MDNS_NAME_BUF_LEN - 1);
@ -2338,7 +2450,12 @@ static void _mdns_free_service(mdns_service_t * service)
free((char *)s->value);
free(s);
}
free(service->txt);
while (service->subtype) {
mdns_subtype_t * next = service->subtype->next;
free((char *)service->subtype->subtype);
free(service->subtype);
service->subtype = next;
}
free(service);
}
@ -2701,9 +2818,12 @@ static bool _mdns_name_is_ours(mdns_name_t * name)
return false;
}
//find the service
mdns_srv_item_t * service;
if (_str_null_or_empty(name->host)) {
if (name->sub) {
service = _mdns_get_service_item_subtype(name->host, name->service, name->proto);
} else if (_str_null_or_empty(name->host)) {
service = _mdns_get_service_item(name->service, name->proto, NULL);
} else {
service = _mdns_get_service_item_instance(name->host, name->service, name->proto, NULL);
@ -2712,8 +2832,8 @@ static bool _mdns_name_is_ours(mdns_name_t * name)
return false;
}
//if host is empty and we have service, we have success
if (_str_null_or_empty(name->host)) {
//if query is PTR query and we have service, we have success
if (name->sub || _str_null_or_empty(name->host)) {
return true;
}
@ -3122,7 +3242,8 @@ void mdns_parse_packet(mdns_rx_packet_t * packet)
a = a->next;
}
continue;
} else if (name->sub || !_mdns_name_is_ours(name)) {
}
if (!_mdns_name_is_ours(name)) {
continue;
}
@ -3140,6 +3261,7 @@ void mdns_parse_packet(mdns_rx_packet_t * packet)
question->unicast = unicast;
question->type = type;
question->sub = name->sub;
if (_mdns_strdup_check(&(question->host), name->host)
|| _mdns_strdup_check(&(question->service), name->service)
|| _mdns_strdup_check(&(question->proto), name->proto)
@ -4247,6 +4369,9 @@ static void _mdns_free_action(mdns_action_t * action)
case ACTION_SERVICE_TXT_DEL:
free(action->data.srv_txt_del.key);
break;
case ACTION_SERVICE_SUBTYPE_ADD:
free(action->data.srv_subtype_add.subtype);
break;
case ACTION_SEARCH_ADD:
//fallthrough
case ACTION_SEARCH_SEND:
@ -4282,6 +4407,8 @@ static void _mdns_execute_action(mdns_action_t * action)
mdns_service_t * service;
char * key;
char * value;
char *subtype;
mdns_subtype_t *subtype_item;
mdns_txt_linked_item_t * txt, * t;
switch(action->type) {
@ -4395,6 +4522,19 @@ static void _mdns_execute_action(mdns_action_t * action)
_mdns_announce_all_pcbs(&action->data.srv_txt_set.service, 1, false);
break;
case ACTION_SERVICE_SUBTYPE_ADD:
service = action->data.srv_subtype_add.service->service;
subtype = action->data.srv_subtype_add.subtype;
subtype_item = (mdns_subtype_t *)malloc(sizeof(mdns_subtype_t));
if (!subtype_item) {
HOOK_MALLOC_FAILED;
_mdns_free_action(action);
return;
}
subtype_item->subtype = subtype;
subtype_item->next = service->subtype;
service->subtype = subtype_item;
break;
case ACTION_SERVICE_DEL:
a = _mdns_server->services;
@ -5249,6 +5389,40 @@ esp_err_t mdns_service_txt_item_remove(const char * service, const char * proto,
return mdns_service_txt_item_remove_for_host(service, proto, _mdns_server->hostname, key);
}
esp_err_t mdns_service_subtype_add_for_host(const char *instance_name, const char *service, const char *proto,
const char *hostname, const char *subtype)
{
if (!_mdns_server || !_mdns_server->services || _str_null_or_empty(service) || _str_null_or_empty(proto) ||
_str_null_or_empty(subtype)) {
return ESP_ERR_INVALID_ARG;
}
mdns_srv_item_t * s = _mdns_get_service_item_instance(instance_name, service, proto, hostname);
if (!s) {
return ESP_ERR_NOT_FOUND;
}
mdns_action_t * action = (mdns_action_t *)malloc(sizeof(mdns_action_t));
if (!action) {
HOOK_MALLOC_FAILED;
return ESP_ERR_NO_MEM;
}
action->type = ACTION_SERVICE_SUBTYPE_ADD;
action->data.srv_subtype_add.service = s;
action->data.srv_subtype_add.subtype = strdup(subtype);
if (!action->data.srv_subtype_add.subtype) {
free(action);
return ESP_ERR_NO_MEM;
}
if (xQueueSend(_mdns_server->action_queue, &action, (portTickType)0) != pdPASS) {
free(action->data.srv_subtype_add.subtype);
free(action);
return ESP_ERR_NO_MEM;
}
return ESP_OK;
}
esp_err_t mdns_service_instance_name_set_for_host(const char * service, const char * proto, const char * hostname,
const char * instance)
{

View File

@ -1,16 +1,8 @@
// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
//
// 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.
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef MDNS_PRIVATE_H_
#define MDNS_PRIVATE_H_
@ -176,6 +168,7 @@ typedef enum {
ACTION_SERVICE_TXT_REPLACE,
ACTION_SERVICE_TXT_SET,
ACTION_SERVICE_TXT_DEL,
ACTION_SERVICE_SUBTYPE_ADD,
ACTION_SERVICES_CLEAR,
ACTION_SEARCH_ADD,
ACTION_SEARCH_SEND,
@ -225,6 +218,7 @@ typedef struct {
typedef struct mdns_parsed_question_s {
struct mdns_parsed_question_s * next;
uint16_t type;
bool sub;
bool unicast;
char * host;
char * service;
@ -279,6 +273,11 @@ typedef struct mdns_txt_linked_item_s {
struct mdns_txt_linked_item_s * next; /*!< next result, or NULL for the last result in the list */
} mdns_txt_linked_item_t;
typedef struct mdns_subtype_s {
const char *subtype; /*!< subtype */
struct mdns_subtype_s * next; /*!< next result, or NULL for the last result in the list */
} mdns_subtype_t;
typedef struct {
const char * instance;
const char * service;
@ -288,6 +287,7 @@ typedef struct {
uint16_t weight;
uint16_t port;
mdns_txt_linked_item_t * txt;
mdns_subtype_t *subtype;
} mdns_service_t;
typedef struct mdns_srv_item_s {
@ -431,6 +431,10 @@ typedef struct {
mdns_srv_item_t * service;
char * key;
} srv_txt_del;
struct {
mdns_srv_item_t * service;
char * subtype;
} srv_subtype_add;
struct {
mdns_search_once_t * search;
} search_add;

View File

@ -53,6 +53,7 @@ static void initialise_mdns(void)
//initialize service
ESP_ERROR_CHECK( mdns_service_add("ESP32-WebServer", "_http", "_tcp", 80, serviceTxtData, 3) );
ESP_ERROR_CHECK( mdns_service_subtype_add_for_host("ESP32-WebServer", "_http", "_tcp", NULL, "_server") );
#if CONFIG_MDNS_MULTIPLE_INSTANCE
ESP_ERROR_CHECK( mdns_service_add("ESP32-WebServer1", "_http", "_tcp", 80, NULL, 0) );
#endif

View File

@ -1833,13 +1833,11 @@ components/mdns/host_test/components/freertos_linux/include/freertos/task.h
components/mdns/host_test/components/freertos_linux/queue_unique_ptr.cpp
components/mdns/host_test/components/freertos_linux/queue_unique_ptr.hpp
components/mdns/host_test/main/main.c
components/mdns/include/mdns.h
components/mdns/include/mdns_console.h
components/mdns/mdns_console.c
components/mdns/mdns_networking_lwip.c
components/mdns/mdns_networking_socket.c
components/mdns/private_include/mdns_networking.h
components/mdns/private_include/mdns_private.h
components/mdns/test/test_mdns.c
components/mdns/test_afl_fuzz_host/esp32_mock.c
components/mdns/test_afl_fuzz_host/esp32_mock.h