MDNS send probes to verify domain before use

Signed-off-by: Simon Goldschmidt <goldsimon@gmx.de>
This commit is contained in:
Jens Nielsen 2018-01-16 22:47:00 +01:00 committed by Simon Goldschmidt
parent 8223a8c73c
commit 9f1196fb53
3 changed files with 331 additions and 40 deletions

View File

@ -57,11 +57,11 @@ Answers will use the supplied TTL (in seconds)
MDNS allows UTF-8 names, but it is recommended to stay within ASCII,
since the default case-insensitive comparison assumes this.
It is recommended to call this function after an IPv4 address has been set,
since there is currently no check if the v4 address is valid.
Call mdns_resp_announce() every time the IP address on the netif has changed.
Call mdns_resp_netif_settings_changed() every time the IP address
on the netif has changed.
Call mdns_resp_restart() every time the network interface comes up after being
down, for example cable connected after being disconnected, administrative
interface comes up after being down, or the device wakes up from sleep.
To stop responding on a netif, run
mdns_resp_remove_netif(struct netif *netif)
@ -108,6 +108,5 @@ and point them to <hostname>.local:80
Relevant information will be sent as additional records to reduce number of
requests required from a client.
Removing services is currently not supported. Services are removed when the
netif is removed.
To remove a service from a netif, run
mdns_resp_del_service(struct netif *netif, s8_t slot)

View File

@ -13,7 +13,7 @@
* Things left to implement:
* -------------------------
*
* - Probing/conflict resolution
* - Tiebreaking for simultaneous probing
* - Sending goodbye messages (zero ttl) - shutdown, DHCP lease about to expire, DHCP turned off...
* - Checking that source address of unicast requests are on the same network
* - Limiting multicast responses to 1 per second per resource record
@ -63,6 +63,7 @@
#include "lwip/mem.h"
#include "lwip/prot/dns.h"
#include "lwip/prot/iana.h"
#include "lwip/timeouts.h"
#include <string.h>
@ -102,6 +103,7 @@ static const ip_addr_t v6group = DNS_MQUERY_IPV6_GROUP_INIT;
static u8_t mdns_netif_client_id;
static struct udp_pcb *mdns_pcb;
NETIF_DECLARE_EXT_CALLBACK(netif_callback)
static mdns_name_result_cb_t mdns_name_result_cb = NULL;
#define NETIF_TO_HOST(netif) (struct mdns_host*)(netif_get_client_data(netif, mdns_netif_client_id))
@ -135,6 +137,12 @@ NETIF_DECLARE_EXT_CALLBACK(netif_callback)
/* Lookup for text info on service instance */
#define REPLY_SERVICE_TXT 0x80
typedef enum {
MDNS_PROBING_NOT_STARTED,
MDNS_PROBING_ONGOING,
MDNS_PROBING_COMPLETE,
} mdns_probing_state;
static const char *dnssd_protos[] = {
"_udp", /* DNSSD_PROTO_UDP */
"_tcp", /* DNSSD_PROTO_TCP */
@ -168,6 +176,10 @@ struct mdns_host {
struct mdns_service *services[MDNS_MAX_SERVICES];
/** TTL in seconds of A/AAAA/PTR replies */
u32_t dns_ttl;
/** Number of probes sent for the current name */
u8_t probes_sent;
/** State in probing sequence */
mdns_probing_state probing_state;
};
/** Information about received packet */
@ -191,7 +203,7 @@ struct mdns_packet {
/** Number of unparsed questions */
u16_t questions_left;
/** Number of answers in packet,
* (sum of normal, authorative and additional answers)
* (sum of normal, authoritative and additional answers)
* read from packet header */
u16_t answers;
/** Number of unparsed answers */
@ -215,6 +227,8 @@ struct mdns_outpacket {
u16_t questions;
/** Number of normal answers written */
u16_t answers;
/** Number of authoritative answers written */
u16_t authoritative;
/** Number of additional answers written */
u16_t additional;
/** Offsets for written domain names in packet.
@ -261,6 +275,9 @@ struct mdns_answer {
u16_t rd_offset;
};
static err_t mdns_send_outpacket(struct mdns_outpacket *outpkt, u8_t flags);
static void mdns_probe(void* arg);
static err_t
mdns_domain_add_label_base(struct mdns_domain *domain, u8_t len)
{
@ -1279,13 +1296,14 @@ mdns_init_outpacket(struct mdns_outpacket *out, struct mdns_packet *in)
* Add additional answers based on the selected answers
* Send the packet
*/
static void
mdns_send_outpacket(struct mdns_outpacket *outpkt)
static err_t
mdns_send_outpacket(struct mdns_outpacket *outpkt, u8_t flags)
{
struct mdns_service *service;
err_t res;
err_t res = ERR_ARG;
int i;
struct mdns_host *mdns = NETIF_TO_HOST(outpkt->netif);
u16_t answers = 0;
/* Write answers to host questions */
#if LWIP_IPV4
@ -1294,14 +1312,14 @@ mdns_send_outpacket(struct mdns_outpacket *outpkt)
if (res != ERR_OK) {
goto cleanup;
}
outpkt->answers++;
answers++;
}
if (outpkt->host_replies & REPLY_HOST_PTR_V4) {
res = mdns_add_hostv4_ptr_answer(outpkt, outpkt->cache_flush, outpkt->netif);
if (res != ERR_OK) {
goto cleanup;
}
outpkt->answers++;
answers++;
}
#endif
#if LWIP_IPV6
@ -1313,7 +1331,7 @@ mdns_send_outpacket(struct mdns_outpacket *outpkt)
if (res != ERR_OK) {
goto cleanup;
}
outpkt->answers++;
answers++;
}
}
}
@ -1326,7 +1344,7 @@ mdns_send_outpacket(struct mdns_outpacket *outpkt)
if (res != ERR_OK) {
goto cleanup;
}
outpkt->answers++;
answers++;
}
addrindex++;
rev_addrs >>= 1;
@ -1346,7 +1364,7 @@ mdns_send_outpacket(struct mdns_outpacket *outpkt)
if (res != ERR_OK) {
goto cleanup;
}
outpkt->answers++;
answers++;
}
if (outpkt->serv_replies[i] & REPLY_SERVICE_NAME_PTR) {
@ -1354,7 +1372,7 @@ mdns_send_outpacket(struct mdns_outpacket *outpkt)
if (res != ERR_OK) {
goto cleanup;
}
outpkt->answers++;
answers++;
}
if (outpkt->serv_replies[i] & REPLY_SERVICE_SRV) {
@ -1362,7 +1380,7 @@ mdns_send_outpacket(struct mdns_outpacket *outpkt)
if (res != ERR_OK) {
goto cleanup;
}
outpkt->answers++;
answers++;
}
if (outpkt->serv_replies[i] & REPLY_SERVICE_TXT) {
@ -1370,10 +1388,17 @@ mdns_send_outpacket(struct mdns_outpacket *outpkt)
if (res != ERR_OK) {
goto cleanup;
}
outpkt->answers++;
answers++;
}
}
/* if this is a response, the data above is anwers, else this is a probe and the answers above goes into auth section */
if ( flags & DNS_FLAG1_RESPONSE ) {
outpkt->answers += answers;
} else {
outpkt->authoritative += answers;
}
/* All answers written, add additional RRs */
for (i = 0; i < MDNS_MAX_SERVICES; ++i) {
service = mdns->services[i];
@ -1439,13 +1464,12 @@ mdns_send_outpacket(struct mdns_outpacket *outpkt)
/* Write header */
memset(&hdr, 0, sizeof(hdr));
hdr.flags1 = DNS_FLAG1_RESPONSE | DNS_FLAG1_AUTHORATIVE;
hdr.flags1 = flags;
hdr.numquestions = lwip_htons(outpkt->questions);
hdr.numanswers = lwip_htons(outpkt->answers);
hdr.numauthrr = lwip_htons(outpkt->authoritative);
hdr.numextrarr = lwip_htons(outpkt->additional);
if (outpkt->legacy_query) {
hdr.numquestions = lwip_htons(1);
hdr.id = lwip_htons(outpkt->tx_id);
}
hdr.id = lwip_htons(outpkt->tx_id);
pbuf_take(outpkt->pbuf, &hdr, sizeof(hdr));
/* Shrink packet */
@ -1463,9 +1487,9 @@ mdns_send_outpacket(struct mdns_outpacket *outpkt)
/* Send created packet */
LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Sending packet, len=%d, unicast=%d\n", outpkt->write_offset, outpkt->unicast_reply));
if (outpkt->unicast_reply) {
udp_sendto_if(mdns_pcb, outpkt->pbuf, &outpkt->dest_addr, outpkt->dest_port, outpkt->netif);
res = udp_sendto_if(mdns_pcb, outpkt->pbuf, &outpkt->dest_addr, outpkt->dest_port, outpkt->netif);
} else {
udp_sendto_if(mdns_pcb, outpkt->pbuf, mcast_destaddr, LWIP_IANA_PORT_MDNS, outpkt->netif);
res = udp_sendto_if(mdns_pcb, outpkt->pbuf, mcast_destaddr, LWIP_IANA_PORT_MDNS, outpkt->netif);
}
}
@ -1474,8 +1498,10 @@ cleanup:
pbuf_free(outpkt->pbuf);
outpkt->pbuf = NULL;
}
return res;
}
/**
* Send unsolicited answer containing all our known data
* @param netif The network interface to send on
@ -1515,7 +1541,7 @@ mdns_announce(struct netif *netif, const ip_addr_t *destination)
announce.dest_port = LWIP_IANA_PORT_MDNS;
SMEMCPY(&announce.dest_addr, destination, sizeof(announce.dest_addr));
mdns_send_outpacket(&announce);
mdns_send_outpacket(&announce, DNS_FLAG1_RESPONSE | DNS_FLAG1_AUTHORATIVE);
}
/**
@ -1534,6 +1560,12 @@ mdns_handle_question(struct mdns_packet *pkt)
err_t res;
struct mdns_host *mdns = NETIF_TO_HOST(pkt->netif);
if (mdns->probing_state != MDNS_PROBING_COMPLETE) {
/* Don't answer questions until we've verified our domains via probing */
/* todo we should check incoming questions during probing for tiebreaking */
return;
}
mdns_init_outpacket(&reply, pkt);
while (pkt->questions_left) {
@ -1569,6 +1601,7 @@ mdns_handle_question(struct mdns_packet *pkt)
if (replies && reply.legacy_query) {
/* Add question to reply packet (legacy packet only has 1 question) */
res = mdns_add_question(&reply, &q.info.domain, q.info.type, q.info.klass, 0);
reply.questions = 1;
if (res != ERR_OK) {
goto cleanup;
}
@ -1724,7 +1757,7 @@ mdns_handle_question(struct mdns_packet *pkt)
}
}
mdns_send_outpacket(&reply);
mdns_send_outpacket(&reply, DNS_FLAG1_RESPONSE | DNS_FLAG1_AUTHORATIVE);
cleanup:
if (reply.pbuf) {
@ -1741,6 +1774,8 @@ cleanup:
static void
mdns_handle_response(struct mdns_packet *pkt)
{
struct mdns_host* mdns = NETIF_TO_HOST(pkt->netif);
/* Ignore all questions */
while (pkt->questions_left) {
struct mdns_question q;
@ -1766,6 +1801,39 @@ mdns_handle_response(struct mdns_packet *pkt)
LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Answer for domain "));
mdns_domain_debug_print(&ans.info.domain);
LWIP_DEBUGF(MDNS_DEBUG, (" type %d class %d\n", ans.info.type, ans.info.klass));
/*"Apparently conflicting Multicast DNS responses received *before* the first probe packet is sent MUST
be silently ignored" so drop answer if we haven't started probing yet*/
if (mdns->probing_state == MDNS_PROBING_ONGOING && mdns->probes_sent > 0) {
struct mdns_domain domain;
u8_t i;
u8_t conflict = 0;
res = mdns_build_host_domain(&domain, mdns);
if (res == ERR_OK && mdns_domain_eq(&ans.info.domain, &domain)) {
LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Probe response matches host domain!"));
conflict = 1;
}
for (i = 0; i < MDNS_MAX_SERVICES; ++i) {
struct mdns_service* service = mdns->services[i];
if (!service) {
continue;
}
res = mdns_build_service_domain(&domain, service, 1);
if (res == ERR_OK && mdns_domain_eq(&ans.info.domain, &domain)) {
LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Probe response matches service domain!"));
conflict = 1;
}
}
if (conflict != 0) {
sys_untimeout(mdns_probe, pkt->netif);
if (mdns_name_result_cb != NULL) {
mdns_name_result_cb(pkt->netif, 0);
}
}
}
}
}
@ -1851,13 +1919,13 @@ mdns_netif_ext_status_callback(struct netif *netif, netif_nsc_reason_t reason, c
if (reason & LWIP_NSC_STATUS_CHANGED) {
if (args->status_changed.state != 0) {
mdns_resp_announce(netif);
mdns_resp_restart(netif);
}
/* TODO: send goodbye message */
}
if (reason & LWIP_NSC_LINK_CHANGED) {
if (args->link_changed.state != 0) {
mdns_resp_announce(netif);
mdns_resp_restart(netif);
}
}
if (reason & (LWIP_NSC_IPV4_ADDRESS_CHANGED | LWIP_NSC_IPV4_GATEWAY_CHANGED |
@ -1868,10 +1936,111 @@ mdns_netif_ext_status_callback(struct netif *netif, netif_nsc_reason_t reason, c
}
#endif
static err_t
mdns_send_probe(struct netif* netif, const ip_addr_t *destination)
{
struct mdns_host* mdns;
struct mdns_outpacket pkt;
struct mdns_domain domain;
u8_t i;
err_t res;
mdns = NETIF_TO_HOST(netif);
memset(&pkt, 0, sizeof(pkt));
pkt.netif = netif;
/* Add unicast questions with rtype ANY for all our desired records */
mdns_build_host_domain(&domain, mdns);
res = mdns_add_question(&pkt, &domain, DNS_RRTYPE_ANY, DNS_RRCLASS_IN, 1);
if (res != ERR_OK) {
goto cleanup;
}
pkt.questions++;
for (i = 0; i < MDNS_MAX_SERVICES; ++i) {
struct mdns_service* service = mdns->services[i];
if (!service) {
continue;
}
mdns_build_service_domain(&domain, service, 1);
res = mdns_add_question(&pkt, &domain, DNS_RRTYPE_ANY, DNS_RRCLASS_IN, 1);
if (res != ERR_OK) {
goto cleanup;
}
pkt.questions++;
}
/* Add answers to the questions above into the authority section for tiebreaking */
#if LWIP_IPV4
if (!ip4_addr_isany_val(*netif_ip4_addr(netif))) {
pkt.host_replies = REPLY_HOST_A;
}
#endif
#if LWIP_IPV6
for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; ++i) {
if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i))) {
pkt.host_replies |= REPLY_HOST_AAAA;
}
}
#endif
for (i = 0; i < MDNS_MAX_SERVICES; i++) {
struct mdns_service *serv = mdns->services[i];
if (serv) {
pkt.serv_replies[i] = REPLY_SERVICE_SRV | REPLY_SERVICE_TXT;
}
}
pkt.tx_id = 0;
pkt.dest_port = LWIP_IANA_PORT_MDNS;
SMEMCPY(&pkt.dest_addr, destination, sizeof(pkt.dest_addr));
res = mdns_send_outpacket(&pkt, 0);
cleanup:
if (pkt.pbuf) {
pbuf_free(pkt.pbuf);
pkt.pbuf = NULL;
}
return res;
}
/**
* Timer callback for probing network.
*/
static void
mdns_probe(void* arg)
{
struct netif *netif = (struct netif *)arg;
struct mdns_host* mdns = NETIF_TO_HOST(netif);
if(mdns->probes_sent >= 3) {
/* probing successful, announce the new name */
mdns->probing_state = MDNS_PROBING_COMPLETE;
mdns_resp_announce(netif);
if (mdns_name_result_cb != NULL) {
mdns_name_result_cb(netif, 1);
}
} else {
#if LWIP_IPV4
/*if ipv4 wait with probing until address is set*/
if (!ip4_addr_isany_val(*netif_ip4_addr(netif)) &&
mdns_send_probe(netif, IP4_ADDR_ANY) == ERR_OK)
#endif
{
#if LWIP_IPV6
if (mdns_send_probe(netif, IP6_ADDR_ANY) == ERR_OK)
#endif
{
mdns->probes_sent++;
}
}
sys_timeout(250, mdns_probe, netif);
}
}
/**
* @ingroup mdns
* Activate MDNS responder for a network interface and send announce packets.
* Don't forget to call mdns_resp_announce() after you added all netifs and services.
* Activate MDNS responder for a network interface.
* @param netif The network interface to activate.
* @param hostname Name to use. Queries for &lt;hostname&gt;.local will be answered
* with the IP addresses of the netif. The hostname will be copied, the
@ -1897,6 +2066,8 @@ mdns_resp_add_netif(struct netif *netif, const char *hostname, u32_t dns_ttl)
MEMCPY(&mdns->name, hostname, LWIP_MIN(MDNS_LABEL_MAXLEN, strlen(hostname)));
mdns->dns_ttl = dns_ttl;
mdns->probes_sent = 0;
mdns->probing_state = MDNS_PROBING_NOT_STARTED;
/* Join multicast groups */
#if LWIP_IPV4
@ -1912,6 +2083,8 @@ mdns_resp_add_netif(struct netif *netif, const char *hostname, u32_t dns_ttl)
}
#endif
mdns_resp_restart(netif);
return ERR_OK;
cleanup:
@ -1938,6 +2111,10 @@ mdns_resp_remove_netif(struct netif *netif)
mdns = NETIF_TO_HOST(netif);
LWIP_ERROR("mdns_resp_remove_netif: Not an active netif", (mdns != NULL), return ERR_VAL);
if (mdns->probing_state == MDNS_PROBING_ONGOING) {
sys_untimeout(mdns_probe, netif);
}
for (i = 0; i < MDNS_MAX_SERVICES; i++) {
struct mdns_service *service = mdns->services[i];
if (service) {
@ -1958,10 +2135,39 @@ mdns_resp_remove_netif(struct netif *netif)
return ERR_OK;
}
/**
* @ingroup mdns
* Update MDNS hostname for a network interface.
* @param netif The network interface to activate.
* @param hostname Name to use. Queries for &lt;hostname&gt;.local will be answered
* with the IP addresses of the netif. The hostname will be copied, the
* given pointer can be on the stack.
* @return ERR_OK if name could be set on netif, an err_t otherwise
*/
err_t
mdns_resp_rename_netif(struct netif *netif, const char *hostname)
{
struct mdns_host *mdns;
u8_t len;
LWIP_ASSERT_CORE_LOCKED();
len = strlen(hostname);
LWIP_ERROR("mdns_resp_rename_netif: netif != NULL", (netif != NULL), return ERR_VAL);
LWIP_ERROR("mdns_resp_rename_netif: Hostname too long", (len <= MDNS_LABEL_MAXLEN), return ERR_VAL);
mdns = NETIF_TO_HOST(netif);
LWIP_ERROR("mdns_resp_rename_netif: Not an mdns netif", (mdns != NULL), return ERR_VAL);
MEMCPY(&mdns->name, hostname, len);
mdns->name[len] = '\0'; /* null termination in case new name is shorter than previous */
mdns_resp_restart(netif);
return ERR_OK;
}
/**
* @ingroup mdns
* Add a service to the selected network interface.
* Don't forget to call mdns_resp_announce() after you added all services.
* @param netif The network interface to publish this service on
* @param name The name of the service
* @param service The service type, like "_http"
@ -2012,6 +2218,8 @@ mdns_resp_add_service(struct netif *netif, const char *name, const char *service
mdns->services[slot] = srv;
mdns_resp_restart(netif);
return slot;
}
@ -2039,6 +2247,40 @@ mdns_resp_del_service(struct netif *netif, s8_t slot)
return ERR_OK;
}
/**
* @ingroup mdns
* Update name for an MDNS service.
* @param netif The network interface to activate.
* @param slot The service slot number returned by mdns_resp_add_service
* @param name The new name for the service
* @return ERR_OK if name could be set on service, an err_t otherwise
*/
err_t
mdns_resp_rename_service(struct netif *netif, s8_t slot, const char *name)
{
struct mdns_service *srv;
struct mdns_host *mdns;
u8_t len;
LWIP_ASSERT_CORE_LOCKED();
len = strlen(name);
LWIP_ASSERT("mdns_resp_rename_service: netif != NULL", netif);
mdns = NETIF_TO_HOST(netif);
LWIP_ERROR("mdns_resp_rename_service: Not an mdns netif", (mdns != NULL), return ERR_VAL);
LWIP_ERROR("mdns_resp_rename_service: Name too long", (len <= MDNS_LABEL_MAXLEN), return ERR_VAL);
LWIP_ERROR("mdns_resp_rename_service: Invalid Service ID", (slot >= 0) && (slot < MDNS_MAX_SERVICES), return ERR_VAL);
LWIP_ERROR("mdns_resp_rename_service: Invalid Service ID", (mdns->services[slot] != NULL), return ERR_VAL);
srv = mdns->services[slot];
MEMCPY(&srv->name, name, len);
srv->name[len] = '\0'; /* null termination in case new name is shorter than previous */
mdns_resp_restart(netif);
return ERR_OK;
}
/**
* @ingroup mdns
* Call this function from inside the service_get_txt_fn_t callback to add text data.
@ -2066,22 +2308,61 @@ mdns_resp_add_service_txtitem(struct mdns_service *service, const char *txt, u8_
void
mdns_resp_announce(struct netif *netif)
{
struct mdns_host* mdns;
LWIP_ASSERT_CORE_LOCKED();
LWIP_ERROR("mdns_resp_announce: netif != NULL", (netif != NULL), return);
if (NETIF_TO_HOST(netif) == NULL) {
mdns = NETIF_TO_HOST(netif);
if (mdns == NULL) {
return;
}
/* Announce on IPv6 and IPv4 */
if (mdns->probing_state == MDNS_PROBING_COMPLETE)
{
/* Announce on IPv6 and IPv4 */
#if LWIP_IPV6
mdns_announce(netif, IP6_ADDR_ANY);
mdns_announce(netif, IP6_ADDR_ANY);
#endif
#if LWIP_IPV4
if (!ip4_addr_isany_val(*netif_ip4_addr(netif))) {
mdns_announce(netif, IP4_ADDR_ANY);
}
if (!ip4_addr_isany_val(*netif_ip4_addr(netif))) {
mdns_announce(netif, IP4_ADDR_ANY);
}
#endif
} /* else: ip address changed while probing was ongoing? todo reset counter to restart? */
}
void
mdns_resp_register_name_result_cb(mdns_name_result_cb_t cb)
{
mdns_name_result_cb = cb;
}
/**
* @ingroup mdns
* Restart mdns responder. Call this when cable is connected after being disconnected or
* administrative interface is set up after being down
* @param netif The network interface to send on
*/
void
mdns_resp_restart(struct netif *netif)
{
struct mdns_host* mdns;
LWIP_ASSERT_CORE_LOCKED();
LWIP_ERROR("mdns_resp_restart: netif != NULL", (netif != NULL), return);
mdns = NETIF_TO_HOST(netif);
if (mdns == NULL) {
return;
}
if (mdns->probing_state == MDNS_PROBING_ONGOING) {
sys_untimeout(mdns_probe, netif);
}
/*todo first probe timeout SHOULD be random 0-250 ms*/
/*todo if we've failed 15 times within a 10 second period we MUST wait 5 seconds (or wait 5 seconds every time except first)*/
mdns->probes_sent = 0;
mdns->probing_state = MDNS_PROBING_ONGOING;
sys_timeout(250, mdns_probe, netif);
}
/**

View File

@ -56,16 +56,27 @@ struct mdns_service;
/** Callback function to add text to a reply, called when generating the reply */
typedef void (*service_get_txt_fn_t)(struct mdns_service *service, void *txt_userdata);
/** Callback function to let application know the result of probing network for name
* uniqueness, called with result 1 if no other node claimed use for the name for the
* netif or a service and is safe to use, or 0 if another node is already using it and
* mdns is disabled on this interface */
typedef void (*mdns_name_result_cb_t)(struct netif* netif, u8_t result);
void mdns_resp_init(void);
void mdns_resp_register_name_result_cb(mdns_name_result_cb_t cb);
err_t mdns_resp_add_netif(struct netif *netif, const char *hostname, u32_t dns_ttl);
err_t mdns_resp_remove_netif(struct netif *netif);
err_t mdns_resp_rename_netif(struct netif *netif, const char *hostname);
s8_t mdns_resp_add_service(struct netif *netif, const char *name, const char *service, enum mdns_sd_proto proto, u16_t port, u32_t dns_ttl, service_get_txt_fn_t txt_fn, void *txt_userdata);
err_t mdns_resp_del_service(struct netif *netif, s8_t slot);
err_t mdns_resp_rename_service(struct netif *netif, s8_t slot, const char *name);
err_t mdns_resp_add_service_txtitem(struct mdns_service *service, const char *txt, u8_t txt_len);
void mdns_resp_restart(struct netif *netif);
void mdns_resp_announce(struct netif *netif);
/**