1 /*
2  * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include "mdns.h"
8 #include "mdns_private.h"
9 #include "mdns_networking.h"
10 #include "esp_log.h"
11 #include <string.h>
12 #include <sys/param.h>
13 
14 #ifdef MDNS_ENABLE_DEBUG
15 void mdns_debug_packet(const uint8_t * data, size_t len);
16 #endif
17 
18 // Internal size of IPv6 address is defined here as size of AAAA record in mdns packet
19 // since the ip6_addr_t is defined in lwip and depends on using IPv6 zones
20 #define _MDNS_SIZEOF_IP6_ADDR (MDNS_ANSWER_AAAA_SIZE)
21 
22 static const char * MDNS_DEFAULT_DOMAIN = "local";
23 static const char * MDNS_SUB_STR = "_sub";
24 
25 mdns_server_t * _mdns_server = NULL;
26 static mdns_host_item_t * _mdns_host_list = NULL;
27 static mdns_host_item_t _mdns_self_host;
28 
29 static const char *TAG = "MDNS";
30 
31 static volatile TaskHandle_t _mdns_service_task_handle = NULL;
32 static SemaphoreHandle_t _mdns_service_semaphore = NULL;
33 
34 static void _mdns_search_finish_done(void);
35 static mdns_search_once_t * _mdns_search_find_from(mdns_search_once_t * search, mdns_name_t * name, uint16_t type, mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol);
36 static void _mdns_search_result_add_ip(mdns_search_once_t * search, const char * hostname, esp_ip_addr_t * ip,
37                                        mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, uint32_t ttl);
38 static void _mdns_search_result_add_srv(mdns_search_once_t *search, const char *hostname, uint16_t port,
39                                         mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, uint32_t ttl);
40 static void _mdns_search_result_add_txt(mdns_search_once_t *search, mdns_txt_item_t *txt, uint8_t *txt_value_len,
41                                         size_t txt_count, mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol,
42                                         uint32_t ttl);
43 static mdns_result_t * _mdns_search_result_add_ptr(mdns_search_once_t * search, const char * instance,
44                                                    const char * service_type, const char * proto, mdns_if_t tcpip_if,
45                                                    mdns_ip_protocol_t ip_protocol, uint32_t ttl);
46 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);
47 static bool _mdns_append_host_list(mdns_out_answer_t ** destination, bool flush, bool bye);
48 static void _mdns_remap_self_service_hostname(const char *old_hostname, const char *new_hostname);
49 
50 /*
51  * @brief  Internal collection of mdns supported interfaces
52  *
53  */
54 static esp_netif_t * s_esp_netifs[MDNS_IF_MAX] = {};
55 
56 /*
57  * @brief  Convert mdns if to esp-netif handle
58  */
_mdns_get_esp_netif(mdns_if_t tcpip_if)59 esp_netif_t *_mdns_get_esp_netif(mdns_if_t tcpip_if)
60 {
61     if (tcpip_if < MDNS_IF_MAX) {
62         if (s_esp_netifs[tcpip_if] == NULL) {
63             // if local netif copy is NULL, try to search for the default interface key
64             if (tcpip_if == MDNS_IF_STA) {
65                 s_esp_netifs[MDNS_IF_STA] = esp_netif_get_handle_from_ifkey("WIFI_STA_DEF");
66             } else if (tcpip_if == MDNS_IF_AP) {
67                 s_esp_netifs[MDNS_IF_AP] = esp_netif_get_handle_from_ifkey("WIFI_AP_DEF");
68 #if CONFIG_ETH_ENABLED
69             } else if (tcpip_if == MDNS_IF_ETH) {
70                 s_esp_netifs[MDNS_IF_ETH] = esp_netif_get_handle_from_ifkey("ETH_DEF");
71 #endif
72             }
73         }
74         return s_esp_netifs[tcpip_if];
75     }
76     return NULL;
77 }
78 
79 
80 /*
81  * @brief Clean internal mdns interface's pointer
82  */
_mdns_clean_netif_ptr(mdns_if_t tcpip_if)83 static inline void _mdns_clean_netif_ptr(mdns_if_t tcpip_if) {
84     if (tcpip_if < MDNS_IF_MAX) {
85         s_esp_netifs[tcpip_if] = NULL;
86     }
87 }
88 
89 
90 /*
91  * @brief  Convert esp-netif handle to mdns if
92  */
_mdns_get_if_from_esp_netif(esp_netif_t * interface)93 static mdns_if_t _mdns_get_if_from_esp_netif(esp_netif_t *interface)
94 {
95     for (int i=0; i<MDNS_IF_MAX; ++i) {
96         if (interface == s_esp_netifs[i])
97             return i;
98     }
99     return MDNS_IF_MAX;
100 }
101 
102 
103 
_str_null_or_empty(const char * str)104 static inline bool _str_null_or_empty(const char * str){
105     return (str == NULL || *str == 0);
106 }
107 
108 /*
109  * @brief  Appends/increments a number to name/instance in case of collision
110  * */
_mdns_mangle_name(char * in)111 static char * _mdns_mangle_name(char* in) {
112     char *p = strrchr(in, '-');
113     int suffix = 0;
114     if (p == NULL) {
115         //No - in ``in``
116         suffix = 2;
117     } else {
118         char *endp = NULL;
119         suffix = strtol(p + 1, &endp, 10);
120         if (*endp != 0) {
121             //suffix is not numerical
122             suffix = 2;
123             p = NULL; //so we append -suffix to the entire string
124         }
125     }
126     char *ret;
127     if (p == NULL) {
128         //need to add -2 to string
129         ret = malloc(strlen(in) + 3);
130         if (ret == NULL) {
131             HOOK_MALLOC_FAILED;
132             return NULL;
133         }
134         sprintf(ret, "%s-2", in);
135     } else {
136         ret = malloc(strlen(in) + 2); //one extra byte in case 9-10 or 99-100 etc
137         if (ret == NULL) {
138             HOOK_MALLOC_FAILED;
139             return NULL;
140         }
141         strcpy(ret, in);
142         int baseLen = p - in; //length of 'bla' in 'bla-123'
143         //overwrite suffix with new suffix
144         sprintf(ret + baseLen, "-%d", suffix + 1);
145     }
146     return ret;
147 }
148 
_mdns_service_match(const mdns_service_t * srv,const char * service,const char * proto,const char * hostname)149 static bool _mdns_service_match(const mdns_service_t * srv, const char * service, const char * proto,
150                                 const char * hostname)
151 {
152     return !strcasecmp(srv->service, service) && !strcasecmp(srv->proto, proto) &&
153         (_str_null_or_empty(hostname) || !strcasecmp(srv->hostname, hostname));
154 }
155 
156 /**
157  * @brief  finds service from given service type
158  * @param  server       the server
159  * @param  service      service type to match
160  * @param  proto        proto to match
161  * @param  hostname     hostname of the service (if non-null)
162  *
163  * @return the service item if found or NULL on error
164  */
_mdns_get_service_item(const char * service,const char * proto,const char * hostname)165 static mdns_srv_item_t * _mdns_get_service_item(const char * service, const char * proto, const char * hostname)
166 {
167     mdns_srv_item_t * s = _mdns_server->services;
168     while (s) {
169         if (_mdns_service_match(s->service, service, proto, hostname)) {
170             return s;
171         }
172         s = s->next;
173     }
174     return NULL;
175 }
176 
mdns_get_host_item(const char * hostname)177 static mdns_host_item_t * mdns_get_host_item(const char * hostname)
178 {
179     if (hostname == NULL || strcasecmp(hostname, _mdns_server->hostname) == 0) {
180         return &_mdns_self_host;
181     }
182     mdns_host_item_t * host = _mdns_host_list;
183     while (host != NULL) {
184         if (strcasecmp(host->hostname, hostname) == 0) {
185             return host;
186         }
187         host = host->next;
188     }
189     return NULL;
190 }
191 
_mdns_can_add_more_services(void)192 static bool _mdns_can_add_more_services(void)
193 {
194     mdns_srv_item_t * s = _mdns_server->services;
195     uint16_t service_num = 0;
196     while (s) {
197         service_num ++;
198         s = s->next;
199         if (service_num >= MDNS_MAX_SERVICES) {
200             return false;
201         }
202     }
203 
204     return true;
205 }
206 
_mdns_send_rx_action(mdns_rx_packet_t * packet)207 esp_err_t _mdns_send_rx_action(mdns_rx_packet_t * packet)
208 {
209     mdns_action_t * action = NULL;
210 
211     action = (mdns_action_t *)malloc(sizeof(mdns_action_t));
212     if (!action) {
213         HOOK_MALLOC_FAILED;
214         return ESP_ERR_NO_MEM;
215     }
216 
217     action->type = ACTION_RX_HANDLE;
218     action->data.rx_handle.packet = packet;
219     if (xQueueSend(_mdns_server->action_queue, &action, (portTickType)0) != pdPASS) {
220         free(action);
221         return ESP_ERR_NO_MEM;
222     }
223     return ESP_OK;
224 }
225 
_mdns_get_default_instance_name(void)226 static const char *_mdns_get_default_instance_name(void)
227 {
228     if (_mdns_server && !_str_null_or_empty(_mdns_server->instance)) {
229         return _mdns_server->instance;
230     }
231 
232     if (_mdns_server && !_str_null_or_empty(_mdns_server->hostname)) {
233         return _mdns_server->hostname;
234     }
235 
236     return NULL;
237 }
238 
239 /**
240  * @brief  Get the service name of a service
241  */
_mdns_get_service_instance_name(const mdns_service_t * service)242 static const char * _mdns_get_service_instance_name(const mdns_service_t * service)
243 {
244     if (service && !_str_null_or_empty(service->instance)) {
245         return service->instance;
246     }
247 
248     return _mdns_get_default_instance_name();
249 }
250 
_mdns_instance_name_match(const char * lhs,const char * rhs)251 static bool _mdns_instance_name_match(const char *lhs, const char *rhs)
252 {
253     if (lhs == NULL) {
254         lhs = _mdns_get_default_instance_name();
255     }
256     if (rhs == NULL) {
257         rhs = _mdns_get_default_instance_name();
258     }
259     return !strcasecmp(lhs, rhs);
260 }
261 
_mdns_service_match_instance(const mdns_service_t * srv,const char * instance,const char * service,const char * proto,const char * hostname)262 static bool _mdns_service_match_instance(const mdns_service_t *srv, const char *instance, const char *service,
263                                          const char *proto, const char *hostname)
264 {
265     return !strcasecmp(srv->service, service) && _mdns_instance_name_match(srv->instance, instance) &&
266         !strcasecmp(srv->proto, proto) && (_str_null_or_empty(hostname) || !strcasecmp(srv->hostname, hostname));
267 }
268 
_mdns_get_service_item_instance(const char * instance,const char * service,const char * proto,const char * hostname)269 static mdns_srv_item_t *_mdns_get_service_item_instance(const char *instance, const char *service, const char *proto,
270                                                         const char *hostname)
271 {
272     mdns_srv_item_t *s = _mdns_server->services;
273     while (s) {
274         if (_mdns_service_match_instance(s->service, instance, service, proto, hostname)) {
275             return s;
276         }
277         s = s->next;
278     }
279     return NULL;
280 }
281 
282 /**
283  * @brief  reads MDNS FQDN into mdns_name_t structure
284  *         FQDN is in format: [hostname.|[instance.]_service._proto.]local.
285  *
286  * @param  packet       MDNS packet
287  * @param  start        Starting point of FQDN
288  * @param  name         mdns_name_t structure to populate
289  * @param  buf          temporary char buffer
290  *
291  * @return the address after the parsed FQDN in the packet or NULL on error
292  */
_mdns_read_fqdn(const uint8_t * packet,const uint8_t * start,mdns_name_t * name,char * buf)293 static const uint8_t * _mdns_read_fqdn(const uint8_t * packet, const uint8_t * start, mdns_name_t * name, char * buf)
294 {
295     size_t index = 0;
296     while (start[index]) {
297         if (name->parts == 4) {
298             name->invalid = true;
299         }
300         uint8_t len = start[index++];
301         if (len < 0xC0) {
302             if (len > 63) {
303                 //length can not be more than 63
304                 return NULL;
305             }
306             uint8_t i;
307             for (i=0; i<len; i++) {
308                 buf[i] = start[index++];
309             }
310             buf[len] = '\0';
311             if (name->parts == 1 && buf[0] != '_'
312                     && (strcasecmp(buf, MDNS_DEFAULT_DOMAIN) != 0)
313                     && (strcasecmp(buf, "arpa") != 0)
314                     && (strcasecmp(buf, "ip6") != 0)
315                     && (strcasecmp(buf, "in-addr") != 0)) {
316                 strlcat(name->host, ".", sizeof(name->host));
317                 strlcat(name->host, buf, sizeof(name->host));
318             } else if (strcasecmp(buf, MDNS_SUB_STR) == 0) {
319                 name->sub = 1;
320             } else if (!name->invalid) {
321                 char* mdns_name_ptrs[]={name->host, name->service, name->proto, name->domain};
322                 memcpy(mdns_name_ptrs[name->parts++], buf, len+1);
323             }
324         } else {
325             size_t address = (((uint16_t)len & 0x3F) << 8) | start[index++];
326             if ((packet + address) >= start) {
327                 //reference address can not be after where we are
328                 return NULL;
329             }
330             if (_mdns_read_fqdn(packet, packet + address, name, buf)) {
331                 return start + index;
332             }
333             return NULL;
334         }
335     }
336     return start + index + 1;
337 }
338 
339 /**
340  * @brief  sets uint16_t value in a packet
341  *
342  * @param  packet       MDNS packet
343  * @param  index        offset of uint16_t value
344  * @param  value        the value to set
345  */
_mdns_set_u16(uint8_t * packet,uint16_t index,uint16_t value)346 static inline void _mdns_set_u16(uint8_t * packet, uint16_t index, uint16_t value)
347 {
348     if ((index + 1) >= MDNS_MAX_PACKET_SIZE) {
349         return;
350     }
351     packet[index] = (value >> 8) & 0xFF;
352     packet[index+1] = value & 0xFF;
353 }
354 
355 /**
356  * @brief  appends byte in a packet, incrementing the index
357  *
358  * @param  packet       MDNS packet
359  * @param  index        offset in the packet
360  * @param  value        the value to set
361  *
362  * @return length of added data: 0 on error or 1 on success
363  */
_mdns_append_u8(uint8_t * packet,uint16_t * index,uint8_t value)364 static inline uint8_t _mdns_append_u8(uint8_t * packet, uint16_t * index, uint8_t value)
365 {
366     if (*index >= MDNS_MAX_PACKET_SIZE) {
367         return 0;
368     }
369     packet[*index] = value;
370     *index += 1;
371     return 1;
372 }
373 
374 /**
375  * @brief  appends uint16_t in a packet, incrementing the index
376  *
377  * @param  packet       MDNS packet
378  * @param  index        offset in the packet
379  * @param  value        the value to set
380  *
381  * @return length of added data: 0 on error or 2 on success
382  */
_mdns_append_u16(uint8_t * packet,uint16_t * index,uint16_t value)383 static inline uint8_t _mdns_append_u16(uint8_t * packet, uint16_t * index, uint16_t value)
384 {
385     if ((*index + 1) >= MDNS_MAX_PACKET_SIZE) {
386         return 0;
387     }
388     _mdns_append_u8(packet, index, (value >> 8) & 0xFF);
389     _mdns_append_u8(packet, index, value & 0xFF);
390     return 2;
391 }
392 
393 /**
394  * @brief  appends uint32_t in a packet, incrementing the index
395  *
396  * @param  packet       MDNS packet
397  * @param  index        offset in the packet
398  * @param  value        the value to set
399  *
400  * @return length of added data: 0 on error or 4 on success
401  */
_mdns_append_u32(uint8_t * packet,uint16_t * index,uint32_t value)402 static inline uint8_t _mdns_append_u32(uint8_t * packet, uint16_t * index, uint32_t value)
403 {
404     if ((*index + 3) >= MDNS_MAX_PACKET_SIZE) {
405         return 0;
406     }
407     _mdns_append_u8(packet, index, (value >> 24) & 0xFF);
408     _mdns_append_u8(packet, index, (value >> 16) & 0xFF);
409     _mdns_append_u8(packet, index, (value >> 8) & 0xFF);
410     _mdns_append_u8(packet, index, value & 0xFF);
411     return 4;
412 }
413 
414 /**
415  * @brief  appends answer type, class, ttl and data length to a packet, incrementing the index
416  *
417  * @param  packet       MDNS packet
418  * @param  index        offset in the packet
419  * @param  type         answer type
420  * @param  ttl          answer ttl
421  *
422  * @return length of added data: 0 on error or 10 on success
423  */
_mdns_append_type(uint8_t * packet,uint16_t * index,uint8_t type,bool flush,uint32_t ttl)424 static inline uint8_t _mdns_append_type(uint8_t * packet, uint16_t * index, uint8_t type, bool flush, uint32_t ttl)
425 {
426     if ((*index + 10) >= MDNS_MAX_PACKET_SIZE) {
427         return 0;
428     }
429     uint16_t mdns_class = MDNS_CLASS_IN;
430     if (flush) {
431         mdns_class = MDNS_CLASS_IN_FLUSH_CACHE;
432     }
433     if (type == MDNS_ANSWER_PTR) {
434         _mdns_append_u16(packet, index, MDNS_TYPE_PTR);
435         _mdns_append_u16(packet, index, mdns_class);
436     } else if (type == MDNS_ANSWER_TXT) {
437         _mdns_append_u16(packet, index, MDNS_TYPE_TXT);
438         _mdns_append_u16(packet, index, mdns_class);
439     } else if (type == MDNS_ANSWER_SRV) {
440         _mdns_append_u16(packet, index, MDNS_TYPE_SRV);
441         _mdns_append_u16(packet, index, mdns_class);
442     } else if (type == MDNS_ANSWER_A) {
443         _mdns_append_u16(packet, index, MDNS_TYPE_A);
444         _mdns_append_u16(packet, index, mdns_class);
445     } else if (type == MDNS_ANSWER_AAAA) {
446         _mdns_append_u16(packet, index, MDNS_TYPE_AAAA);
447         _mdns_append_u16(packet, index, mdns_class);
448     } else {
449         return 0;
450     }
451     _mdns_append_u32(packet, index, ttl);
452     _mdns_append_u16(packet, index, 0);
453     return 10;
454 }
455 
_mdns_append_string_with_len(uint8_t * packet,uint16_t * index,const char * string,uint8_t len)456 static inline uint8_t _mdns_append_string_with_len(uint8_t * packet, uint16_t * index, const char * string, uint8_t len)
457 {
458     if ((*index + len + 1) >= MDNS_MAX_PACKET_SIZE) {
459         return 0;
460     }
461     _mdns_append_u8(packet, index, len);
462     memcpy(packet + *index, string, len);
463     *index += len;
464     return len + 1;
465 }
466 
467 /**
468  * @brief  appends single string to a packet, incrementing the index
469  *
470  * @param  packet       MDNS packet
471  * @param  index        offset in the packet
472  * @param  string       the string to append
473  *
474  * @return length of added data: 0 on error or length of the string + 1 on success
475  */
_mdns_append_string(uint8_t * packet,uint16_t * index,const char * string)476 static inline uint8_t _mdns_append_string(uint8_t * packet, uint16_t * index, const char * string)
477 {
478     uint8_t len = strlen(string);
479     if ((*index + len + 1) >= MDNS_MAX_PACKET_SIZE) {
480         return 0;
481     }
482     _mdns_append_u8(packet, index, len);
483     memcpy(packet + *index, string, len);
484     *index += len;
485     return len + 1;
486 }
487 
488 /**
489  * @brief  appends one TXT record ("key=value" or "key")
490  *
491  * @param  packet       MDNS packet
492  * @param  index        offset in the packet
493  * @param  txt          one txt record
494  *
495  * @return length of added data: length of the added txt value + 1 on success
496  *         0  if data won't fit the packet
497  *         -1 if invalid TXT entry
498  */
append_one_txt_record_entry(uint8_t * packet,uint16_t * index,mdns_txt_linked_item_t * txt)499 static inline int append_one_txt_record_entry(uint8_t * packet, uint16_t * index, mdns_txt_linked_item_t * txt)
500 {
501     if (txt == NULL || txt->key == NULL) {
502         return -1;
503     }
504     size_t key_len = strlen(txt->key);
505     size_t len = key_len + txt->value_len + (txt->value ? 1 : 0);
506     if ((*index + len + 1) >= MDNS_MAX_PACKET_SIZE) {
507         return 0;
508     }
509     _mdns_append_u8(packet, index, len);
510     memcpy(packet + *index, txt->key, key_len);
511     if (txt->value) {
512         packet[*index + key_len] = '=';
513         memcpy(packet + *index + key_len + 1, txt->value, txt->value_len);
514     }
515     *index += len;
516     return len + 1;
517 }
518 
519 /**
520  * @brief  appends FQDN to a packet, incrementing the index and
521  *         compressing the output if previous occurrence of the string (or part of it) has been found
522  *
523  * @param  packet       MDNS packet
524  * @param  index        offset in the packet
525  * @param  strings      string array containing the parts of the FQDN
526  * @param  count        number of strings in the array
527  *
528  * @return length of added data: 0 on error or length on success
529  */
_mdns_append_fqdn(uint8_t * packet,uint16_t * index,const char * strings[],uint8_t count)530 static uint16_t _mdns_append_fqdn(uint8_t * packet, uint16_t * index, const char * strings[], uint8_t count)
531 {
532     if (!count) {
533         //empty string so terminate
534         return _mdns_append_u8(packet, index, 0);
535     }
536     mdns_name_t name;
537     static char buf[MDNS_NAME_BUF_LEN];
538     uint8_t len = strlen(strings[0]);
539     //try to find first the string length in the packet (if it exists)
540     uint8_t * len_location = (uint8_t *)memchr(packet, (char)len, *index);
541     while (len_location) {
542         //check if the string after len_location is the string that we are looking for
543         if (memcmp(len_location+1, strings[0], len)) { //not continuing with our string
544 search_next:
545             //try and find the length byte further in the packet
546             len_location = (uint8_t *)memchr(len_location+1, (char)len, *index - (len_location+1 - packet));
547             continue;
548         }
549         //seems that we might have found the string that we are looking for
550         //read the destination into name and compare
551         name.parts = 0;
552         name.sub = 0;
553         name.host[0] = 0;
554         name.service[0] = 0;
555         name.proto[0] = 0;
556         name.domain[0] = 0;
557         const uint8_t * content = _mdns_read_fqdn(packet, len_location, &name, buf);
558         if (!content) {
559             //not a readable fqdn?
560             return 0;
561         }
562         if (name.parts == count) {
563             uint8_t i;
564             for (i=0; i<count; i++) {
565                 if (strcasecmp(strings[i], (const char *)&name + (i * (MDNS_NAME_BUF_LEN)))) {
566                     //not our string! let's search more
567                     goto search_next;
568                 }
569             }
570             //we actually have found the string
571             break;
572         } else {
573             goto search_next;
574         }
575     }
576     //string is not yet in the packet, so let's add it
577     if (!len_location) {
578         uint8_t written = _mdns_append_string(packet, index, strings[0]);
579         if (!written) {
580             return 0;
581         }
582         //run the same for the other strings in the name
583         return written + _mdns_append_fqdn(packet, index, &strings[1], count - 1);
584     }
585 
586     //we have found the string so let's insert a pointer to it instead
587     uint16_t offset = len_location - packet;
588     offset |= MDNS_NAME_REF;
589     return _mdns_append_u16(packet, index, offset);
590 }
591 
592 /**
593  * @brief  appends PTR record for service to a packet, incrementing the index
594  *
595  * @param  packet       MDNS packet
596  * @param  index        offset in the packet
597  * @param  server       the server that is hosting the service
598  * @param  service      the service to add record for
599  *
600  * @return length of added data: 0 on error or length on success
601  */
_mdns_append_ptr_record(uint8_t * packet,uint16_t * index,const char * instance,const char * service,const char * proto,bool flush,bool bye)602 static uint16_t _mdns_append_ptr_record(uint8_t * packet, uint16_t * index, const char * instance, const char * service, const char * proto, bool flush, bool bye)
603 {
604     const char * str[4];
605     uint16_t record_length = 0;
606     uint8_t part_length;
607 
608     if (service == NULL) {
609         return 0;
610     }
611 
612     str[0] = instance;
613     str[1] = service;
614     str[2] = proto;
615     str[3] = MDNS_DEFAULT_DOMAIN;
616 
617     part_length = _mdns_append_fqdn(packet, index, str + 1, 3);
618     if (!part_length) {
619         return 0;
620     }
621     record_length += part_length;
622 
623     part_length = _mdns_append_type(packet, index, MDNS_ANSWER_PTR, false, bye?0:MDNS_ANSWER_PTR_TTL);
624     if (!part_length) {
625         return 0;
626     }
627     record_length += part_length;
628 
629     uint16_t data_len_location = *index - 2;
630     part_length = _mdns_append_fqdn(packet, index, str, 4);
631     if (!part_length) {
632         return 0;
633     }
634     _mdns_set_u16(packet, data_len_location, part_length);
635     record_length += part_length;
636     return record_length;
637 }
638 
639 /**
640  * @brief  appends DNS-SD PTR record for service to a packet, incrementing the index
641  *
642  * @param  packet       MDNS packet
643  * @param  index        offset in the packet
644  * @param  server       the server that is hosting the service
645  * @param  service      the service to add record for
646  *
647  * @return length of added data: 0 on error or length on success
648  */
_mdns_append_sdptr_record(uint8_t * packet,uint16_t * index,mdns_service_t * service,bool flush,bool bye)649 static uint16_t _mdns_append_sdptr_record(uint8_t * packet, uint16_t * index, mdns_service_t * service, bool flush, bool bye)
650 {
651     const char * str[3];
652     const char * sd_str[4];
653     uint16_t record_length = 0;
654     uint8_t part_length;
655 
656     if (service == NULL) {
657         return 0;
658     }
659 
660     sd_str[0] = (char*)"_services";
661     sd_str[1] = (char*)"_dns-sd";
662     sd_str[2] = (char*)"_udp";
663     sd_str[3] = MDNS_DEFAULT_DOMAIN;
664 
665     str[0] = service->service;
666     str[1] = service->proto;
667     str[2] = MDNS_DEFAULT_DOMAIN;
668 
669     part_length = _mdns_append_fqdn(packet, index, sd_str, 4);
670 
671     record_length += part_length;
672 
673     part_length = _mdns_append_type(packet, index, MDNS_ANSWER_PTR, flush, MDNS_ANSWER_PTR_TTL);
674     if (!part_length) {
675         return 0;
676     }
677     record_length += part_length;
678 
679     uint16_t data_len_location = *index - 2;
680     part_length = _mdns_append_fqdn(packet, index, str, 3);
681     if (!part_length) {
682         return 0;
683     }
684     _mdns_set_u16(packet, data_len_location, part_length);
685     record_length += part_length;
686     return record_length;
687 }
688 
689 /**
690  * @brief  appends TXT record for service to a packet, incrementing the index
691  *
692  * @param  packet       MDNS packet
693  * @param  index        offset in the packet
694  * @param  server       the server that is hosting the service
695  * @param  service      the service to add record for
696  *
697  * @return length of added data: 0 on error or length on success
698  */
_mdns_append_txt_record(uint8_t * packet,uint16_t * index,mdns_service_t * service,bool flush,bool bye)699 static uint16_t _mdns_append_txt_record(uint8_t * packet, uint16_t * index, mdns_service_t * service, bool flush, bool bye)
700 {
701     const char * str[4];
702     uint16_t record_length = 0;
703     uint8_t part_length;
704 
705     if (service == NULL) {
706         return 0;
707     }
708 
709     str[0] = _mdns_get_service_instance_name(service);
710     str[1] = service->service;
711     str[2] = service->proto;
712     str[3] = MDNS_DEFAULT_DOMAIN;
713 
714     if (!str[0]) {
715         return 0;
716     }
717 
718     part_length = _mdns_append_fqdn(packet, index, str, 4);
719     if (!part_length) {
720         return 0;
721     }
722     record_length += part_length;
723 
724     part_length = _mdns_append_type(packet, index, MDNS_ANSWER_TXT, flush, bye?0:MDNS_ANSWER_TXT_TTL);
725     if (!part_length) {
726         return 0;
727     }
728     record_length += part_length;
729 
730     uint16_t data_len_location = *index - 2;
731     uint16_t data_len = 0;
732 
733     mdns_txt_linked_item_t * txt = service->txt;
734     while (txt) {
735         int l = append_one_txt_record_entry(packet, index, txt);
736         if (l > 0) {
737             data_len += l;
738         } else if (l == 0) { // TXT entry won't fit into the mdns packet
739             return 0;
740         }
741         txt = txt->next;
742     }
743     if (!data_len) {
744         data_len = 1;
745         packet[*index] = 0;
746         *index = *index + 1;
747     }
748     _mdns_set_u16(packet, data_len_location, data_len);
749     record_length += data_len;
750     return record_length;
751 }
752 
753 /**
754  * @brief  appends SRV record for service to a packet, incrementing the index
755  *
756  * @param  packet       MDNS packet
757  * @param  index        offset in the packet
758  * @param  server       the server that is hosting the service
759  * @param  service      the service to add record for
760  *
761  * @return length of added data: 0 on error or length on success
762  */
_mdns_append_srv_record(uint8_t * packet,uint16_t * index,mdns_service_t * service,bool flush,bool bye)763 static uint16_t _mdns_append_srv_record(uint8_t * packet, uint16_t * index, mdns_service_t * service, bool flush, bool bye)
764 {
765     const char * str[4];
766     uint16_t record_length = 0;
767     uint8_t part_length;
768 
769     if (service == NULL) {
770         return 0;
771     }
772 
773     str[0] = _mdns_get_service_instance_name(service);
774     str[1] = service->service;
775     str[2] = service->proto;
776     str[3] = MDNS_DEFAULT_DOMAIN;
777 
778     if (!str[0]) {
779         return 0;
780     }
781 
782     part_length = _mdns_append_fqdn(packet, index, str, 4);
783     if (!part_length) {
784         return 0;
785     }
786     record_length += part_length;
787 
788     part_length = _mdns_append_type(packet, index, MDNS_ANSWER_SRV, flush, bye?0:MDNS_ANSWER_SRV_TTL);
789     if (!part_length) {
790         return 0;
791     }
792     record_length += part_length;
793 
794     uint16_t data_len_location = *index - 2;
795 
796     part_length = 0;
797     part_length += _mdns_append_u16(packet, index, service->priority);
798     part_length += _mdns_append_u16(packet, index, service->weight);
799     part_length += _mdns_append_u16(packet, index, service->port);
800     if (part_length != 6) {
801         return 0;
802     }
803 
804     if (service->hostname) {
805         str[0] = service->hostname;
806     } else {
807         str[0] = _mdns_server->hostname;
808     }
809     str[1] = MDNS_DEFAULT_DOMAIN;
810 
811     if (_str_null_or_empty(str[0])) {
812         return 0;
813     }
814 
815     part_length = _mdns_append_fqdn(packet, index, str, 2);
816     if (!part_length) {
817         return 0;
818     }
819     _mdns_set_u16(packet, data_len_location, part_length + 6);
820 
821     record_length += part_length + 6;
822     return record_length;
823 }
824 
825 /**
826  * @brief  appends A record to a packet, incrementing the index
827  *
828  * @param  packet       MDNS packet
829  * @param  index        offset in the packet
830  * @param  hostname     the hostname address to add
831  * @param  ip           the IP address to add
832  *
833  * @return length of added data: 0 on error or length on success
834  */
_mdns_append_a_record(uint8_t * packet,uint16_t * index,const char * hostname,uint32_t ip,bool flush,bool bye)835 static uint16_t _mdns_append_a_record(uint8_t * packet, uint16_t * index, const char * hostname, uint32_t ip, bool flush, bool bye)
836 {
837     const char * str[2];
838     uint16_t record_length = 0;
839     uint8_t part_length;
840 
841     str[0] = hostname;
842     str[1] = MDNS_DEFAULT_DOMAIN;
843 
844     if (_str_null_or_empty(str[0])) {
845         return 0;
846     }
847 
848     part_length = _mdns_append_fqdn(packet, index, str, 2);
849     if (!part_length) {
850         return 0;
851     }
852     record_length += part_length;
853 
854     part_length = _mdns_append_type(packet, index, MDNS_ANSWER_A, flush, bye?0:MDNS_ANSWER_A_TTL);
855     if (!part_length) {
856         return 0;
857     }
858     record_length += part_length;
859 
860     uint16_t data_len_location = *index - 2;
861 
862     if ((*index + 3) >= MDNS_MAX_PACKET_SIZE) {
863         return 0;
864     }
865     _mdns_append_u8(packet, index, ip & 0xFF);
866     _mdns_append_u8(packet, index, (ip >> 8) & 0xFF);
867     _mdns_append_u8(packet, index, (ip >> 16) & 0xFF);
868     _mdns_append_u8(packet, index, (ip >> 24) & 0xFF);
869     _mdns_set_u16(packet, data_len_location, 4);
870 
871     record_length += 4;
872     return record_length;
873 }
874 
875 #if CONFIG_LWIP_IPV6
876 /**
877  * @brief  appends AAAA record to a packet, incrementing the index
878  *
879  * @param  packet       MDNS packet
880  * @param  index        offset in the packet
881  * @param  hostname     the hostname address to add
882  * @param  ipv6         the IPv6 address to add
883  *
884  * @return length of added data: 0 on error or length on success
885  */
_mdns_append_aaaa_record(uint8_t * packet,uint16_t * index,const char * hostname,uint8_t * ipv6,bool flush,bool bye)886 static uint16_t _mdns_append_aaaa_record(uint8_t * packet, uint16_t * index, const char * hostname, uint8_t * ipv6, bool flush, bool bye)
887 {
888     const char * str[2];
889     uint16_t record_length = 0;
890     uint8_t part_length;
891 
892     str[0] = hostname;
893     str[1] = MDNS_DEFAULT_DOMAIN;
894 
895     if (_str_null_or_empty(str[0])) {
896         return 0;
897     }
898 
899 
900     part_length = _mdns_append_fqdn(packet, index, str, 2);
901     if (!part_length) {
902         return 0;
903     }
904     record_length += part_length;
905 
906     part_length = _mdns_append_type(packet, index, MDNS_ANSWER_AAAA, flush, bye?0:MDNS_ANSWER_AAAA_TTL);
907     if (!part_length) {
908         return 0;
909     }
910     record_length += part_length;
911 
912     uint16_t data_len_location = *index - 2;
913 
914     if ((*index + MDNS_ANSWER_AAAA_SIZE) > MDNS_MAX_PACKET_SIZE) {
915         return 0;
916     }
917 
918     part_length = MDNS_ANSWER_AAAA_SIZE;
919     memcpy(packet + *index, ipv6, part_length);
920     *index += part_length;
921     _mdns_set_u16(packet, data_len_location, part_length);
922     record_length += part_length;
923     return record_length;
924 }
925 #endif
926 
927 /**
928  * @brief  Append question to packet
929  */
_mdns_append_question(uint8_t * packet,uint16_t * index,mdns_out_question_t * q)930 static uint16_t _mdns_append_question(uint8_t * packet, uint16_t * index, mdns_out_question_t * q)
931 {
932     const char * str[4];
933     uint8_t str_index = 0;
934     uint8_t part_length;
935     if (q->host) {
936         str[str_index++] = q->host;
937     }
938     if (q->service) {
939         str[str_index++] = q->service;
940     }
941     if (q->proto) {
942         str[str_index++] = q->proto;
943     }
944     if (q->domain) {
945         str[str_index++] = q->domain;
946     }
947 
948     part_length = _mdns_append_fqdn(packet, index, str, str_index);
949     if (!part_length) {
950         return 0;
951     }
952 
953     part_length += _mdns_append_u16(packet, index, q->type);
954     part_length += _mdns_append_u16(packet, index, q->unicast?0x8001:0x0001);
955     return part_length;
956 }
957 
958 /**
959  * @brief  Helper to get either ETH or STA if the other is provided
960  *          Used when two interfaces are on the same subnet
961  */
_mdns_get_other_if(mdns_if_t tcpip_if)962 static mdns_if_t _mdns_get_other_if (mdns_if_t tcpip_if)
963 {
964     if (tcpip_if == MDNS_IF_STA) {
965         return MDNS_IF_ETH;
966     } else if (tcpip_if == MDNS_IF_ETH) {
967         return MDNS_IF_STA;
968     }
969     return MDNS_IF_MAX;
970 }
971 
972 /**
973  * @brief  Check if interface is duplicate (two interfaces on the same subnet)
974  */
_mdns_if_is_dup(mdns_if_t tcpip_if)975 static bool _mdns_if_is_dup(mdns_if_t tcpip_if)
976 {
977     mdns_if_t other_if = _mdns_get_other_if (tcpip_if);
978     if (other_if == MDNS_IF_MAX) {
979         return false;
980     }
981     if (_mdns_server->interfaces[tcpip_if].pcbs[MDNS_IP_PROTOCOL_V4].state == PCB_DUP
982         || _mdns_server->interfaces[tcpip_if].pcbs[MDNS_IP_PROTOCOL_V6].state == PCB_DUP
983         || _mdns_server->interfaces[other_if].pcbs[MDNS_IP_PROTOCOL_V4].state == PCB_DUP
984         || _mdns_server->interfaces[other_if].pcbs[MDNS_IP_PROTOCOL_V6].state == PCB_DUP
985     ) {
986         return true;
987     }
988     return false;
989 }
990 
991 #if CONFIG_LWIP_IPV6
992 /**
993  * @brief  Check if IPv6 address is NULL
994  */
_ipv6_address_is_zero(esp_ip6_addr_t ip6)995 static bool _ipv6_address_is_zero(esp_ip6_addr_t ip6)
996 {
997     uint8_t i;
998     uint8_t * data = (uint8_t *)ip6.addr;
999     for (i=0; i<_MDNS_SIZEOF_IP6_ADDR; i++) {
1000         if (data[i]) {
1001             return false;
1002         }
1003     }
1004     return true;
1005 }
1006 #endif
1007 
_mdns_append_host_answer(uint8_t * packet,uint16_t * index,mdns_host_item_t * host,uint8_t address_type,bool flush,bool bye)1008 static uint8_t _mdns_append_host_answer(uint8_t * packet, uint16_t * index, mdns_host_item_t * host,
1009                                         uint8_t address_type, bool flush, bool bye)
1010 {
1011     mdns_ip_addr_t * addr = host->address_list;
1012     uint8_t num_records = 0;
1013 
1014     while (addr != NULL) {
1015         if (addr->addr.type == address_type) {
1016             if (address_type == ESP_IPADDR_TYPE_V4 &&
1017                 _mdns_append_a_record(packet, index, host->hostname, addr->addr.u_addr.ip4.addr, flush, bye) <= 0) {
1018                 break;
1019             }
1020 #if CONFIG_LWIP_IPV6
1021             if (address_type == ESP_IPADDR_TYPE_V6 &&
1022                 _mdns_append_aaaa_record(packet, index, host->hostname, (uint8_t *)addr->addr.u_addr.ip6.addr, flush,
1023                                          bye) <= 0) {
1024                 break;
1025             }
1026 #endif // CONFIG_LWIP_IPV6
1027             num_records++;
1028         }
1029         addr = addr->next;
1030     }
1031     return num_records;
1032 }
1033 
1034 /**
1035  * @brief  Append answer to packet
1036  *
1037  *  @return number of answers added to the packet
1038  */
_mdns_append_answer(uint8_t * packet,uint16_t * index,mdns_out_answer_t * answer,mdns_if_t tcpip_if)1039 static uint8_t _mdns_append_answer(uint8_t * packet, uint16_t * index, mdns_out_answer_t * answer, mdns_if_t tcpip_if)
1040 {
1041     if (answer->type == MDNS_TYPE_PTR) {
1042 
1043         if (answer->service) {
1044             return _mdns_append_ptr_record(packet, index,
1045                 _mdns_get_service_instance_name(answer->service),
1046                 answer->service->service, answer->service->proto,
1047                 answer->flush, answer->bye) > 0;
1048         } else {
1049             return _mdns_append_ptr_record(packet, index,
1050                 answer->custom_instance, answer->custom_service, answer->custom_proto,
1051                 answer->flush, answer->bye) > 0;
1052         }
1053     } else if (answer->type == MDNS_TYPE_SRV) {
1054         return _mdns_append_srv_record(packet, index, answer->service, answer->flush, answer->bye) > 0;
1055     } else if (answer->type == MDNS_TYPE_TXT) {
1056         return _mdns_append_txt_record(packet, index, answer->service, answer->flush, answer->bye) > 0;
1057     } else if (answer->type == MDNS_TYPE_SDPTR) {
1058         return _mdns_append_sdptr_record(packet, index, answer->service, answer->flush, answer->bye) > 0;
1059     } else if (answer->type == MDNS_TYPE_A) {
1060         if (answer->host == &_mdns_self_host) {
1061             esp_netif_ip_info_t if_ip_info;
1062             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) {
1063                 return 0;
1064             }
1065             if (esp_netif_get_ip_info(_mdns_get_esp_netif(tcpip_if), &if_ip_info)) {
1066                 return 0;
1067             }
1068             if (_mdns_append_a_record(packet, index, _mdns_server->hostname, if_ip_info.ip.addr, answer->flush, answer->bye) <= 0) {
1069                 return 0;
1070             }
1071             if (!_mdns_if_is_dup(tcpip_if)) {
1072                 return 1;
1073             }
1074             mdns_if_t other_if = _mdns_get_other_if (tcpip_if);
1075             if (esp_netif_get_ip_info(_mdns_get_esp_netif(other_if), &if_ip_info)) {
1076                 return 1;
1077             }
1078             if (_mdns_append_a_record(packet, index, _mdns_server->hostname, if_ip_info.ip.addr, answer->flush, answer->bye) > 0) {
1079                 return 2;
1080             }
1081             return 1;
1082         } else if (answer->host != NULL) {
1083             return _mdns_append_host_answer(packet, index, answer->host, ESP_IPADDR_TYPE_V4, answer->flush, answer->bye);
1084         }
1085     }
1086 #if CONFIG_LWIP_IPV6
1087     else if (answer->type == MDNS_TYPE_AAAA) {
1088         if (answer->host == &_mdns_self_host) {
1089             struct esp_ip6_addr if_ip6;
1090             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) {
1091                 return 0;
1092             }
1093             if (esp_netif_get_ip6_linklocal(_mdns_get_esp_netif(tcpip_if), &if_ip6)) {
1094                 return 0;
1095             }
1096             if (_ipv6_address_is_zero(if_ip6)) {
1097                 return 0;
1098             }
1099             if (_mdns_append_aaaa_record(packet, index, _mdns_server->hostname, (uint8_t*)if_ip6.addr, answer->flush, answer->bye) <= 0) {
1100                 return 0;
1101             }
1102             if (!_mdns_if_is_dup(tcpip_if)) {
1103                 return 1;
1104             }
1105             mdns_if_t other_if = _mdns_get_other_if (tcpip_if);
1106             if (esp_netif_get_ip6_linklocal(_mdns_get_esp_netif(other_if), &if_ip6)) {
1107                 return 1;
1108             }
1109             if (_mdns_append_aaaa_record(packet, index, _mdns_server->hostname, (uint8_t*)if_ip6.addr, answer->flush, answer->bye) > 0) {
1110                 return 2;
1111             }
1112             return 1;
1113         } else if (answer->host != NULL) {
1114             return _mdns_append_host_answer(packet, index, answer->host, ESP_IPADDR_TYPE_V6, answer->flush, answer->bye);
1115         }
1116     }
1117 #endif
1118     return 0;
1119 }
1120 
1121 /**
1122  * @brief  sends a packet
1123  *
1124  * @param  p       the packet
1125  */
_mdns_dispatch_tx_packet(mdns_tx_packet_t * p)1126 static void _mdns_dispatch_tx_packet(mdns_tx_packet_t * p)
1127 {
1128     static uint8_t packet[MDNS_MAX_PACKET_SIZE];
1129     uint16_t index = MDNS_HEAD_LEN;
1130     memset(packet, 0, MDNS_HEAD_LEN);
1131     mdns_out_question_t * q;
1132     mdns_out_answer_t * a;
1133     uint8_t count;
1134 
1135     _mdns_set_u16(packet, MDNS_HEAD_FLAGS_OFFSET, p->flags);
1136     _mdns_set_u16(packet, MDNS_HEAD_ID_OFFSET, p->id);
1137 
1138     count = 0;
1139     q = p->questions;
1140     while (q) {
1141         if (_mdns_append_question(packet, &index, q)) {
1142             count++;
1143         }
1144         q = q->next;
1145     }
1146     _mdns_set_u16(packet, MDNS_HEAD_QUESTIONS_OFFSET, count);
1147 
1148     count = 0;
1149     a = p->answers;
1150     while (a) {
1151         count += _mdns_append_answer(packet, &index, a, p->tcpip_if);
1152         a = a->next;
1153     }
1154     _mdns_set_u16(packet, MDNS_HEAD_ANSWERS_OFFSET, count);
1155 
1156     count = 0;
1157     a = p->servers;
1158     while (a) {
1159         count += _mdns_append_answer(packet, &index, a, p->tcpip_if);
1160         a = a->next;
1161     }
1162     _mdns_set_u16(packet, MDNS_HEAD_SERVERS_OFFSET, count);
1163 
1164     count = 0;
1165     a = p->additional;
1166     while (a) {
1167         count += _mdns_append_answer(packet, &index, a, p->tcpip_if);
1168         a = a->next;
1169     }
1170     _mdns_set_u16(packet, MDNS_HEAD_ADDITIONAL_OFFSET, count);
1171 
1172 #ifdef MDNS_ENABLE_DEBUG
1173     _mdns_dbg_printf("\nTX[%u][%u]: ", p->tcpip_if, p->ip_protocol);
1174     if (p->dst.type == ESP_IPADDR_TYPE_V4) {
1175         _mdns_dbg_printf("To: " IPSTR ":%u, ", IP2STR(&p->dst.u_addr.ip4), p->port);
1176     } else {
1177         _mdns_dbg_printf("To: " IPV6STR ":%u, ", IPV62STR(p->dst.u_addr.ip6), p->port);
1178     }
1179     mdns_debug_packet(packet, index);
1180 #endif
1181 
1182     _mdns_udp_pcb_write(p->tcpip_if, p->ip_protocol, &p->dst, p->port, packet, index);
1183 }
1184 
1185 /**
1186  * @brief  frees a packet
1187  *
1188  * @param  packet       the packet
1189  */
_mdns_free_tx_packet(mdns_tx_packet_t * packet)1190 static void _mdns_free_tx_packet(mdns_tx_packet_t * packet)
1191 {
1192     if (!packet) {
1193         return;
1194     }
1195     mdns_out_question_t * q = packet->questions;
1196     while (q) {
1197         mdns_out_question_t * next = q->next;
1198         if (q->own_dynamic_memory) {
1199             free((char *)q->host);
1200             free((char *)q->service);
1201             free((char *)q->proto);
1202             free((char *)q->domain);
1203         }
1204         free(q);
1205         q = next;
1206     }
1207     queueFree(mdns_out_answer_t, packet->answers);
1208     queueFree(mdns_out_answer_t, packet->servers);
1209     queueFree(mdns_out_answer_t, packet->additional);
1210     free(packet);
1211 }
1212 
1213 /**
1214  * @brief  schedules a packet to be sent after given milliseconds
1215  *
1216  * @param  packet       the packet
1217  * @param  ms_after     number of milliseconds after which the packet should be dispatched
1218  */
_mdns_schedule_tx_packet(mdns_tx_packet_t * packet,uint32_t ms_after)1219 static void _mdns_schedule_tx_packet(mdns_tx_packet_t * packet, uint32_t ms_after)
1220 {
1221     if (!packet) {
1222         return;
1223     }
1224     packet->send_at = (xTaskGetTickCount() * portTICK_PERIOD_MS) + ms_after;
1225     packet->next = NULL;
1226     if (!_mdns_server->tx_queue_head || _mdns_server->tx_queue_head->send_at > packet->send_at) {
1227         packet->next = _mdns_server->tx_queue_head;
1228         _mdns_server->tx_queue_head = packet;
1229         return;
1230     }
1231     mdns_tx_packet_t * q = _mdns_server->tx_queue_head;
1232     while (q->next && q->next->send_at <= packet->send_at) {
1233         q = q->next;
1234     }
1235     packet->next = q->next;
1236     q->next = packet;
1237 }
1238 
1239 /**
1240  * @brief  free all packets scheduled for sending
1241  */
_mdns_clear_tx_queue_head(void)1242 static void _mdns_clear_tx_queue_head(void)
1243 {
1244     mdns_tx_packet_t * q;
1245     while (_mdns_server->tx_queue_head) {
1246         q = _mdns_server->tx_queue_head;
1247         _mdns_server->tx_queue_head = _mdns_server->tx_queue_head->next;
1248         _mdns_free_tx_packet(q);
1249     }
1250 }
1251 
1252 /**
1253  * @brief  clear packets scheduled for sending on a specific interface
1254  *
1255  * @param  tcpip_if     the interface
1256  * @param  ip_protocol     pcb type V4/V6
1257  */
_mdns_clear_pcb_tx_queue_head(mdns_if_t tcpip_if,mdns_ip_protocol_t ip_protocol)1258 static void _mdns_clear_pcb_tx_queue_head(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
1259 {
1260     mdns_tx_packet_t * q, * p;
1261     while (_mdns_server->tx_queue_head && _mdns_server->tx_queue_head->tcpip_if == tcpip_if && _mdns_server->tx_queue_head->ip_protocol == ip_protocol) {
1262         q = _mdns_server->tx_queue_head;
1263         _mdns_server->tx_queue_head = _mdns_server->tx_queue_head->next;
1264         _mdns_free_tx_packet(q);
1265     }
1266     if (_mdns_server->tx_queue_head) {
1267         q = _mdns_server->tx_queue_head;
1268         while (q->next) {
1269             if (q->next->tcpip_if == tcpip_if && q->next->ip_protocol == ip_protocol) {
1270                 p = q->next;
1271                 q->next = p->next;
1272                 _mdns_free_tx_packet(p);
1273             } else {
1274                 q = q->next;
1275             }
1276         }
1277     }
1278 }
1279 
1280 /**
1281  * @brief  get the next packet scheduled for sending on a specific interface
1282  *
1283  * @param  tcpip_if     the interface
1284  * @param  ip_protocol     pcb type V4/V6
1285  */
_mdns_get_next_pcb_packet(mdns_if_t tcpip_if,mdns_ip_protocol_t ip_protocol)1286 static mdns_tx_packet_t * _mdns_get_next_pcb_packet(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
1287 {
1288     mdns_tx_packet_t * q = _mdns_server->tx_queue_head;
1289     while (q) {
1290         if (q->tcpip_if == tcpip_if && q->ip_protocol == ip_protocol) {
1291             return q;
1292         }
1293         q = q->next;
1294     }
1295     return NULL;
1296 }
1297 
1298 /**
1299  * @brief  Find, remove and free answer from the scheduled packets
1300  */
_mdns_remove_scheduled_answer(mdns_if_t tcpip_if,mdns_ip_protocol_t ip_protocol,uint16_t type,mdns_srv_item_t * service)1301 static void _mdns_remove_scheduled_answer(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, uint16_t type, mdns_srv_item_t * service)
1302 {
1303     mdns_srv_item_t s = {NULL, NULL};
1304     if (!service) {
1305         service = &s;
1306     }
1307     mdns_tx_packet_t * q = _mdns_server->tx_queue_head;
1308     while (q) {
1309         if (q->tcpip_if == tcpip_if && q->ip_protocol == ip_protocol && q->distributed) {
1310             mdns_out_answer_t * a = q->answers;
1311             if (a->type == type && a->service == service->service) {
1312                 q->answers = q->answers->next;
1313                 free(a);
1314             } else {
1315                 while (a->next) {
1316                     if (a->next->type == type && a->next->service == service->service) {
1317                         mdns_out_answer_t * b = a->next;
1318                         a->next = b->next;
1319                         free(b);
1320                         break;
1321                     }
1322                     a = a->next;
1323                 }
1324             }
1325         }
1326         q = q->next;
1327     }
1328 }
1329 
1330 /**
1331  * @brief  Remove and free answer from answer list (destination)
1332  */
_mdns_dealloc_answer(mdns_out_answer_t ** destination,uint16_t type,mdns_srv_item_t * service)1333 static void _mdns_dealloc_answer(mdns_out_answer_t ** destination, uint16_t type, mdns_srv_item_t * service)
1334 {
1335     mdns_out_answer_t * d = *destination;
1336     if (!d) {
1337         return;
1338     }
1339     mdns_srv_item_t s = {NULL, NULL};
1340     if (!service) {
1341         service = &s;
1342     }
1343     if (d->type == type && d->service == service->service) {
1344         *destination = d->next;
1345         free(d);
1346         return;
1347     }
1348     while (d->next) {
1349         mdns_out_answer_t * a = d->next;
1350         if (a->type == type && a->service == service->service) {
1351             d->next = a->next;
1352             free(a);
1353             return;
1354         }
1355         d = d->next;
1356     }
1357 }
1358 
1359 /**
1360  * @brief  Allocate new answer and add it to answer list (destination)
1361  */
_mdns_alloc_answer(mdns_out_answer_t ** destination,uint16_t type,mdns_service_t * service,mdns_host_item_t * host,bool flush,bool bye)1362 static bool _mdns_alloc_answer(mdns_out_answer_t ** destination, uint16_t type, mdns_service_t * service,
1363                                mdns_host_item_t * host, bool flush, bool bye)
1364 {
1365     mdns_out_answer_t * d = *destination;
1366     while (d) {
1367         if (d->type == type && d->service == service && d->host == host) {
1368             return true;
1369         }
1370         d = d->next;
1371     }
1372 
1373     mdns_out_answer_t * a = (mdns_out_answer_t *)malloc(sizeof(mdns_out_answer_t));
1374     if (!a) {
1375         HOOK_MALLOC_FAILED;
1376         return false;
1377     }
1378     a->type = type;
1379     a->service = service;
1380     a->host = host;
1381     a->custom_service = NULL;
1382     a->bye = bye;
1383     a->flush = flush;
1384     a->next = NULL;
1385     queueToEnd(mdns_out_answer_t, *destination, a);
1386     return true;
1387 }
1388 
1389 /**
1390  * @brief  Allocate new packet for sending
1391  */
_mdns_alloc_packet_default(mdns_if_t tcpip_if,mdns_ip_protocol_t ip_protocol)1392 static mdns_tx_packet_t * _mdns_alloc_packet_default(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
1393 {
1394     mdns_tx_packet_t * packet = (mdns_tx_packet_t*)malloc(sizeof(mdns_tx_packet_t));
1395     if (!packet) {
1396         HOOK_MALLOC_FAILED;
1397         return NULL;
1398     }
1399     memset((uint8_t*)packet, 0, sizeof(mdns_tx_packet_t));
1400     packet->tcpip_if = tcpip_if;
1401     packet->ip_protocol = ip_protocol;
1402     packet->port = MDNS_SERVICE_PORT;
1403     if (ip_protocol == MDNS_IP_PROTOCOL_V4) {
1404         esp_ip_addr_t addr = ESP_IP4ADDR_INIT(224, 0, 0, 251);
1405         memcpy(&packet->dst, &addr, sizeof(esp_ip_addr_t));
1406     }
1407 #if CONFIG_LWIP_IPV6
1408     else {
1409         esp_ip_addr_t addr = ESP_IP6ADDR_INIT(0x000002ff, 0, 0, 0xfb000000);
1410         memcpy(&packet->dst, &addr, sizeof(esp_ip_addr_t));
1411     }
1412 #endif
1413     return packet;
1414 }
1415 
_mdns_create_answer_from_service(mdns_tx_packet_t * packet,mdns_service_t * service,mdns_parsed_question_t * question,bool shared,bool send_flush)1416 static bool _mdns_create_answer_from_service(mdns_tx_packet_t * packet, mdns_service_t * service,
1417                                              mdns_parsed_question_t * question, bool shared, bool send_flush)
1418 {
1419     mdns_host_item_t * host = mdns_get_host_item(service->hostname);
1420     if (question->type == MDNS_TYPE_PTR || question->type == MDNS_TYPE_ANY) {
1421         if (!_mdns_alloc_answer(&packet->answers, MDNS_TYPE_PTR, service, NULL, false, false) ||
1422             !_mdns_alloc_answer(&packet->answers, MDNS_TYPE_SRV, service, NULL, send_flush, false) ||
1423             !_mdns_alloc_answer(&packet->answers, MDNS_TYPE_TXT, service, NULL, send_flush, false) ||
1424             !_mdns_alloc_answer(shared ? &packet->additional : &packet->answers, MDNS_TYPE_A, service, host, send_flush,
1425                                 false) ||
1426             !_mdns_alloc_answer(shared ? &packet->additional : &packet->answers, MDNS_TYPE_AAAA, service, host,
1427                                 send_flush, false)) {
1428             return false;
1429         }
1430     } else if (question->type == MDNS_TYPE_SRV) {
1431         if (!_mdns_alloc_answer(&packet->answers, MDNS_TYPE_SRV, service, NULL, send_flush, false) ||
1432             !_mdns_alloc_answer(&packet->additional, MDNS_TYPE_A, service, host, send_flush, false) ||
1433             !_mdns_alloc_answer(&packet->additional, MDNS_TYPE_AAAA, service, host, send_flush, false)) {
1434             return false;
1435         }
1436     } else if (question->type == MDNS_TYPE_TXT) {
1437         if (!_mdns_alloc_answer(&packet->answers, MDNS_TYPE_TXT, service, NULL, send_flush, false)) {
1438             return false;
1439         }
1440     } else if (question->type == MDNS_TYPE_SDPTR) {
1441         shared = true;
1442         if (!_mdns_alloc_answer(&packet->answers, MDNS_TYPE_SDPTR, service, NULL, false, false)) {
1443             return false;
1444         }
1445     }
1446     return true;
1447 }
1448 
_mdns_create_answer_from_hostname(mdns_tx_packet_t * packet,const char * hostname,bool send_flush)1449 static bool _mdns_create_answer_from_hostname(mdns_tx_packet_t * packet, const char * hostname, bool send_flush)
1450 {
1451     mdns_host_item_t * host = mdns_get_host_item(hostname);
1452     if (!_mdns_alloc_answer(&packet->answers, MDNS_TYPE_A, NULL, host, send_flush, false) ||
1453         !_mdns_alloc_answer(&packet->answers, MDNS_TYPE_AAAA, NULL, host, send_flush, false)) {
1454         return false;
1455     }
1456     return true;
1457 }
1458 
1459 /**
1460  * @brief  Create answer packet to questions from parsed packet
1461  */
_mdns_create_answer_from_parsed_packet(mdns_parsed_packet_t * parsed_packet)1462 static void _mdns_create_answer_from_parsed_packet(mdns_parsed_packet_t *parsed_packet)
1463 {
1464     if (!parsed_packet->questions) {
1465         return;
1466     }
1467     bool send_flush = parsed_packet->src_port == MDNS_SERVICE_PORT;
1468     bool unicast = false;
1469     bool shared = false;
1470     mdns_tx_packet_t *packet = _mdns_alloc_packet_default(parsed_packet->tcpip_if, parsed_packet->ip_protocol);
1471     if (!packet) {
1472         return;
1473     }
1474     packet->flags = MDNS_FLAGS_AUTHORITATIVE;
1475     packet->distributed = parsed_packet->distributed;
1476     packet->id = parsed_packet->id;
1477 
1478     mdns_parsed_question_t *q = parsed_packet->questions;
1479     while (q) {
1480         shared = q->type == MDNS_TYPE_PTR || q->type == MDNS_TYPE_SDPTR || !parsed_packet->probe;
1481         if (q->type == MDNS_TYPE_SRV || q->type == MDNS_TYPE_TXT) {
1482             mdns_srv_item_t *service = _mdns_get_service_item_instance(q->host, q->service, q->proto, NULL);
1483             if (!_mdns_create_answer_from_service(packet, service->service, q, shared, send_flush)) {
1484                 _mdns_free_tx_packet(packet);
1485                 return;
1486             }
1487         } else if (q->service && q->proto) {
1488             mdns_srv_item_t *service = _mdns_server->services;
1489             while (service) {
1490                 if (_mdns_service_match(service->service, q->service, q->proto, NULL)) {
1491                     if (!_mdns_create_answer_from_service(packet, service->service, q, shared, send_flush)) {
1492                         _mdns_free_tx_packet(packet);
1493                         return;
1494                     }
1495                 }
1496                 service = service->next;
1497             }
1498         } else if (q->type == MDNS_TYPE_A || q->type == MDNS_TYPE_AAAA) {
1499             if (!_mdns_create_answer_from_hostname(packet, q->host, send_flush)) {
1500                 _mdns_free_tx_packet(packet);
1501                 return;
1502             }
1503         } else if (q->type == MDNS_TYPE_ANY) {
1504             if (!_mdns_append_host_list(&packet->answers, send_flush, false)) {
1505                 _mdns_free_tx_packet(packet);
1506                 return;
1507             }
1508         } else if (!_mdns_alloc_answer(&packet->answers, q->type, NULL, NULL, send_flush, false)) {
1509             _mdns_free_tx_packet(packet);
1510             return;
1511         }
1512 
1513 #ifdef MDNS_REPEAT_QUERY_IN_RESPONSE
1514         if (parsed_packet->src_port != MDNS_SERVICE_PORT &&  // Repeat the queries only for "One-Shot mDNS queries"
1515            (q->type == MDNS_TYPE_ANY || q->type == MDNS_TYPE_A || q->type == MDNS_TYPE_AAAA)) {
1516             mdns_out_question_t * out_question = malloc(sizeof(mdns_out_question_t));
1517             if (out_question == NULL) {
1518                 HOOK_MALLOC_FAILED;
1519                 _mdns_free_tx_packet(packet);
1520                 return;
1521             }
1522             out_question->type = q->type;
1523             out_question->unicast = q->unicast;
1524             out_question->host = q->host;
1525             q->host = NULL;
1526             out_question->service = q->service;
1527             q->service = NULL;
1528             out_question->proto = q->proto;
1529             q->proto = NULL;
1530             out_question->domain = q->domain;
1531             q->domain = NULL;
1532             out_question->next = NULL;
1533             out_question->own_dynamic_memory = true;
1534             queueToEnd(mdns_out_question_t, packet->questions, out_question);
1535         }
1536 #endif // MDNS_REPEAT_QUERY_IN_RESPONSE
1537         if (q->unicast) {
1538             unicast = true;
1539         }
1540         q = q->next;
1541     }
1542     if (unicast || !send_flush) {
1543         memcpy(&packet->dst, &parsed_packet->src, sizeof(esp_ip_addr_t));
1544         packet->port = parsed_packet->src_port;
1545     }
1546 
1547     static uint8_t share_step = 0;
1548     if (shared) {
1549         _mdns_schedule_tx_packet(packet, 25 + (share_step * 25));
1550         share_step = (share_step + 1) & 0x03;
1551     } else {
1552         _mdns_dispatch_tx_packet(packet);
1553         _mdns_free_tx_packet(packet);
1554     }
1555 }
1556 
1557 /**
1558  * @brief  Check if question is already in the list
1559  */
_mdns_question_exists(mdns_out_question_t * needle,mdns_out_question_t * haystack)1560 static bool _mdns_question_exists(mdns_out_question_t * needle, mdns_out_question_t * haystack)
1561 {
1562     while (haystack) {
1563         if (haystack->type == needle->type
1564             && haystack->host == needle->host
1565             && haystack->service == needle->service
1566             && haystack->proto == needle->proto) {
1567             return true;
1568         }
1569         haystack = haystack->next;
1570     }
1571     return false;
1572 }
1573 
_mdns_append_host(mdns_out_answer_t ** destination,mdns_host_item_t * host,bool flush,bool bye)1574 static bool _mdns_append_host(mdns_out_answer_t ** destination, mdns_host_item_t * host, bool flush, bool bye)
1575 {
1576     if (!_mdns_alloc_answer(destination, MDNS_TYPE_A, NULL, host, flush, bye)) {
1577         return false;
1578     }
1579     if (!_mdns_alloc_answer(destination, MDNS_TYPE_AAAA, NULL, host, flush, bye)) {
1580         return false;
1581     }
1582     return true;
1583 }
1584 
_mdns_append_host_list_in_services(mdns_out_answer_t ** destination,mdns_srv_item_t * services[],size_t services_len,bool flush,bool bye)1585 static bool _mdns_append_host_list_in_services(mdns_out_answer_t ** destination, mdns_srv_item_t * services[],
1586                                                size_t services_len, bool flush, bool bye)
1587 {
1588     if (services == NULL) {
1589         mdns_host_item_t * host = mdns_get_host_item(_mdns_server->hostname);
1590         if (host != NULL) {
1591             return _mdns_append_host(destination, host, flush, bye);
1592         }
1593         return true;
1594     }
1595     for (size_t i = 0; i < services_len; i++) {
1596         mdns_host_item_t *host = mdns_get_host_item(services[i]->service->hostname);
1597         if (!_mdns_append_host(destination, host, flush, bye)) {
1598             return false;
1599         }
1600     }
1601     return true;
1602 }
1603 
_mdns_append_host_list(mdns_out_answer_t ** destination,bool flush,bool bye)1604 static bool _mdns_append_host_list(mdns_out_answer_t ** destination, bool flush, bool bye)
1605 {
1606     if (!_str_null_or_empty(_mdns_server->hostname)) {
1607         mdns_host_item_t * self_host = mdns_get_host_item(_mdns_server->hostname);
1608         if (!_mdns_append_host(destination, self_host, flush, bye)) {
1609             return false;
1610         }
1611     }
1612     mdns_host_item_t * host = _mdns_host_list;
1613     while (host != NULL) {
1614         host = host->next;
1615         if (!_mdns_append_host(destination, host, flush, bye)) {
1616             return false;
1617         }
1618     }
1619     return true;
1620 }
1621 
_mdns_append_host_question(mdns_out_question_t ** questions,const char * hostname,bool unicast)1622 static bool _mdns_append_host_question(mdns_out_question_t **questions, const char *hostname, bool unicast)
1623 {
1624     mdns_out_question_t *q = (mdns_out_question_t *)malloc(sizeof(mdns_out_question_t));
1625     if (!q) {
1626         HOOK_MALLOC_FAILED;
1627         return false;
1628     }
1629     q->next = NULL;
1630     q->unicast = unicast;
1631     q->type = MDNS_TYPE_ANY;
1632     q->host = hostname;
1633     q->service = NULL;
1634     q->proto = NULL;
1635     q->domain = MDNS_DEFAULT_DOMAIN;
1636     q->own_dynamic_memory = false;
1637     if (_mdns_question_exists(q, *questions)) {
1638         free(q);
1639     } else {
1640         queueToEnd(mdns_out_question_t, *questions, q);
1641     }
1642     return true;
1643 }
1644 
_mdns_append_host_questions_for_services(mdns_out_question_t ** questions,mdns_srv_item_t * services[],size_t len,bool unicast)1645 static bool _mdns_append_host_questions_for_services(mdns_out_question_t **questions, mdns_srv_item_t *services[],
1646                                                      size_t len, bool unicast)
1647 {
1648     if (!_str_null_or_empty(_mdns_server->hostname) &&
1649         !_mdns_append_host_question(questions, _mdns_server->hostname, unicast)) {
1650         return false;
1651     }
1652     for (size_t i = 0; i < len; i++) {
1653         if (!_mdns_append_host_question(questions, services[i]->service->hostname, unicast)) {
1654             return false;
1655         }
1656     }
1657     return true;
1658 }
1659 
1660 /**
1661  * @brief  Create probe packet for particular services on particular PCB
1662  */
_mdns_create_probe_packet(mdns_if_t tcpip_if,mdns_ip_protocol_t ip_protocol,mdns_srv_item_t * services[],size_t len,bool first,bool include_ip)1663 static mdns_tx_packet_t * _mdns_create_probe_packet(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, mdns_srv_item_t * services[], size_t len, bool first, bool include_ip)
1664 {
1665     mdns_tx_packet_t * packet = _mdns_alloc_packet_default(tcpip_if, ip_protocol);
1666     if (!packet) {
1667         return NULL;
1668     }
1669 
1670     size_t i;
1671     for (i=0; i<len; i++) {
1672         mdns_out_question_t * q = (mdns_out_question_t *)malloc(sizeof(mdns_out_question_t));
1673         if (!q) {
1674             HOOK_MALLOC_FAILED;
1675             _mdns_free_tx_packet(packet);
1676             return NULL;
1677         }
1678         q->next = NULL;
1679         q->unicast = first;
1680         q->type = MDNS_TYPE_ANY;
1681         q->host = _mdns_get_service_instance_name(services[i]->service);
1682         q->service = services[i]->service->service;
1683         q->proto = services[i]->service->proto;
1684         q->domain = MDNS_DEFAULT_DOMAIN;
1685         q->own_dynamic_memory = false;
1686         if (!q->host || _mdns_question_exists(q, packet->questions)) {
1687             free(q);
1688             continue;
1689         } else {
1690             queueToEnd(mdns_out_question_t, packet->questions, q);
1691         }
1692 
1693         if (!q->host || !_mdns_alloc_answer(&packet->servers, MDNS_TYPE_SRV, services[i]->service, NULL, false, false)) {
1694             _mdns_free_tx_packet(packet);
1695             return NULL;
1696         }
1697     }
1698 
1699     if (include_ip) {
1700         if (!_mdns_append_host_questions_for_services(&packet->questions, services, len, first)) {
1701             _mdns_free_tx_packet(packet);
1702             return NULL;
1703         }
1704 
1705         if (!_mdns_append_host_list_in_services(&packet->servers, services, len, false, false)) {
1706             _mdns_free_tx_packet(packet);
1707             return NULL;
1708         }
1709     }
1710 
1711     return packet;
1712 }
1713 
1714 /**
1715  * @brief  Create announce packet for particular services on particular PCB
1716  */
_mdns_create_announce_packet(mdns_if_t tcpip_if,mdns_ip_protocol_t ip_protocol,mdns_srv_item_t * services[],size_t len,bool include_ip)1717 static mdns_tx_packet_t * _mdns_create_announce_packet(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, mdns_srv_item_t * services[], size_t len, bool include_ip)
1718 {
1719     mdns_tx_packet_t * packet = _mdns_alloc_packet_default(tcpip_if, ip_protocol);
1720     if (!packet) {
1721         return NULL;
1722     }
1723     packet->flags = MDNS_FLAGS_AUTHORITATIVE;
1724 
1725     uint8_t i;
1726     for (i=0; i<len; i++) {
1727         if (!_mdns_alloc_answer(&packet->answers, MDNS_TYPE_SDPTR, services[i]->service, NULL, false, false)
1728                 || !_mdns_alloc_answer(&packet->answers, MDNS_TYPE_PTR, services[i]->service, NULL, false, false)
1729                 || !_mdns_alloc_answer(&packet->answers, MDNS_TYPE_SRV, services[i]->service, NULL, true, false)
1730                 || !_mdns_alloc_answer(&packet->answers, MDNS_TYPE_TXT, services[i]->service, NULL, true, false)) {
1731             _mdns_free_tx_packet(packet);
1732             return NULL;
1733         }
1734     }
1735     if (include_ip) {
1736         if (!_mdns_append_host_list_in_services(&packet->servers, services, len, true, false)) {
1737             _mdns_free_tx_packet(packet);
1738             return NULL;
1739         }
1740     }
1741     return packet;
1742 }
1743 
1744 /**
1745  * @brief  Convert probe packet to announce
1746  */
_mdns_create_announce_from_probe(mdns_tx_packet_t * probe)1747 static mdns_tx_packet_t * _mdns_create_announce_from_probe(mdns_tx_packet_t * probe)
1748 {
1749 
1750     mdns_tx_packet_t * packet = _mdns_alloc_packet_default(probe->tcpip_if, probe->ip_protocol);
1751     if (!packet) {
1752         return NULL;
1753     }
1754     packet->flags = MDNS_FLAGS_AUTHORITATIVE;
1755 
1756     mdns_out_answer_t * s = probe->servers;
1757     while (s) {
1758         if (s->type == MDNS_TYPE_SRV) {
1759             if (!_mdns_alloc_answer(&packet->answers, MDNS_TYPE_SDPTR, s->service, NULL, false, false)
1760                     || !_mdns_alloc_answer(&packet->answers, MDNS_TYPE_PTR, s->service, NULL, false, false)
1761                     || !_mdns_alloc_answer(&packet->answers, MDNS_TYPE_SRV, s->service, NULL, true, false)
1762                     || !_mdns_alloc_answer(&packet->answers, MDNS_TYPE_TXT, s->service, NULL, true, false)) {
1763                 _mdns_free_tx_packet(packet);
1764                 return NULL;
1765             }
1766 
1767         } else if (s->type == MDNS_TYPE_A || s->type == MDNS_TYPE_AAAA) {
1768             if (!_mdns_alloc_answer(&packet->answers, s->type, NULL, s->host, true, false)) {
1769                 _mdns_free_tx_packet(packet);
1770                 return NULL;
1771             }
1772         }
1773 
1774         s = s->next;
1775     }
1776     return packet;
1777 }
1778 
1779 /**
1780  * @brief  Send by for particular services on particular PCB
1781  */
_mdns_pcb_send_bye(mdns_if_t tcpip_if,mdns_ip_protocol_t ip_protocol,mdns_srv_item_t ** services,size_t len,bool include_ip)1782 static void _mdns_pcb_send_bye(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, mdns_srv_item_t ** services, size_t len, bool include_ip)
1783 {
1784     mdns_tx_packet_t * packet = _mdns_alloc_packet_default(tcpip_if, ip_protocol);
1785     if (!packet) {
1786         return;
1787     }
1788     packet->flags = MDNS_FLAGS_AUTHORITATIVE;
1789     size_t i;
1790     for (i=0; i<len; i++) {
1791         if (!_mdns_alloc_answer(&packet->answers, MDNS_TYPE_PTR, services[i]->service, NULL, true, true)) {
1792             _mdns_free_tx_packet(packet);
1793             return;
1794         }
1795     }
1796     if (include_ip) {
1797         _mdns_append_host_list_in_services(&packet->answers, services, len, true, true);
1798     }
1799     _mdns_dispatch_tx_packet(packet);
1800     _mdns_free_tx_packet(packet);
1801 }
1802 
1803 /**
1804  * @brief  Send probe for additional services on particular PCB
1805  */
_mdns_init_pcb_probe_new_service(mdns_if_t tcpip_if,mdns_ip_protocol_t ip_protocol,mdns_srv_item_t ** services,size_t len,bool probe_ip)1806 static void _mdns_init_pcb_probe_new_service(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, mdns_srv_item_t ** services, size_t len, bool probe_ip)
1807 {
1808     mdns_pcb_t * pcb = &_mdns_server->interfaces[tcpip_if].pcbs[ip_protocol];
1809     size_t services_final_len = len;
1810 
1811     if (PCB_STATE_IS_PROBING(pcb)) {
1812         services_final_len += pcb->probe_services_len;
1813     }
1814     mdns_srv_item_t ** _services = NULL;
1815     if (services_final_len) {
1816         _services = (mdns_srv_item_t **)malloc(sizeof(mdns_srv_item_t *) * services_final_len);
1817         if (!_services) {
1818             HOOK_MALLOC_FAILED;
1819             return;
1820         }
1821 
1822         size_t i;
1823         for (i=0; i<len; i++) {
1824             _services[i] = services[i];
1825         }
1826         if (pcb->probe_services) {
1827             for (i=0; i<pcb->probe_services_len; i++) {
1828                 _services[len+i] = pcb->probe_services[i];
1829             }
1830             free(pcb->probe_services);
1831         }
1832     }
1833 
1834     probe_ip = pcb->probe_ip || probe_ip;
1835 
1836     pcb->probe_ip = false;
1837     pcb->probe_services = NULL;
1838     pcb->probe_services_len = 0;
1839     pcb->probe_running = false;
1840 
1841     mdns_tx_packet_t * packet = _mdns_create_probe_packet(tcpip_if, ip_protocol, _services, services_final_len, true, probe_ip);
1842     if (!packet) {
1843         free(_services);
1844         return;
1845     }
1846 
1847     pcb->probe_ip = probe_ip;
1848     pcb->probe_services = _services;
1849     pcb->probe_services_len = services_final_len;
1850     pcb->probe_running = true;
1851     _mdns_schedule_tx_packet(packet, ((pcb->failed_probes > 5)?1000:120) + (esp_random() & 0x7F));
1852     pcb->state = PCB_PROBE_1;
1853 }
1854 
1855 /**
1856  * @brief  Send probe for particular services on particular PCB
1857  *
1858  * Tests possible duplication on probing service structure and probes only for new entries.
1859  * - If pcb probing then add only non-probing services and restarts probing
1860  * - If pcb not probing, run probing for all specified services
1861  */
_mdns_init_pcb_probe(mdns_if_t tcpip_if,mdns_ip_protocol_t ip_protocol,mdns_srv_item_t ** services,size_t len,bool probe_ip)1862 static void _mdns_init_pcb_probe(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, mdns_srv_item_t ** services, size_t len, bool probe_ip)
1863 {
1864     mdns_pcb_t * pcb = &_mdns_server->interfaces[tcpip_if].pcbs[ip_protocol];
1865 
1866     _mdns_clear_pcb_tx_queue_head(tcpip_if, ip_protocol);
1867 
1868     if (_str_null_or_empty(_mdns_server->hostname)) {
1869         pcb->state = PCB_RUNNING;
1870         return;
1871     }
1872 
1873     if (PCB_STATE_IS_PROBING(pcb)) {
1874         // Looking for already probing services to resolve duplications
1875         mdns_srv_item_t * new_probe_services[len];
1876         int new_probe_service_len = 0;
1877         bool found;
1878         for (size_t j=0; j < len; ++j) {
1879             found = false;
1880             for (int i=0; i < pcb->probe_services_len; ++i) {
1881                 if (pcb->probe_services[i] == services[j]) {
1882                     found = true;
1883                     break;
1884                 }
1885             }
1886             if (!found) {
1887                 new_probe_services[new_probe_service_len++] = services[j];
1888             }
1889         }
1890         // init probing for newly added services
1891         _mdns_init_pcb_probe_new_service(tcpip_if, ip_protocol,
1892                                          new_probe_service_len?new_probe_services:NULL, new_probe_service_len, probe_ip);
1893     } else {
1894         // not probing, so init for all services
1895         _mdns_init_pcb_probe_new_service(tcpip_if, ip_protocol, services, len, probe_ip);
1896     }
1897 }
1898 
1899 /**
1900  * @brief  Restart the responder on particular PCB
1901  */
_mdns_restart_pcb(mdns_if_t tcpip_if,mdns_ip_protocol_t ip_protocol)1902 static void _mdns_restart_pcb(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
1903 {
1904     size_t srv_count = 0;
1905     mdns_srv_item_t * a = _mdns_server->services;
1906     while (a) {
1907         srv_count++;
1908         a = a->next;
1909     }
1910     mdns_srv_item_t * services[srv_count];
1911     size_t i = 0;
1912     a = _mdns_server->services;
1913     while (a) {
1914         services[i++] = a;
1915         a = a->next;
1916     }
1917     _mdns_init_pcb_probe(tcpip_if, ip_protocol, services, srv_count, true);
1918 }
1919 
1920 /**
1921  * @brief  Send by for particular services
1922  */
_mdns_send_bye(mdns_srv_item_t ** services,size_t len,bool include_ip)1923 static void _mdns_send_bye(mdns_srv_item_t ** services, size_t len, bool include_ip)
1924 {
1925     uint8_t i, j;
1926     if (_str_null_or_empty(_mdns_server->hostname)) {
1927         return;
1928     }
1929 
1930     for (i=0; i<MDNS_IF_MAX; i++) {
1931         for (j=0; j<MDNS_IP_PROTOCOL_MAX; j++) {
1932             if (_mdns_server->interfaces[i].pcbs[j].pcb && _mdns_server->interfaces[i].pcbs[j].state == PCB_RUNNING) {
1933                 _mdns_pcb_send_bye((mdns_if_t)i, (mdns_ip_protocol_t)j, services, len, include_ip);
1934             }
1935         }
1936     }
1937 }
1938 
1939 /**
1940  * @brief  Send announcement on particular PCB
1941  */
_mdns_announce_pcb(mdns_if_t tcpip_if,mdns_ip_protocol_t ip_protocol,mdns_srv_item_t ** services,size_t len,bool include_ip)1942 static void _mdns_announce_pcb(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, mdns_srv_item_t ** services, size_t len, bool include_ip)
1943 {
1944     mdns_pcb_t * _pcb = &_mdns_server->interfaces[tcpip_if].pcbs[ip_protocol];
1945     size_t i;
1946     if (_pcb->pcb) {
1947         if (PCB_STATE_IS_PROBING(_pcb)) {
1948             _mdns_init_pcb_probe(tcpip_if, ip_protocol, services, len, include_ip);
1949         } else if (PCB_STATE_IS_ANNOUNCING(_pcb)) {
1950             mdns_tx_packet_t *  p = _mdns_get_next_pcb_packet(tcpip_if, ip_protocol);
1951             if (p) {
1952                 for (i=0; i<len; i++) {
1953                     if (!_mdns_alloc_answer(&p->answers, MDNS_TYPE_SDPTR, services[i]->service, NULL, false, false)
1954                             || !_mdns_alloc_answer(&p->answers, MDNS_TYPE_PTR, services[i]->service, NULL, false, false)
1955                             || !_mdns_alloc_answer(&p->answers, MDNS_TYPE_SRV, services[i]->service, NULL, true, false)
1956                             || !_mdns_alloc_answer(&p->answers, MDNS_TYPE_TXT, services[i]->service, NULL, true, false)) {
1957                         break;
1958                     }
1959                 }
1960                 if (include_ip) {
1961                     _mdns_dealloc_answer(&p->additional, MDNS_TYPE_A, NULL);
1962                     _mdns_dealloc_answer(&p->additional, MDNS_TYPE_AAAA, NULL);
1963                     _mdns_append_host_list_in_services(&p->answers, services, len, true, false);
1964                 }
1965                 _pcb->state = PCB_ANNOUNCE_1;
1966             }
1967         } else if (_pcb->state == PCB_RUNNING) {
1968 
1969             if (_str_null_or_empty(_mdns_server->hostname)) {
1970                 return;
1971             }
1972 
1973             _pcb->state = PCB_ANNOUNCE_1;
1974             mdns_tx_packet_t * p = _mdns_create_announce_packet(tcpip_if, ip_protocol, services, len, include_ip);
1975             if (p) {
1976                 _mdns_schedule_tx_packet(p, 0);
1977             }
1978         }
1979     }
1980 }
1981 
1982 /**
1983  * @brief  Send probe on all active PCBs
1984  */
_mdns_probe_all_pcbs(mdns_srv_item_t ** services,size_t len,bool probe_ip,bool clear_old_probe)1985 static void _mdns_probe_all_pcbs(mdns_srv_item_t ** services, size_t len, bool probe_ip, bool clear_old_probe)
1986 {
1987     uint8_t i, j;
1988     for (i=0; i<MDNS_IF_MAX; i++) {
1989         for (j=0; j<MDNS_IP_PROTOCOL_MAX; j++) {
1990             if (_mdns_server->interfaces[i].pcbs[j].pcb) {
1991                 mdns_pcb_t * _pcb = &_mdns_server->interfaces[i].pcbs[j];
1992                 if (clear_old_probe) {
1993                     free(_pcb->probe_services);
1994                     _pcb->probe_services = NULL;
1995                     _pcb->probe_services_len = 0;
1996                     _pcb->probe_running = false;
1997                 }
1998                 _mdns_init_pcb_probe((mdns_if_t)i, (mdns_ip_protocol_t)j, services, len, probe_ip);
1999             }
2000         }
2001     }
2002 }
2003 
2004 /**
2005  * @brief  Send announcement on all active PCBs
2006  */
_mdns_announce_all_pcbs(mdns_srv_item_t ** services,size_t len,bool include_ip)2007 static void _mdns_announce_all_pcbs(mdns_srv_item_t ** services, size_t len, bool include_ip)
2008 {
2009     uint8_t i, j;
2010     for (i=0; i<MDNS_IF_MAX; i++) {
2011         for (j=0; j<MDNS_IP_PROTOCOL_MAX; j++) {
2012             _mdns_announce_pcb((mdns_if_t)i, (mdns_ip_protocol_t)j, services, len, include_ip);
2013         }
2014     }
2015 }
2016 
2017 /**
2018  * @brief  Restart the responder on all active PCBs
2019  */
_mdns_send_final_bye(bool include_ip)2020 static void _mdns_send_final_bye(bool include_ip)
2021 {
2022     //collect all services and start probe
2023     size_t srv_count = 0;
2024     mdns_srv_item_t * a = _mdns_server->services;
2025     while (a) {
2026         srv_count++;
2027         a = a->next;
2028     }
2029     if (!srv_count) {
2030         return;
2031     }
2032     mdns_srv_item_t * services[srv_count];
2033     size_t i = 0;
2034     a = _mdns_server->services;
2035     while (a) {
2036         services[i++] = a;
2037         a = a->next;
2038     }
2039     _mdns_send_bye(services, srv_count, include_ip);
2040 }
2041 
2042 /**
2043  * @brief  Stop the responder on all services without instance
2044  */
_mdns_send_bye_all_pcbs_no_instance(bool include_ip)2045 static void _mdns_send_bye_all_pcbs_no_instance(bool include_ip)
2046 {
2047     size_t srv_count = 0;
2048     mdns_srv_item_t * a = _mdns_server->services;
2049     while (a) {
2050         if (!a->service->instance) {
2051             srv_count++;
2052         }
2053         a = a->next;
2054     }
2055     if (!srv_count) {
2056         return;
2057     }
2058     mdns_srv_item_t * services[srv_count];
2059     size_t i = 0;
2060     a = _mdns_server->services;
2061     while (a) {
2062         if (!a->service->instance) {
2063             services[i++] = a;
2064         }
2065         a = a->next;
2066     }
2067     _mdns_send_bye(services, srv_count, include_ip);
2068 }
2069 
2070 /**
2071  * @brief  Restart the responder on all services without instance
2072  */
_mdns_restart_all_pcbs_no_instance(void)2073 static void _mdns_restart_all_pcbs_no_instance(void)
2074 {
2075     size_t srv_count = 0;
2076     mdns_srv_item_t * a = _mdns_server->services;
2077     while (a) {
2078         if (!a->service->instance) {
2079             srv_count++;
2080         }
2081         a = a->next;
2082     }
2083     if (!srv_count) {
2084         return;
2085     }
2086     mdns_srv_item_t * services[srv_count];
2087     size_t i = 0;
2088     a = _mdns_server->services;
2089     while (a) {
2090         if (!a->service->instance) {
2091             services[i++] = a;
2092         }
2093         a = a->next;
2094     }
2095     _mdns_probe_all_pcbs(services, srv_count, false, true);
2096 }
2097 
2098 /**
2099  * @brief  Restart the responder on all active PCBs
2100  */
_mdns_restart_all_pcbs(void)2101 static void _mdns_restart_all_pcbs(void)
2102 {
2103     _mdns_clear_tx_queue_head();
2104     size_t srv_count = 0;
2105     mdns_srv_item_t * a = _mdns_server->services;
2106     while (a) {
2107         srv_count++;
2108         a = a->next;
2109     }
2110     mdns_srv_item_t * services[srv_count];
2111     size_t l = 0;
2112     a = _mdns_server->services;
2113     while (a) {
2114         services[l++] = a;
2115         a = a->next;
2116     }
2117 
2118     _mdns_probe_all_pcbs(services, srv_count, true, true);
2119 }
2120 
2121 
2122 
2123 /**
2124  * @brief  creates/allocates new text item list
2125  * @param  num_items     service number of txt items or 0
2126  * @param  txt           service txt items array or NULL
2127  *
2128  * @return pointer to the linked txt item list or NULL
2129  */
_mdns_allocate_txt(size_t num_items,mdns_txt_item_t txt[])2130 static mdns_txt_linked_item_t * _mdns_allocate_txt(size_t num_items, mdns_txt_item_t txt[])
2131 {
2132     mdns_txt_linked_item_t * new_txt = NULL;
2133     size_t i = 0;
2134     if (num_items) {
2135         for (i=0; i<num_items; i++) {
2136             mdns_txt_linked_item_t * new_item = (mdns_txt_linked_item_t *)malloc(sizeof(mdns_txt_linked_item_t));
2137             if (!new_item) {
2138                 HOOK_MALLOC_FAILED;
2139                 break;
2140             }
2141             new_item->key = strdup(txt[i].key);
2142             if (!new_item->key) {
2143                 free(new_item);
2144                 break;
2145             }
2146             new_item->value = strdup(txt[i].value);
2147             if (!new_item->value) {
2148                 free((char *)new_item->key);
2149                 free(new_item);
2150                 break;
2151             }
2152             new_item->value_len = strlen(new_item->value);
2153             new_item->next = new_txt;
2154             new_txt = new_item;
2155         }
2156     }
2157     return new_txt;
2158 }
_mdns_free_linked_txt(mdns_txt_linked_item_t * txt)2159 static void _mdns_free_linked_txt(mdns_txt_linked_item_t *txt)
2160 {
2161     mdns_txt_linked_item_t *t;
2162     while (txt) {
2163         t = txt;
2164         txt = txt->next;
2165         free((char *)t->value);
2166         free((char *)t->key);
2167         free(t);
2168     }
2169 }
2170 
2171 /**
2172  * @brief  creates/allocates new service
2173  * @param  service       service type
2174  * @param  proto         service proto
2175  * @param  port          service port
2176  * @param  instance      service instance
2177  * @param  num_items     service number of txt items or 0
2178  * @param  txt           service txt items array or NULL
2179  *
2180  * @return pointer to the service or NULL on error
2181  */
_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[])2182 static mdns_service_t * _mdns_create_service(const char * service, const char * proto, const char * hostname,
2183                                              uint16_t port, const char * instance, size_t num_items,
2184                                              mdns_txt_item_t txt[])
2185 {
2186     mdns_service_t * s = (mdns_service_t *)malloc(sizeof(mdns_service_t));
2187     if (!s) {
2188         HOOK_MALLOC_FAILED;
2189         return NULL;
2190     }
2191 
2192     mdns_txt_linked_item_t * new_txt = _mdns_allocate_txt(num_items, txt);
2193     if (num_items && new_txt == NULL) {
2194         free(s);
2195         return NULL;
2196     }
2197 
2198     s->priority = 0;
2199     s->weight = 0;
2200     s->instance = instance?strndup(instance, MDNS_NAME_BUF_LEN - 1):NULL;
2201     s->txt = new_txt;
2202     s->port = port;
2203 
2204     if (hostname) {
2205         s->hostname = strndup(hostname, MDNS_NAME_BUF_LEN - 1);
2206         if (!s->hostname) {
2207             free(s);
2208             return NULL;
2209         }
2210     } else {
2211         s->hostname = NULL;
2212     }
2213 
2214     s->service = strndup(service, MDNS_NAME_BUF_LEN - 1);
2215     if (!s->service) {
2216         free(s);
2217         return NULL;
2218     }
2219 
2220     s->proto = strndup(proto, MDNS_NAME_BUF_LEN - 1);
2221     if (!s->proto) {
2222         free((char *)s->service);
2223         free(s);
2224         return NULL;
2225     }
2226 
2227     return s;
2228 }
2229 
2230 /**
2231  * @brief  Remove and free service answer from answer list (destination)
2232  */
_mdns_dealloc_scheduled_service_answers(mdns_out_answer_t ** destination,mdns_service_t * service)2233 static void _mdns_dealloc_scheduled_service_answers(mdns_out_answer_t ** destination, mdns_service_t * service)
2234 {
2235     mdns_out_answer_t * d = *destination;
2236     if (!d) {
2237         return;
2238     }
2239     while (d && d->service == service) {
2240         *destination = d->next;
2241         free(d);
2242         d = *destination;
2243     }
2244     while (d && d->next) {
2245         mdns_out_answer_t * a = d->next;
2246         if (a->service == service) {
2247             d->next = a->next;
2248             free(a);
2249         } else {
2250             d = d->next;
2251         }
2252     }
2253 }
2254 
2255 /**
2256  * @brief  Find, remove and free answers and scheduled packets for service
2257  */
_mdns_remove_scheduled_service_packets(mdns_service_t * service)2258 static void _mdns_remove_scheduled_service_packets(mdns_service_t * service)
2259 {
2260     if (!service) {
2261         return;
2262     }
2263     mdns_tx_packet_t * p = NULL;
2264     mdns_tx_packet_t * q = _mdns_server->tx_queue_head;
2265     while (q) {
2266         bool had_answers = (q->answers != NULL);
2267 
2268         _mdns_dealloc_scheduled_service_answers(&(q->answers), service);
2269         _mdns_dealloc_scheduled_service_answers(&(q->additional), service);
2270         _mdns_dealloc_scheduled_service_answers(&(q->servers), service);
2271 
2272 
2273         mdns_pcb_t * _pcb = &_mdns_server->interfaces[q->tcpip_if].pcbs[q->ip_protocol];
2274         if(_pcb->pcb) {
2275             if (PCB_STATE_IS_PROBING(_pcb)) {
2276                 uint8_t i;
2277                 //check if we are probing this service
2278                 for (i=0; i<_pcb->probe_services_len; i++) {
2279                     mdns_srv_item_t * s = _pcb->probe_services[i];
2280                     if (s->service == service){
2281                         break;
2282                     }
2283                 }
2284                 if (i < _pcb->probe_services_len) {
2285                     if (_pcb->probe_services_len > 1) {
2286                         uint8_t n;
2287                         for (n=(i+1); n<_pcb->probe_services_len; n++) {
2288                             _pcb->probe_services[n-1] = _pcb->probe_services[n];
2289                         }
2290                         _pcb->probe_services_len--;
2291                     } else {
2292                         _pcb->probe_services_len = 0;
2293                         free(_pcb->probe_services);
2294                         _pcb->probe_services = NULL;
2295                         if (!_pcb->probe_ip) {
2296                             _pcb->probe_running = false;
2297                             _pcb->state = PCB_RUNNING;
2298                         }
2299                     }
2300 
2301                     if (q->questions) {
2302                         mdns_out_question_t * qsn = NULL;
2303                         mdns_out_question_t * qs = q->questions;
2304                         if (qs->type == MDNS_TYPE_ANY
2305                             && qs->service && strcmp(qs->service, service->service) == 0
2306                             && qs->proto && strcmp(qs->proto, service->proto) == 0)
2307                         {
2308                             q->questions = q->questions->next;
2309                             free(qs);
2310                         } else while (qs->next) {
2311                             qsn = qs->next;
2312                             if (qsn->type == MDNS_TYPE_ANY
2313                                 && qsn->service && strcmp(qsn->service, service->service) == 0
2314                                 && qsn->proto && strcmp(qsn->proto, service->proto) == 0)
2315                             {
2316                                 qs->next = qsn->next;
2317                                 free(qsn);
2318                                 break;
2319                             }
2320                             qs = qs->next;
2321                         }
2322                     }
2323                 }
2324             } else if (PCB_STATE_IS_ANNOUNCING(_pcb)) {
2325                 //if answers were cleared, set to running
2326                 if (had_answers && q->answers == NULL) {
2327                     _pcb->state = PCB_RUNNING;
2328                 }
2329             }
2330         }
2331 
2332         p = q;
2333         q = q->next;
2334         if(!p->questions && !p->answers && !p->additional && !p->servers){
2335             queueDetach(mdns_tx_packet_t, _mdns_server->tx_queue_head, p);
2336             _mdns_free_tx_packet(p);
2337         }
2338     }
2339 }
2340 
2341 /**
2342  * @brief  free service memory
2343  *
2344  * @param  service      the service
2345  */
_mdns_free_service(mdns_service_t * service)2346 static void _mdns_free_service(mdns_service_t * service)
2347 {
2348     if (!service) {
2349         return;
2350     }
2351     free((char *)service->instance);
2352     free((char *)service->service);
2353     free((char *)service->proto);
2354     free((char *)service->hostname);
2355     while (service->txt) {
2356         mdns_txt_linked_item_t * s = service->txt;
2357         service->txt = service->txt->next;
2358         free((char *)s->key);
2359         free((char *)s->value);
2360         free(s);
2361     }
2362     free(service->txt);
2363     free(service);
2364 }
2365 
2366 
2367 /*
2368  * Received Packet Handling
2369  * */
2370 
2371 /**
2372  * @brief  Detect SRV collision
2373  */
_mdns_check_srv_collision(mdns_service_t * service,uint16_t priority,uint16_t weight,uint16_t port,const char * host,const char * domain)2374 static int _mdns_check_srv_collision(mdns_service_t * service, uint16_t priority, uint16_t weight, uint16_t port, const char * host, const char * domain)
2375 {
2376     if (_str_null_or_empty(_mdns_server->hostname)) {
2377         return 0;
2378     }
2379 
2380     size_t our_host_len = strlen(_mdns_server->hostname);
2381     size_t our_len = 14 + our_host_len;
2382 
2383     size_t their_host_len = strlen(host);
2384     size_t their_domain_len = strlen(domain);
2385     size_t their_len = 9 + their_host_len + their_domain_len;
2386 
2387     if (their_len > our_len) {
2388         return 1;//they win
2389     } else if (their_len < our_len) {
2390         return -1;//we win
2391     }
2392 
2393     uint16_t our_index = 0;
2394     uint8_t our_data[our_len];
2395     _mdns_append_u16(our_data, &our_index, service->priority);
2396     _mdns_append_u16(our_data, &our_index, service->weight);
2397     _mdns_append_u16(our_data, &our_index, service->port);
2398     our_data[our_index++] = our_host_len;
2399     memcpy(our_data + our_index, _mdns_server->hostname, our_host_len);
2400     our_index += our_host_len;
2401     our_data[our_index++] = 5;
2402     memcpy(our_data + our_index, MDNS_DEFAULT_DOMAIN, 5);
2403     our_index += 5;
2404     our_data[our_index++] = 0;
2405 
2406     uint16_t their_index = 0;
2407     uint8_t their_data[their_len];
2408     _mdns_append_u16(their_data, &their_index, priority);
2409     _mdns_append_u16(their_data, &their_index, weight);
2410     _mdns_append_u16(their_data, &their_index, port);
2411     their_data[their_index++] = their_host_len;
2412     memcpy(their_data + their_index, host, their_host_len);
2413     their_index += their_host_len;
2414     their_data[their_index++] = their_domain_len;
2415     memcpy(their_data + their_index, domain, their_domain_len);
2416     their_index += their_domain_len;
2417     their_data[their_index++] = 0;
2418 
2419     int ret = memcmp(our_data, their_data, our_len);
2420     if (ret > 0) {
2421         return -1;//we win
2422     } else if (ret < 0) {
2423         return 1;//they win
2424     }
2425     return 0;//same
2426 }
2427 
2428 /**
2429  * @brief  Detect TXT collision
2430  */
_mdns_check_txt_collision(mdns_service_t * service,const uint8_t * data,size_t len)2431 static int _mdns_check_txt_collision(mdns_service_t * service, const uint8_t * data, size_t len)
2432 {
2433     size_t data_len = 0;
2434     if (len == 1 && service->txt) {
2435         return -1;//we win
2436     } else if (len > 1 && !service->txt) {
2437         return 1;//they win
2438     } else if (len == 1 && !service->txt) {
2439         return 0;//same
2440     }
2441 
2442     mdns_txt_linked_item_t * txt = service->txt;
2443     while (txt) {
2444         data_len += 1 /* record-len */ + strlen(txt->key) + txt->value_len + (txt->value ? 1 : 0 /* "=" */);
2445         txt = txt->next;
2446     }
2447 
2448     if (len > data_len) {
2449         return 1;//they win
2450     } else if (len < data_len) {
2451         return -1;//we win
2452     }
2453 
2454     uint8_t ours[len];
2455     uint16_t index = 0;
2456 
2457     txt = service->txt;
2458     while (txt) {
2459         append_one_txt_record_entry(ours, &index, txt);
2460         txt = txt->next;
2461     }
2462 
2463     int ret = memcmp(ours, data, len);
2464     if (ret > 0) {
2465         return -1;//we win
2466     } else if (ret < 0) {
2467         return 1;//they win
2468     }
2469     return 0;//same
2470 }
2471 
2472 /**
2473  * @brief  Set interface as duplicate if another is found on the same subnet
2474  */
_mdns_dup_interface(mdns_if_t tcpip_if)2475 static void _mdns_dup_interface(mdns_if_t tcpip_if)
2476 {
2477     uint8_t i;
2478     mdns_if_t other_if = _mdns_get_other_if (tcpip_if);
2479     if (other_if == MDNS_IF_MAX) {
2480         return; // no other interface found
2481     }
2482     for (i=0; i<MDNS_IP_PROTOCOL_MAX; i++) {
2483         if (_mdns_server->interfaces[other_if].pcbs[i].pcb) {
2484             //stop this interface and mark as dup
2485             if (_mdns_server->interfaces[tcpip_if].pcbs[i].pcb) {
2486                 _mdns_clear_pcb_tx_queue_head(tcpip_if, i);
2487                 _mdns_pcb_deinit(tcpip_if, i);
2488             }
2489             _mdns_server->interfaces[tcpip_if].pcbs[i].state = PCB_DUP;
2490             _mdns_announce_pcb(other_if, i, NULL, 0, true);
2491         }
2492     }
2493 }
2494 
2495 /**
2496  * @brief  Detect IPv4 address collision
2497  */
_mdns_check_a_collision(esp_ip4_addr_t * ip,mdns_if_t tcpip_if)2498 static int _mdns_check_a_collision(esp_ip4_addr_t * ip, mdns_if_t tcpip_if)
2499 {
2500     esp_netif_ip_info_t if_ip_info;
2501     esp_netif_ip_info_t other_ip_info;
2502     if (!ip->addr) {
2503         return 1;//denial! they win
2504     }
2505     if (esp_netif_get_ip_info(_mdns_get_esp_netif(tcpip_if), &if_ip_info)) {
2506         return 1;//they win
2507     }
2508 
2509     int ret = memcmp((uint8_t*)&if_ip_info.ip.addr, (uint8_t*)&ip->addr, sizeof(esp_ip4_addr_t));
2510     if (ret > 0) {
2511         return -1;//we win
2512     } else if (ret < 0) {
2513         //is it the other interface?
2514         mdns_if_t other_if = _mdns_get_other_if (tcpip_if);
2515         if (other_if == MDNS_IF_MAX) {
2516             return 1;//AP interface! They win
2517         }
2518         if (esp_netif_get_ip_info(_mdns_get_esp_netif(other_if), &other_ip_info)) {
2519             return 1;//IPv4 not active! They win
2520         }
2521         if (ip->addr != other_ip_info.ip.addr) {
2522             return 1;//IPv4 not ours! They win
2523         }
2524         _mdns_dup_interface(tcpip_if);
2525         return 2;//they win
2526     }
2527     return 0;//same
2528 }
2529 
2530 #if CONFIG_LWIP_IPV6
2531 /**
2532  * @brief  Detect IPv6 address collision
2533  */
_mdns_check_aaaa_collision(esp_ip6_addr_t * ip,mdns_if_t tcpip_if)2534 static int _mdns_check_aaaa_collision(esp_ip6_addr_t * ip, mdns_if_t tcpip_if)
2535 {
2536     struct esp_ip6_addr if_ip6;
2537     struct esp_ip6_addr other_ip6;
2538     if (_ipv6_address_is_zero(*ip)) {
2539         return 1;//denial! they win
2540     }
2541     if (esp_netif_get_ip6_linklocal(_mdns_get_esp_netif(tcpip_if), &if_ip6)) {
2542         return 1;//they win
2543     }
2544     int ret = memcmp((uint8_t*)&if_ip6.addr, (uint8_t*)ip->addr, _MDNS_SIZEOF_IP6_ADDR);
2545     if (ret > 0) {
2546         return -1;//we win
2547     } else if (ret < 0) {
2548         //is it the other interface?
2549         mdns_if_t other_if = _mdns_get_other_if (tcpip_if);
2550         if (other_if == MDNS_IF_MAX) {
2551             return 1;//AP interface! They win
2552         }
2553         if (esp_netif_get_ip6_linklocal(_mdns_get_esp_netif(other_if), &other_ip6)) {
2554             return 1;//IPv6 not active! They win
2555         }
2556         if (memcmp((uint8_t*)&other_ip6.addr, (uint8_t*)ip->addr, _MDNS_SIZEOF_IP6_ADDR)) {
2557             return 1;//IPv6 not ours! They win
2558         }
2559         _mdns_dup_interface(tcpip_if);
2560         return 2;//they win
2561     }
2562     return 0;//same
2563 }
2564 #endif
2565 
_hostname_is_ours(const char * hostname)2566 static bool _hostname_is_ours(const char * hostname)
2567 {
2568     if (strcasecmp(hostname, _mdns_server->hostname) == 0) {
2569         return true;
2570     }
2571     mdns_host_item_t * host = _mdns_host_list;
2572     while (host != NULL) {
2573         if (strcasecmp(hostname, host->hostname) == 0) {
2574             return true;
2575         }
2576         host = host->next;
2577     }
2578     return false;
2579 }
2580 
_mdns_delegate_hostname_add(const char * hostname,mdns_ip_addr_t * address_list)2581 static bool _mdns_delegate_hostname_add(const char * hostname, mdns_ip_addr_t * address_list)
2582 {
2583     if (_hostname_is_ours(hostname)) {
2584         return true;
2585     }
2586 
2587     mdns_host_item_t * host = (mdns_host_item_t *)malloc(sizeof(mdns_host_item_t));
2588 
2589     if (host == NULL) {
2590         return false;
2591     }
2592     host->address_list = address_list;
2593     host->hostname = hostname;
2594     host->next = _mdns_host_list;
2595     _mdns_host_list = host;
2596     return true;
2597 }
2598 
free_address_list(mdns_ip_addr_t * address_list)2599 static void free_address_list(mdns_ip_addr_t * address_list)
2600 {
2601     while (address_list != NULL) {
2602         mdns_ip_addr_t * next = address_list->next;
2603         free(address_list);
2604         address_list = next;
2605     }
2606 }
2607 
copy_address_list(const mdns_ip_addr_t * address_list)2608 static mdns_ip_addr_t * copy_address_list(const mdns_ip_addr_t * address_list)
2609 {
2610     mdns_ip_addr_t * head = NULL;
2611     mdns_ip_addr_t * tail = NULL;
2612     while (address_list != NULL) {
2613         mdns_ip_addr_t * addr = (mdns_ip_addr_t *)malloc(sizeof(mdns_ip_addr_t));
2614         if (addr == NULL) {
2615             free_address_list(head);
2616             return NULL;
2617         }
2618         addr->addr = address_list->addr;
2619         addr->next = NULL;
2620         if (head == NULL) {
2621             head = addr;
2622             tail = addr;
2623         } else {
2624             tail->next = addr;
2625             tail = tail->next;
2626         }
2627         address_list = address_list->next;
2628     }
2629     return head;
2630 }
2631 
_mdns_delegate_hostname_remove(const char * hostname)2632 static bool _mdns_delegate_hostname_remove(const char * hostname)
2633 {
2634     mdns_srv_item_t * srv = _mdns_server->services;
2635     mdns_srv_item_t * prev_srv = NULL;
2636     while (srv) {
2637         if (strcasecmp(srv->service->hostname, hostname) == 0) {
2638             mdns_srv_item_t * to_free = srv;
2639             _mdns_send_bye(&srv, 1, false);
2640             _mdns_remove_scheduled_service_packets(srv->service);
2641             if (prev_srv == NULL) {
2642                 _mdns_server->services = srv->next;
2643                 srv = srv->next;
2644             } else {
2645                 prev_srv->next = srv->next;
2646                 srv = srv->next;
2647             }
2648             _mdns_free_service(to_free->service);
2649             free(to_free);
2650         } else {
2651             prev_srv = srv;
2652             srv = srv->next;
2653         }
2654     }
2655     mdns_host_item_t * host = _mdns_host_list;
2656     mdns_host_item_t * prev_host = NULL;
2657     while (host != NULL) {
2658         if (strcasecmp(hostname, host->hostname) == 0) {
2659             if (prev_host == NULL) {
2660                 _mdns_host_list = host->next;
2661             } else {
2662                 prev_host->next = host->next;
2663             }
2664             free_address_list(host->address_list);
2665             free((char *)host->hostname);
2666             free(host);
2667             break;
2668         } else {
2669             prev_host = host;
2670             host = host->next;
2671         }
2672     }
2673     return true;
2674 }
2675 
2676 /**
2677  * @brief  Check if parsed name is discovery
2678  */
_mdns_name_is_discovery(mdns_name_t * name,uint16_t type)2679 static bool _mdns_name_is_discovery(mdns_name_t * name, uint16_t type)
2680 {
2681     return (
2682                (name->host && name->host[0] && !strcasecmp(name->host, "_services"))
2683                && (name->service && name->service[0] && !strcasecmp(name->service, "_dns-sd"))
2684                && (name->proto && name->proto[0] && !strcasecmp(name->proto, "_udp"))
2685                && (name->domain && name->domain[0] && !strcasecmp(name->domain, MDNS_DEFAULT_DOMAIN))
2686                && type == MDNS_TYPE_PTR
2687            );
2688 }
2689 
2690 /**
2691  * @brief  Check if the parsed name is ours (matches service or host name)
2692  */
_mdns_name_is_ours(mdns_name_t * name)2693 static bool _mdns_name_is_ours(mdns_name_t * name)
2694 {
2695     //domain have to be "local"
2696     if (_str_null_or_empty(name->domain) || strcasecmp(name->domain, MDNS_DEFAULT_DOMAIN)) {
2697         return false;
2698     }
2699 
2700     //if service and proto are empty, host must match out hostname
2701     if (_str_null_or_empty(name->service) && _str_null_or_empty(name->proto)) {
2702         if (!_str_null_or_empty(name->host)
2703           && !_str_null_or_empty(_mdns_server->hostname)
2704           && _hostname_is_ours(name->host))
2705         {
2706             return true;
2707         }
2708         return false;
2709     }
2710 
2711     //if service or proto is empty, name is invalid
2712     if (_str_null_or_empty(name->service) || _str_null_or_empty(name->proto)) {
2713         return false;
2714     }
2715 
2716     //find the service
2717     mdns_srv_item_t * service;
2718     if (_str_null_or_empty(name->host)) {
2719         service = _mdns_get_service_item(name->service, name->proto, NULL);
2720     } else {
2721         service = _mdns_get_service_item_instance(name->host, name->service, name->proto, NULL);
2722     }
2723     if (!service) {
2724         return false;
2725     }
2726 
2727     //if host is empty and we have service, we have success
2728     if (_str_null_or_empty(name->host)) {
2729         return true;
2730     }
2731 
2732     //OK we have host in the name. find what is the instance of the service
2733     const char * instance = _mdns_get_service_instance_name(service->service);
2734     if (instance == NULL) {
2735         return false;
2736     }
2737 
2738     //compare the instance against the name
2739     if (strcasecmp(name->host, instance) == 0) {
2740         return true;
2741     }
2742 
2743     return false;
2744 }
2745 
2746 /**
2747  * @brief  read uint16_t from a packet
2748  * @param  packet       the packet
2749  * @param  index        index in the packet where the value starts
2750  *
2751  * @return the value
2752  */
_mdns_read_u16(const uint8_t * packet,uint16_t index)2753 static inline uint16_t _mdns_read_u16(const uint8_t * packet, uint16_t index)
2754 {
2755     return (uint16_t)(packet[index]) << 8 | packet[index+1];
2756 }
2757 
2758 /**
2759  * @brief  read uint32_t from a packet
2760  * @param  packet       the packet
2761  * @param  index        index in the packet where the value starts
2762  *
2763  * @return the value
2764  */
_mdns_read_u32(const uint8_t * packet,uint16_t index)2765 static inline uint32_t _mdns_read_u32(const uint8_t * packet, uint16_t index)
2766 {
2767     return (uint32_t)(packet[index]) << 24 | (uint32_t)(packet[index+1]) << 16 | (uint32_t)(packet[index+2]) << 8 | packet[index+3];
2768 }
2769 
2770 /**
2771  * @brief  reads and formats MDNS FQDN into mdns_name_t structure
2772  *
2773  * @param  packet       MDNS packet
2774  * @param  start        Starting point of FQDN
2775  * @param  name         mdns_name_t structure to populate
2776  *
2777  * @return the address after the parsed FQDN in the packet or NULL on error
2778  */
_mdns_parse_fqdn(const uint8_t * packet,const uint8_t * start,mdns_name_t * name)2779 static const uint8_t * _mdns_parse_fqdn(const uint8_t * packet, const uint8_t * start, mdns_name_t * name)
2780 {
2781     name->parts = 0;
2782     name->sub = 0;
2783     name->host[0] = 0;
2784     name->service[0] = 0;
2785     name->proto[0] = 0;
2786     name->domain[0] = 0;
2787     name->invalid = false;
2788 
2789     static char buf[MDNS_NAME_BUF_LEN];
2790 
2791     const uint8_t * next_data = (uint8_t*)_mdns_read_fqdn(packet, start, name, buf);
2792     if (!next_data) {
2793         return 0;
2794     }
2795     if (!name->parts || name->invalid) {
2796         return next_data;
2797     }
2798     if (name->parts == 3) {
2799         memmove((uint8_t*)name + (MDNS_NAME_BUF_LEN), (uint8_t*)name, 3*(MDNS_NAME_BUF_LEN));
2800         name->host[0] = 0;
2801     } else if (name->parts == 2) {
2802         memmove((uint8_t*)(name->domain), (uint8_t*)(name->service), (MDNS_NAME_BUF_LEN));
2803         name->service[0] = 0;
2804         name->proto[0] = 0;
2805     }
2806     if (strcasecmp(name->domain, MDNS_DEFAULT_DOMAIN) == 0 || strcasecmp(name->domain, "arpa") == 0) {
2807         return next_data;
2808     }
2809     name->invalid = true; // mark the current name invalid, but continue with other question
2810     return next_data;
2811 }
2812 
2813 /**
2814  * @brief  Called from parser to check if question matches particular service
2815  */
_mdns_question_matches(mdns_parsed_question_t * question,uint16_t type,mdns_srv_item_t * service)2816 static bool _mdns_question_matches(mdns_parsed_question_t * question, uint16_t type, mdns_srv_item_t * service)
2817 {
2818     if (question->type != type) {
2819         return false;
2820     }
2821     if (type == MDNS_TYPE_A || type == MDNS_TYPE_AAAA) {
2822         return true;
2823     } else if (type == MDNS_TYPE_PTR || type == MDNS_TYPE_SDPTR) {
2824         if (question->service && question->proto && question->domain
2825             && !strcasecmp(service->service->service, question->service)
2826             && !strcasecmp(service->service->proto, question->proto)
2827             && !strcasecmp(MDNS_DEFAULT_DOMAIN, question->domain)) {
2828             return true;
2829         }
2830     } else if (service && (type == MDNS_TYPE_SRV || type == MDNS_TYPE_TXT)) {
2831         const char * name = _mdns_get_service_instance_name(service->service);
2832         if (name && question->host && question->service && question->proto && question->domain
2833             && !strcasecmp(name, question->host)
2834             && !strcasecmp(service->service->service, question->service)
2835             && !strcasecmp(service->service->proto, question->proto)
2836             && !strcasecmp(MDNS_DEFAULT_DOMAIN, question->domain)) {
2837             return true;
2838         }
2839     }
2840 
2841     return false;
2842 }
2843 
2844 /**
2845  * @brief  Removes saved question from parsed data
2846  */
_mdns_remove_parsed_question(mdns_parsed_packet_t * parsed_packet,uint16_t type,mdns_srv_item_t * service)2847 static void _mdns_remove_parsed_question(mdns_parsed_packet_t * parsed_packet, uint16_t type, mdns_srv_item_t * service)
2848 {
2849     mdns_parsed_question_t * q = parsed_packet->questions;
2850 
2851     if (_mdns_question_matches(q, type, service)) {
2852         parsed_packet->questions = q->next;
2853         free(q->host);
2854         free(q->service);
2855         free(q->proto);
2856         free(q->domain);
2857         free(q);
2858         return;
2859     }
2860 
2861     while (q->next) {
2862         mdns_parsed_question_t * p = q->next;
2863         if (_mdns_question_matches(p, type, service)) {
2864             q->next = p->next;
2865             free(p->host);
2866             free(p->service);
2867             free(p->proto);
2868             free(p->domain);
2869             free(p);
2870             return;
2871         }
2872         q = q->next;
2873     }
2874 }
2875 
2876 /**
2877  * @brief  Get number of items in TXT parsed data
2878  */
_mdns_txt_items_count_get(const uint8_t * data,size_t len)2879 static int _mdns_txt_items_count_get(const uint8_t * data, size_t len)
2880 {
2881     if (len == 1) {
2882         return 0;
2883     }
2884 
2885     int num_items = 0;
2886     uint16_t i=0;
2887     size_t partLen = 0;
2888 
2889     while (i < len) {
2890         partLen = data[i++];
2891         if (!partLen) {
2892             break;
2893         }
2894         if ((i+partLen) > len) {
2895             return -1;//error
2896         }
2897         i+=partLen;
2898         num_items++;
2899     }
2900     return num_items;
2901 }
2902 
2903 /**
2904  * @brief  Get the length of TXT item's key name
2905  */
_mdns_txt_item_name_get_len(const uint8_t * data,size_t len)2906 static int _mdns_txt_item_name_get_len(const uint8_t * data, size_t len)
2907 {
2908     if (*data == '=') {
2909         return -1;
2910     }
2911     for (size_t i = 0; i < len; i++) {
2912         if (data[i] == '=') {
2913             return i;
2914         }
2915     }
2916     return len;
2917 }
2918 
2919 /**
2920  * @brief  Create TXT result array from parsed TXT data
2921  */
_mdns_result_txt_create(const uint8_t * data,size_t len,mdns_txt_item_t ** out_txt,uint8_t ** out_value_len,size_t * out_count)2922 static void _mdns_result_txt_create(const uint8_t *data, size_t len, mdns_txt_item_t **out_txt, uint8_t **out_value_len,
2923                                     size_t *out_count)
2924 {
2925     *out_txt = NULL;
2926     *out_count = 0;
2927     uint16_t i=0, y;
2928     size_t partLen = 0;
2929     int num_items = _mdns_txt_items_count_get(data, len);
2930     if (num_items < 0) {
2931         return;//error
2932     }
2933 
2934     if (!num_items) {
2935         return;
2936     }
2937 
2938     mdns_txt_item_t * txt = (mdns_txt_item_t *)malloc(sizeof(mdns_txt_item_t) * num_items);
2939     if (!txt) {
2940         HOOK_MALLOC_FAILED;
2941         return;
2942     }
2943     uint8_t * txt_value_len = (uint8_t *)malloc(num_items);
2944     if (!txt_value_len) {
2945         free(txt);
2946         HOOK_MALLOC_FAILED;
2947         return;
2948     }
2949     memset(txt, 0, sizeof(mdns_txt_item_t) * num_items);
2950     memset(txt_value_len, 0, num_items);
2951     size_t txt_num = 0;
2952 
2953     while (i < len) {
2954         partLen = data[i++];
2955         if (!partLen) {
2956             break;
2957         }
2958 
2959         if ((i+partLen) > len) {
2960             goto handle_error;//error
2961         }
2962 
2963         int name_len = _mdns_txt_item_name_get_len(data+i, partLen);
2964         if (name_len < 0) {//invalid item (no name)
2965             i += partLen;
2966             continue;
2967         }
2968         char * key = (char *)malloc(name_len + 1);
2969         if (!key) {
2970             HOOK_MALLOC_FAILED;
2971             goto handle_error;//error
2972         }
2973 
2974         mdns_txt_item_t * t = &txt[txt_num];
2975         uint8_t * value_len = &txt_value_len[txt_num];
2976         txt_num++;
2977 
2978         memcpy(key, data + i, name_len);
2979         key[name_len] = 0;
2980         i += name_len + 1;
2981         t->key = key;
2982 
2983         int new_value_len = partLen - name_len - 1;
2984         if (new_value_len > 0) {
2985             char * value = (char *)malloc(new_value_len + 1);
2986             if (!value) {
2987                 HOOK_MALLOC_FAILED;
2988                 goto handle_error;//error
2989             }
2990             memcpy(value, data + i, new_value_len);
2991             value[new_value_len] = 0;
2992             *value_len = new_value_len;
2993             i += new_value_len;
2994             t->value = value;
2995         }
2996     }
2997 
2998     *out_txt = txt;
2999     *out_count = txt_num;
3000     *out_value_len = txt_value_len;
3001     return;
3002 
3003 handle_error :
3004     for (y=0; y<txt_num; y++) {
3005         mdns_txt_item_t * t = &txt[y];
3006         free((char *)t->key);
3007         free((char *)t->value);
3008     }
3009     free(txt_value_len);
3010     free(txt);
3011 }
3012 
3013 /**
3014  * @brief  Duplicate string or return error
3015  */
_mdns_strdup_check(char ** out,char * in)3016 static esp_err_t _mdns_strdup_check(char ** out, char * in)
3017 {
3018     if (in && in[0]) {
3019         *out = strdup(in);
3020         if (!*out) {
3021             return ESP_FAIL;
3022         }
3023         return ESP_OK;
3024     }
3025     *out = NULL;
3026     return ESP_OK;
3027 }
3028 
3029 /**
3030  * @brief  main packet parser
3031  *
3032  * @param  packet       the packet
3033  */
mdns_parse_packet(mdns_rx_packet_t * packet)3034 void mdns_parse_packet(mdns_rx_packet_t * packet)
3035 {
3036     static mdns_name_t n;
3037     mdns_header_t header;
3038     const uint8_t * data = _mdns_get_packet_data(packet);
3039     size_t len = _mdns_get_packet_len(packet);
3040     const uint8_t * content = data + MDNS_HEAD_LEN;
3041     bool do_not_reply = false;
3042     mdns_search_once_t * search_result = NULL;
3043 
3044 #ifdef MDNS_ENABLE_DEBUG
3045     _mdns_dbg_printf("\nRX[%u][%u]: ", packet->tcpip_if, (uint32_t)packet->ip_protocol);
3046     if (packet->src.type == ESP_IPADDR_TYPE_V4) {
3047         _mdns_dbg_printf("From: " IPSTR ":%u, To: " IPSTR ", ", IP2STR(&packet->src.u_addr.ip4), packet->src_port, IP2STR(&packet->dest.u_addr.ip4));
3048     } else {
3049         _mdns_dbg_printf("From: " IPV6STR ":%u, To: " IPV6STR ", ", IPV62STR(packet->src.u_addr.ip6), packet->src_port, IPV62STR(packet->dest.u_addr.ip6));
3050     }
3051     mdns_debug_packet(data, len);
3052 #endif
3053 
3054     mdns_parsed_packet_t * parsed_packet = (mdns_parsed_packet_t *)malloc(sizeof(mdns_parsed_packet_t));
3055     if (!parsed_packet) {
3056         HOOK_MALLOC_FAILED;
3057         return;
3058     }
3059     memset(parsed_packet, 0, sizeof(mdns_parsed_packet_t));
3060 
3061     mdns_name_t * name = &n;
3062     memset(name, 0, sizeof(mdns_name_t));
3063 
3064     header.id = _mdns_read_u16(data, MDNS_HEAD_ID_OFFSET);
3065     header.flags.value = _mdns_read_u16(data, MDNS_HEAD_FLAGS_OFFSET);
3066     header.questions = _mdns_read_u16(data, MDNS_HEAD_QUESTIONS_OFFSET);
3067     header.answers = _mdns_read_u16(data, MDNS_HEAD_ANSWERS_OFFSET);
3068     header.servers = _mdns_read_u16(data, MDNS_HEAD_SERVERS_OFFSET);
3069     header.additional = _mdns_read_u16(data, MDNS_HEAD_ADDITIONAL_OFFSET);
3070 
3071     if (header.flags.value == MDNS_FLAGS_AUTHORITATIVE && packet->src_port != MDNS_SERVICE_PORT) {
3072         free(parsed_packet);
3073         return;
3074     }
3075 
3076     //if we have not set the hostname, we can not answer questions
3077     if (header.questions && !header.answers && _str_null_or_empty(_mdns_server->hostname)) {
3078         free(parsed_packet);
3079         return;
3080     }
3081 
3082     parsed_packet->tcpip_if = packet->tcpip_if;
3083     parsed_packet->ip_protocol = packet->ip_protocol;
3084     parsed_packet->multicast = packet->multicast;
3085     parsed_packet->authoritative = header.flags.value == MDNS_FLAGS_AUTHORITATIVE;
3086     parsed_packet->distributed = header.flags.value == MDNS_FLAGS_DISTRIBUTED;
3087     parsed_packet->id = header.id;
3088     esp_netif_ip_addr_copy(&parsed_packet->src, &packet->src);
3089     parsed_packet->src_port = packet->src_port;
3090 
3091     if (header.questions) {
3092         uint8_t qs = header.questions;
3093 
3094         while (qs--) {
3095             content = _mdns_parse_fqdn(data, content, name);
3096             if (!content) {
3097                 header.answers = 0;
3098                 header.additional = 0;
3099                 header.servers = 0;
3100                 goto clear_rx_packet;//error
3101             }
3102 
3103             uint16_t type = _mdns_read_u16(content, MDNS_TYPE_OFFSET);
3104             uint16_t mdns_class = _mdns_read_u16(content, MDNS_CLASS_OFFSET);
3105             bool unicast = !!(mdns_class & 0x8000);
3106             mdns_class &= 0x7FFF;
3107             content = content + 4;
3108 
3109             if (mdns_class != 0x0001 || name->invalid) {//bad class or invalid name for this question entry
3110                 continue;
3111             }
3112 
3113             if (_mdns_name_is_discovery(name, type)) {
3114                 //service discovery
3115                 parsed_packet->discovery = true;
3116                 mdns_srv_item_t * a = _mdns_server->services;
3117                 while (a) {
3118                     mdns_parsed_question_t * question = (mdns_parsed_question_t *)calloc(1, sizeof(mdns_parsed_question_t));
3119                     if (!question) {
3120                         HOOK_MALLOC_FAILED;
3121                         goto clear_rx_packet;
3122                     }
3123                     question->next = parsed_packet->questions;
3124                     parsed_packet->questions = question;
3125 
3126                     question->unicast = unicast;
3127                     question->type = MDNS_TYPE_SDPTR;
3128                     question->host = NULL;
3129                     question->service = strdup(a->service->service);
3130                     question->proto = strdup(a->service->proto);
3131                     question->domain = strdup(MDNS_DEFAULT_DOMAIN);
3132                     if (!question->service || !question->proto || !question->domain) {
3133                         goto clear_rx_packet;
3134                     }
3135                     a = a->next;
3136                 }
3137                 continue;
3138             } else if (name->sub || !_mdns_name_is_ours(name)) {
3139                 continue;
3140             }
3141 
3142             if (type == MDNS_TYPE_ANY && !_str_null_or_empty(name->host)) {
3143                 parsed_packet->probe = true;
3144             }
3145 
3146             mdns_parsed_question_t * question = (mdns_parsed_question_t *)calloc(1, sizeof(mdns_parsed_question_t));
3147             if (!question) {
3148                 HOOK_MALLOC_FAILED;
3149                 goto clear_rx_packet;
3150             }
3151             question->next = parsed_packet->questions;
3152             parsed_packet->questions = question;
3153 
3154             question->unicast = unicast;
3155             question->type = type;
3156             if (_mdns_strdup_check(&(question->host), name->host)
3157               || _mdns_strdup_check(&(question->service), name->service)
3158               || _mdns_strdup_check(&(question->proto), name->proto)
3159               || _mdns_strdup_check(&(question->domain), name->domain)) {
3160                 goto clear_rx_packet;
3161             }
3162         }
3163     }
3164 
3165     if (header.questions && !parsed_packet->questions && !parsed_packet->discovery && !header.answers) {
3166         goto clear_rx_packet;
3167     } else if (header.answers || header.servers || header.additional) {
3168         uint16_t recordIndex = 0;
3169 
3170         while (content < (data + len)) {
3171 
3172             content = _mdns_parse_fqdn(data, content, name);
3173             if (!content) {
3174                 goto clear_rx_packet;//error
3175             }
3176 
3177             uint16_t type = _mdns_read_u16(content, MDNS_TYPE_OFFSET);
3178             uint16_t mdns_class = _mdns_read_u16(content, MDNS_CLASS_OFFSET);
3179             uint32_t ttl = _mdns_read_u32(content, MDNS_TTL_OFFSET);
3180             uint16_t data_len = _mdns_read_u16(content, MDNS_LEN_OFFSET);
3181             const uint8_t * data_ptr = content + MDNS_DATA_OFFSET;
3182             mdns_class &= 0x7FFF;
3183 
3184             content = data_ptr + data_len;
3185             if (content > (data + len)) {
3186                 goto clear_rx_packet;
3187             }
3188 
3189             bool discovery = false;
3190             bool ours = false;
3191             mdns_srv_item_t * service = NULL;
3192             mdns_parsed_record_type_t record_type = MDNS_ANSWER;
3193 
3194             if (recordIndex >= (header.answers + header.servers)) {
3195                 record_type = MDNS_EXTRA;
3196             } else if (recordIndex >= (header.answers)) {
3197                 record_type = MDNS_NS;
3198             }
3199             recordIndex++;
3200 
3201             if (type == MDNS_TYPE_NSEC || type == MDNS_TYPE_OPT) {
3202                 //skip NSEC and OPT
3203                 continue;
3204             }
3205 
3206             if (parsed_packet->discovery && _mdns_name_is_discovery(name, type)) {
3207                 discovery = true;
3208             } else if (!name->sub && _mdns_name_is_ours(name)) {
3209                 ours = true;
3210                 if (name->service && name->service[0] && name->proto && name->proto[0]) {
3211                     service = _mdns_get_service_item(name->service, name->proto, NULL);
3212                 }
3213             } else {
3214                 if (!parsed_packet->authoritative || record_type == MDNS_NS) {
3215                     //skip this record
3216                     continue;
3217                 }
3218                 search_result = _mdns_search_find_from(_mdns_server->search_once, name, type, packet->tcpip_if, packet->ip_protocol);
3219             }
3220 
3221             if (type == MDNS_TYPE_PTR) {
3222                 if (!_mdns_parse_fqdn(data, data_ptr, name)) {
3223                     continue;//error
3224                 }
3225                 if (search_result) {
3226                     _mdns_search_result_add_ptr(search_result, name->host, name->service, name->proto,
3227                                                 packet->tcpip_if, packet->ip_protocol, ttl);
3228                 } else if ((discovery || ours) && !name->sub && _mdns_name_is_ours(name)) {
3229                     if (discovery) {
3230                         service = _mdns_get_service_item(name->service, name->proto, NULL);
3231                         _mdns_remove_parsed_question(parsed_packet, MDNS_TYPE_SDPTR, service);
3232                     } else if (service && parsed_packet->questions && !parsed_packet->probe) {
3233                         _mdns_remove_parsed_question(parsed_packet, type, service);
3234                     } else if (service) {
3235                         //check if TTL is more than half of the full TTL value (4500)
3236                         if (ttl > 2250) {
3237                             _mdns_remove_scheduled_answer(packet->tcpip_if, packet->ip_protocol, type, service);
3238                         }
3239                     }
3240                 }
3241             } else if (type == MDNS_TYPE_SRV) {
3242                 mdns_result_t * result = NULL;
3243                 if (search_result && search_result->type == MDNS_TYPE_PTR) {
3244                     result = search_result->result;
3245                     while (result) {
3246                         if (packet->tcpip_if == result->tcpip_if
3247                             && packet->ip_protocol == result->ip_protocol
3248                             && result->instance_name && !strcmp(name->host, result->instance_name)) {
3249                             break;
3250                         }
3251                         result = result->next;
3252                     }
3253                     if (!result) {
3254                         result = _mdns_search_result_add_ptr(search_result, name->host, name->service, name->proto,
3255                                                              packet->tcpip_if, packet->ip_protocol, ttl);
3256                         if (!result) {
3257                             continue;//error
3258                         }
3259                     }
3260                 }
3261 
3262                 if (!_mdns_parse_fqdn(data, data_ptr + MDNS_SRV_FQDN_OFFSET, name)) {
3263                     continue;//error
3264                 }
3265                 uint16_t priority = _mdns_read_u16(data_ptr, MDNS_SRV_PRIORITY_OFFSET);
3266                 uint16_t weight = _mdns_read_u16(data_ptr, MDNS_SRV_WEIGHT_OFFSET);
3267                 uint16_t port = _mdns_read_u16(data_ptr, MDNS_SRV_PORT_OFFSET);
3268 
3269                 if (search_result) {
3270                     if (search_result->type == MDNS_TYPE_PTR) {
3271                         if (!result->hostname) { // assign host/port for this entry only if not previously set
3272                             result->port = port;
3273                             result->hostname = strdup(name->host);
3274                         }
3275                     } else {
3276                         _mdns_search_result_add_srv(search_result, name->host, port, packet->tcpip_if, packet->ip_protocol, ttl);
3277                     }
3278                 } else if (ours) {
3279                     if (parsed_packet->questions && !parsed_packet->probe) {
3280                         _mdns_remove_parsed_question(parsed_packet, type, service);
3281                         continue;
3282                     } else if (parsed_packet->distributed) {
3283                         _mdns_remove_scheduled_answer(packet->tcpip_if, packet->ip_protocol, type, service);
3284                         continue;
3285                     }
3286                     //detect collision (-1=won, 0=none, 1=lost)
3287                     int col = 0;
3288                     if (mdns_class > 1) {
3289                         col = 1;
3290                     } else if (!mdns_class) {
3291                         col = -1;
3292                     } else if (service) { // only detect srv collision if service existed
3293                         col = _mdns_check_srv_collision(service->service, priority, weight, port, name->host, name->domain);
3294                     }
3295                     if (service && col && (parsed_packet->probe || parsed_packet->authoritative)) {
3296                         if (col > 0 || !port) {
3297                             do_not_reply = true;
3298                             if (_mdns_server->interfaces[packet->tcpip_if].pcbs[packet->ip_protocol].probe_running) {
3299                                 _mdns_server->interfaces[packet->tcpip_if].pcbs[packet->ip_protocol].failed_probes++;
3300                                 if (!_str_null_or_empty(service->service->instance)) {
3301                                     char * new_instance = _mdns_mangle_name((char *)service->service->instance);
3302                                     if (new_instance) {
3303                                         free((char *)service->service->instance);
3304                                         service->service->instance = new_instance;
3305                                     }
3306                                     _mdns_probe_all_pcbs(&service, 1, false, false);
3307                                 } else if (!_str_null_or_empty(_mdns_server->instance)) {
3308                                     char * new_instance = _mdns_mangle_name((char *)_mdns_server->instance);
3309                                     if (new_instance) {
3310                                         free((char *)_mdns_server->instance);
3311                                         _mdns_server->instance = new_instance;
3312                                     }
3313                                     _mdns_restart_all_pcbs_no_instance();
3314                                 } else {
3315                                     char * new_host = _mdns_mangle_name((char *)_mdns_server->hostname);
3316                                     if (new_host) {
3317                                         _mdns_remap_self_service_hostname(_mdns_server->hostname, new_host);
3318                                         free((char *)_mdns_server->hostname);
3319                                         _mdns_server->hostname = new_host;
3320                                         _mdns_self_host.hostname = new_host;
3321                                     }
3322                                     _mdns_restart_all_pcbs();
3323                                 }
3324                             } else if (service) {
3325                                 _mdns_pcb_send_bye(packet->tcpip_if, packet->ip_protocol, &service, 1, false);
3326                                 _mdns_init_pcb_probe(packet->tcpip_if, packet->ip_protocol, &service, 1, false);
3327                             }
3328                         }
3329                     } else if (ttl > 60 && !col && !parsed_packet->authoritative && !parsed_packet->probe && !parsed_packet->questions) {
3330                         _mdns_remove_scheduled_answer(packet->tcpip_if, packet->ip_protocol, type, service);
3331                     }
3332                 }
3333             } else if (type == MDNS_TYPE_TXT) {
3334                 if (search_result) {
3335                     mdns_txt_item_t * txt = NULL;
3336                     uint8_t *txt_value_len = NULL;
3337                     size_t txt_count = 0;
3338 
3339                     mdns_result_t * result = NULL;
3340                     if (search_result->type == MDNS_TYPE_PTR) {
3341                         result = search_result->result;
3342                         while (result) {
3343                             if (packet->tcpip_if == result->tcpip_if
3344                                 && packet->ip_protocol == result->ip_protocol
3345                                 && result->instance_name && !strcmp(name->host, result->instance_name)) {
3346                                 break;
3347                             }
3348                             result = result->next;
3349                         }
3350                         if (!result) {
3351                             result = _mdns_search_result_add_ptr(search_result, name->host, name->service, name->proto,
3352                                                                  packet->tcpip_if, packet->ip_protocol, ttl);
3353                             if (!result) {
3354                                 continue;//error
3355                             }
3356                         }
3357                         if (!result->txt) {
3358                             _mdns_result_txt_create(data_ptr, data_len, &txt, &txt_value_len, &txt_count);
3359                             if (txt_count) {
3360                                 result->txt = txt;
3361                                 result->txt_count = txt_count;
3362                                 result->txt_value_len = txt_value_len;
3363                             }
3364                         }
3365                     } else {
3366                         _mdns_result_txt_create(data_ptr, data_len, &txt, &txt_value_len, &txt_count);
3367                         if (txt_count) {
3368                             _mdns_search_result_add_txt(search_result, txt, txt_value_len, txt_count, packet->tcpip_if, packet->ip_protocol, ttl);
3369                         }
3370                     }
3371                 } else if (ours) {
3372                     if (parsed_packet->questions && !parsed_packet->probe) {
3373                         _mdns_remove_parsed_question(parsed_packet, type, service);
3374                         continue;
3375                     }
3376                     //detect collision (-1=won, 0=none, 1=lost)
3377                     int col = 0;
3378                     if (mdns_class > 1) {
3379                         col = 1;
3380                     } else if (!mdns_class) {
3381                         col = -1;
3382                     } else if (service) { // only detect txt collision if service existed
3383                         col = _mdns_check_txt_collision(service->service, data_ptr, data_len);
3384                     }
3385                     if (col && !_mdns_server->interfaces[packet->tcpip_if].pcbs[packet->ip_protocol].probe_running && service) {
3386                         do_not_reply = true;
3387                         _mdns_init_pcb_probe(packet->tcpip_if, packet->ip_protocol, &service, 1, true);
3388                     } else if (ttl > 2250 && !col && !parsed_packet->authoritative && !parsed_packet->probe && !parsed_packet->questions && !_mdns_server->interfaces[packet->tcpip_if].pcbs[packet->ip_protocol].probe_running) {
3389                         _mdns_remove_scheduled_answer(packet->tcpip_if, packet->ip_protocol, type, service);
3390                     }
3391                 }
3392 
3393             }
3394 #if CONFIG_LWIP_IPV6
3395             else if (type == MDNS_TYPE_AAAA) {//ipv6
3396                 esp_ip_addr_t ip6;
3397                 ip6.type = ESP_IPADDR_TYPE_V6;
3398                 memcpy(ip6.u_addr.ip6.addr, data_ptr, MDNS_ANSWER_AAAA_SIZE);
3399                 if (search_result) {
3400                     //check for more applicable searches (PTR & A/AAAA at the same time)
3401                     while (search_result) {
3402                         _mdns_search_result_add_ip(search_result, name->host, &ip6, packet->tcpip_if, packet->ip_protocol, ttl);
3403                         search_result = _mdns_search_find_from(search_result->next, name, type, packet->tcpip_if, packet->ip_protocol);
3404                     }
3405                 } else if (ours) {
3406                     if (parsed_packet->questions && !parsed_packet->probe) {
3407                         _mdns_remove_parsed_question(parsed_packet, type, NULL);
3408                         continue;
3409                     }
3410                     //detect collision (-1=won, 0=none, 1=lost)
3411                     int col = 0;
3412                     if (mdns_class > 1) {
3413                         col = 1;
3414                     } else if (!mdns_class) {
3415                         col = -1;
3416                     } else {
3417                         col = _mdns_check_aaaa_collision(&(ip6.u_addr.ip6), packet->tcpip_if);
3418                     }
3419                     if (col == 2) {
3420                         goto clear_rx_packet;
3421                     } else if (col == 1) {
3422                         do_not_reply = true;
3423                         if (_mdns_server->interfaces[packet->tcpip_if].pcbs[packet->ip_protocol].probe_running) {
3424                             if (col && (parsed_packet->probe || parsed_packet->authoritative)) {
3425                                 _mdns_server->interfaces[packet->tcpip_if].pcbs[packet->ip_protocol].failed_probes++;
3426                                 char * new_host = _mdns_mangle_name((char *)_mdns_server->hostname);
3427                                 if (new_host) {
3428                                     _mdns_remap_self_service_hostname(_mdns_server->hostname, new_host);
3429                                     free((char *)_mdns_server->hostname);
3430                                     _mdns_server->hostname = new_host;
3431                                     _mdns_self_host.hostname = new_host;
3432                                 }
3433                                 _mdns_restart_all_pcbs();
3434                             }
3435                         } else {
3436                             _mdns_init_pcb_probe(packet->tcpip_if, packet->ip_protocol, NULL, 0, true);
3437                         }
3438                     } else if (ttl > 60 && !col && !parsed_packet->authoritative && !parsed_packet->probe && !parsed_packet->questions && !_mdns_server->interfaces[packet->tcpip_if].pcbs[packet->ip_protocol].probe_running) {
3439                         _mdns_remove_scheduled_answer(packet->tcpip_if, packet->ip_protocol, type, NULL);
3440                     }
3441                 }
3442 
3443             }
3444 #endif
3445             else if (type == MDNS_TYPE_A) {
3446                 esp_ip_addr_t ip;
3447                 ip.type = ESP_IPADDR_TYPE_V4;
3448                 memcpy(&(ip.u_addr.ip4.addr), data_ptr, 4);
3449                 if (search_result) {
3450                     //check for more applicable searches (PTR & A/AAAA at the same time)
3451                     while (search_result) {
3452                         _mdns_search_result_add_ip(search_result, name->host, &ip, packet->tcpip_if, packet->ip_protocol, ttl);
3453                         search_result = _mdns_search_find_from(search_result->next, name, type, packet->tcpip_if, packet->ip_protocol);
3454                     }
3455                 } else if (ours) {
3456                     if (parsed_packet->questions && !parsed_packet->probe) {
3457                         _mdns_remove_parsed_question(parsed_packet, type, NULL);
3458                         continue;
3459                     }
3460                     //detect collision (-1=won, 0=none, 1=lost)
3461                     int col = 0;
3462                     if (mdns_class > 1) {
3463                         col = 1;
3464                     } else if (!mdns_class) {
3465                         col = -1;
3466                     } else {
3467                         col = _mdns_check_a_collision(&(ip.u_addr.ip4), packet->tcpip_if);
3468                     }
3469                     if (col == 2) {
3470                         goto clear_rx_packet;
3471                     } else if (col == 1) {
3472                         do_not_reply = true;
3473                         if (_mdns_server->interfaces[packet->tcpip_if].pcbs[packet->ip_protocol].probe_running) {
3474                             if (col && (parsed_packet->probe || parsed_packet->authoritative)) {
3475                                 _mdns_server->interfaces[packet->tcpip_if].pcbs[packet->ip_protocol].failed_probes++;
3476                                 char * new_host = _mdns_mangle_name((char *)_mdns_server->hostname);
3477                                 if (new_host) {
3478                                     _mdns_remap_self_service_hostname(_mdns_server->hostname, new_host);
3479                                     free((char *)_mdns_server->hostname);
3480                                     _mdns_server->hostname = new_host;
3481                                     _mdns_self_host.hostname = new_host;
3482                                 }
3483                                 _mdns_restart_all_pcbs();
3484                             }
3485                         } else {
3486                             _mdns_init_pcb_probe(packet->tcpip_if, packet->ip_protocol, NULL, 0, true);
3487                         }
3488                     } else if (ttl > 60 && !col && !parsed_packet->authoritative && !parsed_packet->probe && !parsed_packet->questions && !_mdns_server->interfaces[packet->tcpip_if].pcbs[packet->ip_protocol].probe_running) {
3489                         _mdns_remove_scheduled_answer(packet->tcpip_if, packet->ip_protocol, type, NULL);
3490                     }
3491                 }
3492 
3493             }
3494         }
3495         //end while
3496         if (parsed_packet->authoritative) {
3497             _mdns_search_finish_done();
3498         }
3499     }
3500 
3501     if (!do_not_reply && _mdns_server->interfaces[packet->tcpip_if].pcbs[packet->ip_protocol].state > PCB_PROBE_3 && (parsed_packet->questions || parsed_packet->discovery)) {
3502         _mdns_create_answer_from_parsed_packet(parsed_packet);
3503     }
3504 
3505 
3506 clear_rx_packet:
3507     while (parsed_packet->questions) {
3508         mdns_parsed_question_t * question = parsed_packet->questions;
3509         parsed_packet->questions = parsed_packet->questions->next;
3510         if (question->host) {
3511             free(question->host);
3512         }
3513         if (question->service) {
3514             free(question->service);
3515         }
3516         if (question->proto) {
3517             free(question->proto);
3518         }
3519         if (question->domain) {
3520             free(question->domain);
3521         }
3522         free(question);
3523     }
3524     free(parsed_packet);
3525 }
3526 
3527 /**
3528  * @brief  Enable mDNS interface
3529  */
_mdns_enable_pcb(mdns_if_t tcpip_if,mdns_ip_protocol_t ip_protocol)3530 void _mdns_enable_pcb(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
3531 {
3532     if (!_mdns_server->interfaces[tcpip_if].pcbs[ip_protocol].pcb) {
3533         if (_mdns_pcb_init(tcpip_if, ip_protocol)) {
3534             return;
3535         }
3536     }
3537     _mdns_restart_pcb(tcpip_if, ip_protocol);
3538 }
3539 
3540 /**
3541  * @brief  Disable mDNS interface
3542  */
_mdns_disable_pcb(mdns_if_t tcpip_if,mdns_ip_protocol_t ip_protocol)3543 void _mdns_disable_pcb(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
3544 {
3545     _mdns_clean_netif_ptr(tcpip_if);
3546 
3547     if (_mdns_server->interfaces[tcpip_if].pcbs[ip_protocol].pcb) {
3548         _mdns_clear_pcb_tx_queue_head(tcpip_if, ip_protocol);
3549         _mdns_pcb_deinit(tcpip_if, ip_protocol);
3550         mdns_if_t other_if = _mdns_get_other_if (tcpip_if);
3551         if (other_if != MDNS_IF_MAX && _mdns_server->interfaces[other_if].pcbs[ip_protocol].state == PCB_DUP) {
3552             _mdns_server->interfaces[other_if].pcbs[ip_protocol].state = PCB_OFF;
3553             _mdns_enable_pcb(other_if, ip_protocol);
3554         }
3555     }
3556     _mdns_server->interfaces[tcpip_if].pcbs[ip_protocol].state = PCB_OFF;
3557 }
3558 
3559 /**
3560  * @brief  Dispatch interface changes based on system events
3561  */
_mdns_handle_system_event(esp_event_base_t event_base,int32_t event_id,esp_netif_t * interface)3562 static void _mdns_handle_system_event(esp_event_base_t event_base,
3563                                       int32_t event_id, esp_netif_t* interface)
3564 {
3565     if (!_mdns_server) {
3566         return;
3567     }
3568 
3569     esp_netif_dhcp_status_t dcst;
3570     if (event_base == WIFI_EVENT) {
3571         switch(event_id) {
3572             case WIFI_EVENT_STA_CONNECTED:
3573                 if (!esp_netif_dhcpc_get_status(_mdns_get_esp_netif(MDNS_IF_STA), &dcst)) {
3574                     if (dcst == ESP_NETIF_DHCP_STOPPED) {
3575                         _mdns_enable_pcb(MDNS_IF_STA, MDNS_IP_PROTOCOL_V4);
3576                     }
3577                 }
3578                 break;
3579             case WIFI_EVENT_STA_DISCONNECTED:
3580                 _mdns_disable_pcb(MDNS_IF_STA, MDNS_IP_PROTOCOL_V4);
3581                 _mdns_disable_pcb(MDNS_IF_STA, MDNS_IP_PROTOCOL_V6);
3582                 break;
3583             case WIFI_EVENT_AP_START:
3584                 _mdns_enable_pcb(MDNS_IF_AP, MDNS_IP_PROTOCOL_V4);
3585                 break;
3586             case WIFI_EVENT_AP_STOP:
3587                 _mdns_disable_pcb(MDNS_IF_AP, MDNS_IP_PROTOCOL_V4);
3588                 _mdns_disable_pcb(MDNS_IF_AP, MDNS_IP_PROTOCOL_V6);
3589                 break;
3590             default:
3591                 break;
3592         }
3593     }
3594 #if CONFIG_ETH_ENABLED
3595     else if (event_base == ETH_EVENT) {
3596         switch (event_id) {
3597             case ETHERNET_EVENT_CONNECTED:
3598                 if (!esp_netif_dhcpc_get_status(_mdns_get_esp_netif(MDNS_IF_ETH), &dcst)) {
3599                     if (dcst == ESP_NETIF_DHCP_STOPPED) {
3600                         _mdns_enable_pcb(MDNS_IF_ETH, MDNS_IP_PROTOCOL_V4);
3601                     }
3602                 }
3603                 break;
3604             case ETHERNET_EVENT_DISCONNECTED:
3605                 _mdns_disable_pcb(MDNS_IF_ETH, MDNS_IP_PROTOCOL_V4);
3606                 _mdns_disable_pcb(MDNS_IF_ETH, MDNS_IP_PROTOCOL_V6);
3607                 break;
3608             default:
3609                 break;
3610         }
3611     }
3612 #endif
3613     else if (event_base == IP_EVENT) {
3614         switch (event_id) {
3615             case IP_EVENT_STA_GOT_IP:
3616                 _mdns_enable_pcb(MDNS_IF_STA, MDNS_IP_PROTOCOL_V4);
3617                 _mdns_announce_pcb(MDNS_IF_STA, MDNS_IP_PROTOCOL_V6, NULL, 0, true);
3618                 break;
3619 #if CONFIG_ETH_ENABLED
3620             case IP_EVENT_ETH_GOT_IP:
3621                 _mdns_enable_pcb(MDNS_IF_ETH, MDNS_IP_PROTOCOL_V4);
3622                 break;
3623 #endif
3624             case IP_EVENT_GOT_IP6:
3625             {
3626                 mdns_if_t mdns_if = _mdns_get_if_from_esp_netif(interface);
3627                 if (mdns_if != MDNS_IF_MAX) {
3628                     _mdns_enable_pcb(mdns_if, MDNS_IP_PROTOCOL_V6);
3629                     _mdns_announce_pcb(mdns_if, MDNS_IP_PROTOCOL_V4, NULL, 0, true);
3630                 }
3631 
3632             }
3633                 break;
3634             default:
3635                 break;
3636         }
3637     }
3638 }
3639 
3640 /*
3641  * MDNS Search
3642  * */
3643 
3644 /**
3645  * @brief  Free search structure (except the results)
3646  */
_mdns_search_free(mdns_search_once_t * search)3647 static void _mdns_search_free(mdns_search_once_t * search)
3648 {
3649     free(search->instance);
3650     free(search->service);
3651     free(search->proto);
3652     vSemaphoreDelete(search->done_semaphore);
3653     free(search);
3654 }
3655 
3656 /**
3657  * @brief  Allocate new search structure
3658  */
_mdns_search_init(const char * name,const char * service,const char * proto,uint16_t type,uint32_t timeout,uint8_t max_results,mdns_query_notify_t notifier)3659 static mdns_search_once_t *_mdns_search_init(const char *name, const char *service, const char *proto, uint16_t type,
3660                                              uint32_t timeout, uint8_t max_results, mdns_query_notify_t notifier)
3661 {
3662     mdns_search_once_t * search = (mdns_search_once_t *)malloc(sizeof(mdns_search_once_t));
3663     if (!search) {
3664         HOOK_MALLOC_FAILED;
3665         return NULL;
3666     }
3667     memset(search, 0, sizeof(mdns_search_once_t));
3668 
3669     search->done_semaphore = xSemaphoreCreateBinary();
3670     if (!search->done_semaphore) {
3671         free(search);
3672         return NULL;
3673     }
3674 
3675     if (!_str_null_or_empty(name)) {
3676         search->instance = strndup(name, MDNS_NAME_BUF_LEN-1);
3677         if (!search->instance) {
3678             _mdns_search_free(search);
3679             return NULL;
3680         }
3681     }
3682 
3683     if (!_str_null_or_empty(service)) {
3684         search->service = strndup(service, MDNS_NAME_BUF_LEN-1);
3685         if (!search->service) {
3686             _mdns_search_free(search);
3687             return NULL;
3688         }
3689     }
3690 
3691     if (!_str_null_or_empty(proto)) {
3692         search->proto = strndup(proto, MDNS_NAME_BUF_LEN-1);
3693         if (!search->proto) {
3694             _mdns_search_free(search);
3695             return NULL;
3696         }
3697     }
3698 
3699     search->type = type;
3700     search->timeout = timeout;
3701     search->num_results = 0;
3702     search->max_results = max_results;
3703     search->result = NULL;
3704     search->state = SEARCH_INIT;
3705     search->sent_at = 0;
3706     search->started_at = xTaskGetTickCount() * portTICK_PERIOD_MS;
3707     search->notifier = notifier;
3708     search->next = NULL;
3709 
3710     return search;
3711 }
3712 
3713 /**
3714  * @brief  Mark search as finished and remove it from search chain
3715  */
_mdns_search_finish(mdns_search_once_t * search)3716 static void _mdns_search_finish(mdns_search_once_t * search)
3717 {
3718     search->state = SEARCH_OFF;
3719     queueDetach(mdns_search_once_t, _mdns_server->search_once, search);
3720     if (search->notifier) {
3721         search->notifier(search);
3722     }
3723     xSemaphoreGive(search->done_semaphore);
3724 }
3725 
3726 /**
3727  * @brief  Add new search to the search chain
3728  */
_mdns_search_add(mdns_search_once_t * search)3729 static void _mdns_search_add(mdns_search_once_t * search)
3730 {
3731     search->next = _mdns_server->search_once;
3732     _mdns_server->search_once = search;
3733 }
3734 
3735 /**
3736  * @brief  Called from parser to finish any searches that have reached maximum results
3737  */
_mdns_search_finish_done(void)3738 static void _mdns_search_finish_done(void)
3739 {
3740     mdns_search_once_t * search = _mdns_server->search_once;
3741     mdns_search_once_t * s = NULL;
3742     while (search) {
3743         s = search;
3744         search = search->next;
3745         if (s->max_results && s->num_results >= s->max_results) {
3746             _mdns_search_finish(s);
3747         }
3748     }
3749 }
3750 
3751 /**
3752  * @brief  Create linked IP (copy) from parsed one
3753  */
_mdns_result_addr_create_ip(esp_ip_addr_t * ip)3754 static mdns_ip_addr_t * _mdns_result_addr_create_ip(esp_ip_addr_t * ip)
3755 {
3756     mdns_ip_addr_t * a = (mdns_ip_addr_t *)malloc(sizeof(mdns_ip_addr_t));
3757     if (!a) {
3758         HOOK_MALLOC_FAILED;
3759         return NULL;
3760     }
3761     memset(a, 0 , sizeof(mdns_ip_addr_t));
3762     a->addr.type = ip->type;
3763     if (ip->type == ESP_IPADDR_TYPE_V6) {
3764         memcpy(a->addr.u_addr.ip6.addr, ip->u_addr.ip6.addr, 16);
3765     } else {
3766         a->addr.u_addr.ip4.addr = ip->u_addr.ip4.addr;
3767     }
3768     return a;
3769 }
3770 
_mdns_result_update_ttl(mdns_result_t * r,uint32_t ttl)3771 static inline void _mdns_result_update_ttl(mdns_result_t * r, uint32_t ttl)
3772 {
3773     r->ttl = r->ttl < ttl ? r->ttl : ttl;
3774 }
3775 
3776 /**
3777  * @brief  Chain new IP to search result
3778  */
_mdns_result_add_ip(mdns_result_t * r,esp_ip_addr_t * ip)3779 static void _mdns_result_add_ip(mdns_result_t * r, esp_ip_addr_t * ip)
3780 {
3781     mdns_ip_addr_t * a = r->addr;
3782     while (a) {
3783         if (a->addr.type == ip->type) {
3784             if (a->addr.type == ESP_IPADDR_TYPE_V4 && a->addr.u_addr.ip4.addr == ip->u_addr.ip4.addr) {
3785                 return;
3786             }
3787             if (a->addr.type == ESP_IPADDR_TYPE_V6 && !memcmp(a->addr.u_addr.ip6.addr, ip->u_addr.ip6.addr, 16)) {
3788                 return;
3789             }
3790         }
3791         a = a->next;
3792     }
3793     a = _mdns_result_addr_create_ip(ip);
3794     if (!a) {
3795         return;
3796     }
3797     a->next = r->addr;
3798     r->addr = a;
3799 }
3800 
3801 /**
3802  * @brief  Called from parser to add A/AAAA data to search result
3803  */
_mdns_search_result_add_ip(mdns_search_once_t * search,const char * hostname,esp_ip_addr_t * ip,mdns_if_t tcpip_if,mdns_ip_protocol_t ip_protocol,uint32_t ttl)3804 static void _mdns_search_result_add_ip(mdns_search_once_t * search, const char * hostname, esp_ip_addr_t * ip,
3805                                        mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, uint32_t ttl)
3806 {
3807     mdns_result_t * r = NULL;
3808     mdns_ip_addr_t * a = NULL;
3809 
3810     if ((search->type == MDNS_TYPE_A && ip->type == ESP_IPADDR_TYPE_V4)
3811       || (search->type == MDNS_TYPE_AAAA && ip->type == ESP_IPADDR_TYPE_V6)
3812       || search->type == MDNS_TYPE_ANY) {
3813         r = search->result;
3814         while (r) {
3815             if (r->tcpip_if == tcpip_if && r->ip_protocol == ip_protocol) {
3816                 _mdns_result_add_ip(r, ip);
3817                 _mdns_result_update_ttl(r, ttl);
3818                 return;
3819             }
3820             r = r->next;
3821         }
3822         if (!search->max_results || search->num_results < search->max_results) {
3823             r = (mdns_result_t *)malloc(sizeof(mdns_result_t));
3824             if (!r) {
3825                 HOOK_MALLOC_FAILED;
3826                 return;
3827             }
3828 
3829             memset(r, 0 , sizeof(mdns_result_t));
3830 
3831             a = _mdns_result_addr_create_ip(ip);
3832             if (!a) {
3833                 free(r);
3834                 return;
3835             }
3836             a->next = r->addr;
3837             r->hostname = strdup(hostname);
3838             r->addr = a;
3839             r->tcpip_if = tcpip_if;
3840             r->ip_protocol = ip_protocol;
3841             r->next = search->result;
3842             r->ttl = ttl;
3843             search->result = r;
3844             search->num_results++;
3845         }
3846     } else if (search->type == MDNS_TYPE_PTR || search->type == MDNS_TYPE_SRV) {
3847         r = search->result;
3848         while (r) {
3849             if (r->tcpip_if == tcpip_if && r->ip_protocol == ip_protocol && !_str_null_or_empty(r->hostname) && !strcasecmp(hostname, r->hostname)) {
3850                 _mdns_result_add_ip(r, ip);
3851                 _mdns_result_update_ttl(r, ttl);
3852                 break;
3853             }
3854             r = r->next;
3855         }
3856     }
3857 }
3858 
3859 /**
3860  * @brief  Called from parser to add PTR data to search result
3861  */
_mdns_search_result_add_ptr(mdns_search_once_t * search,const char * instance,const char * service_type,const char * proto,mdns_if_t tcpip_if,mdns_ip_protocol_t ip_protocol,uint32_t ttl)3862 static mdns_result_t * _mdns_search_result_add_ptr(mdns_search_once_t * search, const char * instance,
3863                                                    const char * service_type, const char * proto, mdns_if_t tcpip_if,
3864                                                    mdns_ip_protocol_t ip_protocol, uint32_t ttl)
3865 {
3866     mdns_result_t * r = search->result;
3867     while (r) {
3868         if (r->tcpip_if == tcpip_if && r->ip_protocol == ip_protocol && !_str_null_or_empty(r->instance_name) && !strcasecmp(instance, r->instance_name)) {
3869             _mdns_result_update_ttl(r, ttl);
3870             return r;
3871         }
3872         r = r->next;
3873     }
3874     if (!search->max_results || search->num_results < search->max_results) {
3875         r = (mdns_result_t *)malloc(sizeof(mdns_result_t));
3876         if (!r) {
3877             HOOK_MALLOC_FAILED;
3878             return NULL;
3879         }
3880 
3881         memset(r, 0 , sizeof(mdns_result_t));
3882         r->instance_name = strdup(instance);
3883         r->service_type = strdup(service_type);
3884         r->proto = strdup(proto);
3885         if (!r->instance_name) {
3886             free(r);
3887             return NULL;
3888         }
3889 
3890         r->tcpip_if = tcpip_if;
3891         r->ip_protocol = ip_protocol;
3892         r->ttl = ttl;
3893         r->next = search->result;
3894         search->result = r;
3895         search->num_results++;
3896         return r;
3897     }
3898     return NULL;
3899 }
3900 
3901 /**
3902  * @brief  Called from parser to add SRV data to search result
3903  */
_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,uint32_t ttl)3904 static void _mdns_search_result_add_srv(mdns_search_once_t *search, const char *hostname, uint16_t port,
3905                                         mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, uint32_t ttl)
3906 {
3907     mdns_result_t * r = search->result;
3908     while (r) {
3909         if (r->tcpip_if == tcpip_if && r->ip_protocol == ip_protocol && !_str_null_or_empty(r->hostname) && !strcasecmp(hostname, r->hostname)) {
3910             _mdns_result_update_ttl(r, ttl);
3911             return;
3912         }
3913         r = r->next;
3914     }
3915     if (!search->max_results || search->num_results < search->max_results) {
3916         r = (mdns_result_t *)malloc(sizeof(mdns_result_t));
3917         if (!r) {
3918             HOOK_MALLOC_FAILED;
3919             return;
3920         }
3921 
3922         memset(r, 0 , sizeof(mdns_result_t));
3923         r->hostname = strdup(hostname);
3924         if (!r->hostname) {
3925             free(r);
3926             return;
3927         }
3928         if (search->instance) {
3929             r->instance_name = strdup(search->instance);
3930         }
3931         r->service_type = strdup(search->service);
3932         r->proto = strdup(search->proto);
3933         r->port = port;
3934         r->tcpip_if = tcpip_if;
3935         r->ip_protocol = ip_protocol;
3936         r->ttl = ttl;
3937         r->next = search->result;
3938         search->result = r;
3939         search->num_results++;
3940     }
3941 }
3942 
3943 /**
3944  * @brief  Called from parser to add TXT data to search result
3945  */
_mdns_search_result_add_txt(mdns_search_once_t * search,mdns_txt_item_t * txt,uint8_t * txt_value_len,size_t txt_count,mdns_if_t tcpip_if,mdns_ip_protocol_t ip_protocol,uint32_t ttl)3946 static void _mdns_search_result_add_txt(mdns_search_once_t *search, mdns_txt_item_t *txt, uint8_t *txt_value_len,
3947                                         size_t txt_count, mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol,
3948                                         uint32_t ttl)
3949 {
3950     mdns_result_t * r = search->result;
3951     while (r) {
3952         if (r->tcpip_if == tcpip_if && r->ip_protocol == ip_protocol) {
3953             if (r->txt) {
3954                 goto free_txt;
3955             }
3956             r->txt = txt;
3957             r->txt_value_len = txt_value_len;
3958             r->txt_count = txt_count;
3959             _mdns_result_update_ttl(r, ttl);
3960             return;
3961         }
3962         r = r->next;
3963     }
3964     if (!search->max_results || search->num_results < search->max_results) {
3965         r = (mdns_result_t *)malloc(sizeof(mdns_result_t));
3966         if (!r) {
3967             HOOK_MALLOC_FAILED;
3968             goto free_txt;
3969         }
3970 
3971         memset(r, 0 , sizeof(mdns_result_t));
3972         r->txt = txt;
3973         r->txt_value_len = txt_value_len;
3974         r->txt_count = txt_count;
3975         r->tcpip_if = tcpip_if;
3976         r->ip_protocol = ip_protocol;
3977         r->ttl = ttl;
3978         r->next = search->result;
3979         search->result = r;
3980         search->num_results++;
3981     }
3982     return;
3983 
3984 free_txt:
3985     for (size_t i=0; i<txt_count; i++) {
3986         free((char *)(txt[i].key));
3987         free((char *)(txt[i].value));
3988     }
3989     free(txt);
3990 }
3991 
3992 /**
3993  * @brief  Called from packet parser to find matching running search
3994  */
_mdns_search_find_from(mdns_search_once_t * s,mdns_name_t * name,uint16_t type,mdns_if_t tcpip_if,mdns_ip_protocol_t ip_protocol)3995 static mdns_search_once_t * _mdns_search_find_from(mdns_search_once_t * s, mdns_name_t * name, uint16_t type, mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
3996 {
3997     mdns_result_t * r = NULL;
3998     while (s) {
3999         if (s->state == SEARCH_OFF) {
4000             s = s->next;
4001             continue;
4002         }
4003 
4004         if (type == MDNS_TYPE_A || type == MDNS_TYPE_AAAA) {
4005             if ((s->type == MDNS_TYPE_ANY && s->service != NULL)
4006                 || (s->type != MDNS_TYPE_ANY && s->type != type && s->type != MDNS_TYPE_PTR && s->type != MDNS_TYPE_SRV))
4007             {
4008                 s = s->next;
4009                 continue;
4010             }
4011             if (s->type != MDNS_TYPE_PTR && s->type != MDNS_TYPE_SRV) {
4012                 if (!strcasecmp(name->host, s->instance)) {
4013                     return s;
4014                 }
4015                 s = s->next;
4016                 continue;
4017             }
4018             r = s->result;
4019             while (r) {
4020                 if (r->tcpip_if == tcpip_if && r->ip_protocol == ip_protocol && !_str_null_or_empty(r->hostname) && !strcasecmp(name->host, r->hostname)) {
4021                     return s;
4022                 }
4023                 r = r->next;
4024             }
4025             s = s->next;
4026             continue;
4027         }
4028 
4029         if (type == MDNS_TYPE_SRV || type == MDNS_TYPE_TXT) {
4030             if ((s->type == MDNS_TYPE_ANY && s->service == NULL)
4031                 || (s->type != MDNS_TYPE_ANY && s->type != type && s->type != MDNS_TYPE_PTR))
4032             {
4033                 s = s->next;
4034                 continue;
4035             }
4036             if (strcasecmp(name->service, s->service)
4037                 || strcasecmp(name->proto, s->proto))
4038             {
4039                 s = s->next;
4040                 continue;
4041             }
4042             if (s->type != MDNS_TYPE_PTR) {
4043                 if (!strcasecmp(name->host, s->instance)) {
4044                     return s;
4045                 }
4046                 s = s->next;
4047                 continue;
4048             }
4049             return s;
4050         }
4051 
4052         if (type == MDNS_TYPE_PTR && type == s->type && !strcasecmp(name->service, s->service) && !strcasecmp(name->proto, s->proto)) {
4053             return s;
4054         }
4055 
4056         s = s->next;
4057     }
4058 
4059     return NULL;
4060 }
4061 
4062 /**
4063  * @brief  Create search packet for particular interface
4064  */
_mdns_create_search_packet(mdns_search_once_t * search,mdns_if_t tcpip_if,mdns_ip_protocol_t ip_protocol)4065 static mdns_tx_packet_t * _mdns_create_search_packet(mdns_search_once_t * search, mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
4066 {
4067     mdns_result_t * r = NULL;
4068     mdns_tx_packet_t * packet = _mdns_alloc_packet_default(tcpip_if, ip_protocol);
4069     if (!packet) {
4070         return NULL;
4071     }
4072 
4073     mdns_out_question_t * q = (mdns_out_question_t *)malloc(sizeof(mdns_out_question_t));
4074     if (!q) {
4075         HOOK_MALLOC_FAILED;
4076         _mdns_free_tx_packet(packet);
4077         return NULL;
4078     }
4079     q->next = NULL;
4080     q->unicast = search->type != MDNS_TYPE_PTR;
4081     q->type = search->type;
4082     q->host = search->instance;
4083     q->service = search->service;
4084     q->proto = search->proto;
4085     q->domain = MDNS_DEFAULT_DOMAIN;
4086     q->own_dynamic_memory = false;
4087     queueToEnd(mdns_out_question_t, packet->questions, q);
4088 
4089     if (search->type == MDNS_TYPE_PTR) {
4090         r = search->result;
4091         while (r) {
4092             //full record on the same interface is available
4093             if (r->tcpip_if != tcpip_if || r->ip_protocol != ip_protocol || r->instance_name == NULL || r->hostname == NULL || r->addr == NULL) {
4094                 r = r->next;
4095                 continue;
4096             }
4097             mdns_out_answer_t * a = (mdns_out_answer_t *)malloc(sizeof(mdns_out_answer_t));
4098             if (!a) {
4099                 HOOK_MALLOC_FAILED;
4100                 _mdns_free_tx_packet(packet);
4101                 return NULL;
4102             }
4103             a->type = MDNS_TYPE_PTR;
4104             a->service = NULL;
4105             a->custom_instance = r->instance_name;
4106             a->custom_service = search->service;
4107             a->custom_proto = search->proto;
4108             a->bye = false;
4109             a->flush = false;
4110             a->next = NULL;
4111             queueToEnd(mdns_out_answer_t, packet->answers, a);
4112             r = r->next;
4113         }
4114     }
4115 
4116     return packet;
4117 }
4118 
4119 /**
4120  * @brief  Send search packet to particular interface
4121  */
_mdns_search_send_pcb(mdns_search_once_t * search,mdns_if_t tcpip_if,mdns_ip_protocol_t ip_protocol)4122 static void _mdns_search_send_pcb(mdns_search_once_t * search, mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
4123 {
4124     mdns_tx_packet_t * packet = NULL;
4125     if (_mdns_server->interfaces[tcpip_if].pcbs[ip_protocol].pcb && _mdns_server->interfaces[tcpip_if].pcbs[ip_protocol].state > PCB_INIT) {
4126         packet = _mdns_create_search_packet(search, tcpip_if, ip_protocol);
4127         if (!packet) {
4128             return;
4129         }
4130         _mdns_dispatch_tx_packet(packet);
4131         _mdns_free_tx_packet(packet);
4132     }
4133 }
4134 
4135 /**
4136  * @brief  Send search packet to all available interfaces
4137  */
_mdns_search_send(mdns_search_once_t * search)4138 static void _mdns_search_send(mdns_search_once_t * search)
4139 {
4140     mdns_search_once_t* queue = _mdns_server->search_once;
4141     bool found = false;
4142     // looking for this search in active searches
4143     while (queue) {
4144         if (queue == search) {
4145             found = true;
4146             break;
4147         }
4148         queue = queue->next;
4149     }
4150 
4151     if (!found) {
4152         // no longer active -> skip sending this search
4153         return;
4154     }
4155 
4156     uint8_t i, j;
4157     for (i=0; i<MDNS_IF_MAX; i++) {
4158         for (j=0; j<MDNS_IP_PROTOCOL_MAX; j++) {
4159             _mdns_search_send_pcb(search, (mdns_if_t)i, (mdns_ip_protocol_t)j);
4160         }
4161     }
4162 }
4163 
_mdns_tx_handle_packet(mdns_tx_packet_t * p)4164 static void _mdns_tx_handle_packet(mdns_tx_packet_t * p)
4165 {
4166     mdns_tx_packet_t * a = NULL;
4167     mdns_out_question_t * q = NULL;
4168     mdns_pcb_t * pcb = &_mdns_server->interfaces[p->tcpip_if].pcbs[p->ip_protocol];
4169     uint32_t send_after = 1000;
4170 
4171     if (pcb->state == PCB_OFF) {
4172         _mdns_free_tx_packet(p);
4173         return;
4174     }
4175     _mdns_dispatch_tx_packet(p);
4176 
4177     switch(pcb->state) {
4178     case PCB_PROBE_1:
4179         q = p->questions;
4180         while (q) {
4181             q->unicast = false;
4182             q = q->next;
4183         }
4184         //fallthrough
4185     case PCB_PROBE_2:
4186         _mdns_schedule_tx_packet(p, 250);
4187         pcb->state = (mdns_pcb_state_t)((uint8_t)(pcb->state) + 1);
4188         break;
4189     case PCB_PROBE_3:
4190         a = _mdns_create_announce_from_probe(p);
4191         if (!a) {
4192             _mdns_schedule_tx_packet(p, 250);
4193             break;
4194         }
4195         pcb->probe_running = false;
4196         pcb->probe_ip = false;
4197         pcb->probe_services_len = 0;
4198         pcb->failed_probes = 0;
4199         free(pcb->probe_services);
4200         pcb->probe_services = NULL;
4201         _mdns_free_tx_packet(p);
4202         p = a;
4203         send_after = 250;
4204         //fallthrough
4205     case PCB_ANNOUNCE_1:
4206         //fallthrough
4207     case PCB_ANNOUNCE_2:
4208         _mdns_schedule_tx_packet(p, send_after);
4209         pcb->state = (mdns_pcb_state_t)((uint8_t)(pcb->state) + 1);
4210         break;
4211     case PCB_ANNOUNCE_3:
4212         pcb->state = PCB_RUNNING;
4213         _mdns_free_tx_packet(p);
4214         break;
4215     default:
4216         _mdns_free_tx_packet(p);
4217         break;
4218     }
4219 }
4220 
_mdns_remap_self_service_hostname(const char * old_hostname,const char * new_hostname)4221 static void _mdns_remap_self_service_hostname(const char * old_hostname, const char * new_hostname)
4222 {
4223     mdns_srv_item_t * service = _mdns_server->services;
4224 
4225     while (service) {
4226         if (strcmp(service->service->hostname, old_hostname) == 0) {
4227             free((char *)service->service->hostname);
4228             service->service->hostname = strdup(new_hostname);
4229         }
4230         service = service->next;
4231     }
4232 }
4233 
4234 /**
4235  * @brief  Free action data
4236  */
_mdns_free_action(mdns_action_t * action)4237 static void _mdns_free_action(mdns_action_t * action)
4238 {
4239     switch(action->type) {
4240     case ACTION_HOSTNAME_SET:
4241         free(action->data.hostname_set.hostname);
4242         break;
4243     case ACTION_INSTANCE_SET:
4244         free(action->data.instance);
4245         break;
4246     case ACTION_SERVICE_ADD:
4247         _mdns_free_service(action->data.srv_add.service->service);
4248         free(action->data.srv_add.service);
4249         break;
4250     case ACTION_SERVICE_INSTANCE_SET:
4251         free(action->data.srv_instance.instance);
4252         break;
4253     case ACTION_SERVICE_TXT_REPLACE:
4254         _mdns_free_linked_txt(action->data.srv_txt_replace.txt);
4255         break;
4256     case ACTION_SERVICE_TXT_SET:
4257         free(action->data.srv_txt_set.key);
4258         free(action->data.srv_txt_set.value);
4259         break;
4260     case ACTION_SERVICE_TXT_DEL:
4261         free(action->data.srv_txt_del.key);
4262         break;
4263     case ACTION_SEARCH_ADD:
4264         //fallthrough
4265     case ACTION_SEARCH_SEND:
4266         //fallthrough
4267     case ACTION_SEARCH_END:
4268         _mdns_search_free(action->data.search_add.search);
4269         break;
4270     case ACTION_TX_HANDLE:
4271         _mdns_free_tx_packet(action->data.tx_handle.packet);
4272         break;
4273     case ACTION_RX_HANDLE:
4274         _mdns_packet_free(action->data.rx_handle.packet);
4275         break;
4276     case ACTION_DELEGATE_HOSTNAME_ADD:
4277         free((char *)action->data.delegate_hostname.hostname);
4278         free_address_list(action->data.delegate_hostname.address_list);
4279         break;
4280     case ACTION_DELEGATE_HOSTNAME_REMOVE:
4281         free((char *)action->data.delegate_hostname.hostname);
4282         break;
4283     default:
4284         break;
4285     }
4286     free(action);
4287 }
4288 
4289 /**
4290  * @brief  Called from service thread to execute given action
4291  */
_mdns_execute_action(mdns_action_t * action)4292 static void _mdns_execute_action(mdns_action_t * action)
4293 {
4294     mdns_srv_item_t * a = NULL;
4295     mdns_service_t * service;
4296     char * key;
4297     char * value;
4298     mdns_txt_linked_item_t * txt, * t;
4299 
4300     switch(action->type) {
4301     case ACTION_SYSTEM_EVENT:
4302         _mdns_handle_system_event(action->data.sys_event.event_base,
4303             action->data.sys_event.event_id, action->data.sys_event.interface);
4304         break;
4305     case ACTION_HOSTNAME_SET:
4306         _mdns_send_bye_all_pcbs_no_instance(true);
4307         _mdns_remap_self_service_hostname(_mdns_server->hostname, action->data.hostname_set.hostname);
4308         free((char*)_mdns_server->hostname);
4309         _mdns_server->hostname = action->data.hostname_set.hostname;
4310         _mdns_self_host.hostname = action->data.hostname_set.hostname;
4311         _mdns_restart_all_pcbs();
4312         xTaskNotifyGive(action->data.hostname_set.calling_task);
4313         break;
4314     case ACTION_INSTANCE_SET:
4315         _mdns_send_bye_all_pcbs_no_instance(false);
4316         free((char*)_mdns_server->instance);
4317         _mdns_server->instance = action->data.instance;
4318         _mdns_restart_all_pcbs_no_instance();
4319 
4320         break;
4321     case ACTION_SERVICE_ADD:
4322         action->data.srv_add.service->next = _mdns_server->services;
4323         _mdns_server->services = action->data.srv_add.service;
4324         _mdns_probe_all_pcbs(&action->data.srv_add.service, 1, false, false);
4325         break;
4326     case ACTION_SERVICE_INSTANCE_SET:
4327         if (action->data.srv_instance.service->service->instance) {
4328             _mdns_send_bye(&action->data.srv_instance.service, 1, false);
4329             free((char*)action->data.srv_instance.service->service->instance);
4330         }
4331         action->data.srv_instance.service->service->instance = action->data.srv_instance.instance;
4332         _mdns_probe_all_pcbs(&action->data.srv_instance.service, 1, false, false);
4333 
4334         break;
4335     case ACTION_SERVICE_PORT_SET:
4336         action->data.srv_port.service->service->port = action->data.srv_port.port;
4337         _mdns_announce_all_pcbs(&action->data.srv_port.service, 1, true);
4338 
4339         break;
4340     case ACTION_SERVICE_TXT_REPLACE:
4341         service = action->data.srv_txt_replace.service->service;
4342         txt = service->txt;
4343         service->txt = NULL;
4344         _mdns_free_linked_txt(txt);
4345         service->txt = action->data.srv_txt_replace.txt;
4346         _mdns_announce_all_pcbs(&action->data.srv_txt_replace.service, 1, false);
4347 
4348         break;
4349     case ACTION_SERVICE_TXT_SET:
4350         service = action->data.srv_txt_set.service->service;
4351         key = action->data.srv_txt_set.key;
4352         value = action->data.srv_txt_set.value;
4353         txt = service->txt;
4354         while (txt) {
4355             if (strcmp(txt->key, key) == 0) {
4356                 free((char *)txt->value);
4357                 free(key);
4358                 txt->value = value;
4359                 txt->value_len = action->data.srv_txt_set.value_len;
4360                 break;
4361             }
4362             txt = txt->next;
4363         }
4364         if (!txt) {
4365             txt = (mdns_txt_linked_item_t *)malloc(sizeof(mdns_txt_linked_item_t));
4366             if (!txt) {
4367                 HOOK_MALLOC_FAILED;
4368                 _mdns_free_action(action);
4369                 return;
4370             }
4371             txt->key = key;
4372             txt->value = value;
4373             txt->value_len = action->data.srv_txt_set.value_len;
4374             txt->next = service->txt;
4375             service->txt = txt;
4376         }
4377 
4378         _mdns_announce_all_pcbs(&action->data.srv_txt_set.service, 1, false);
4379 
4380         break;
4381     case ACTION_SERVICE_TXT_DEL:
4382         service = action->data.srv_txt_del.service->service;
4383         key = action->data.srv_txt_del.key;
4384         txt = service->txt;
4385         if (!txt) {
4386             break;
4387         }
4388         if (strcmp(txt->key, key) == 0) {
4389             service->txt = txt->next;
4390             free((char *)txt->key);
4391             free((char *)txt->value);
4392             free(txt);
4393         } else {
4394             while (txt->next) {
4395                 if (strcmp(txt->next->key, key) == 0) {
4396                     t = txt->next;
4397                     txt->next = t->next;
4398                     free((char *)t->key);
4399                     free((char *)t->value);
4400                     free(t);
4401                     break;
4402                 } else {
4403                     txt = txt->next;
4404                 }
4405             }
4406         }
4407         free(key);
4408 
4409         _mdns_announce_all_pcbs(&action->data.srv_txt_set.service, 1, false);
4410 
4411         break;
4412     case ACTION_SERVICE_DEL:
4413         a = _mdns_server->services;
4414         if (action->data.srv_del.service) {
4415             if (_mdns_server->services == action->data.srv_del.service) {
4416                 _mdns_server->services = a->next;
4417                 _mdns_send_bye(&a, 1, false);
4418                 _mdns_remove_scheduled_service_packets(a->service);
4419                 _mdns_free_service(a->service);
4420                 free(a);
4421             } else {
4422                 while (a->next && a->next != action->data.srv_del.service) {
4423                     a = a->next;
4424                 }
4425                 if (a->next == action->data.srv_del.service) {
4426                     mdns_srv_item_t * b = a->next;
4427                     a->next = a->next->next;
4428                     _mdns_send_bye(&b, 1, false);
4429                     _mdns_remove_scheduled_service_packets(b->service);
4430                     _mdns_free_service(b->service);
4431                     free(b);
4432                 }
4433             }
4434         }
4435 
4436         break;
4437     case ACTION_SERVICES_CLEAR:
4438         _mdns_send_final_bye(false);
4439         a = _mdns_server->services;
4440         _mdns_server->services = NULL;
4441         while (a) {
4442             mdns_srv_item_t * s = a;
4443             a = a->next;
4444             _mdns_remove_scheduled_service_packets(s->service);
4445             _mdns_free_service(s->service);
4446             free(s);
4447         }
4448 
4449         break;
4450     case ACTION_SEARCH_ADD:
4451         _mdns_search_add(action->data.search_add.search);
4452         break;
4453     case ACTION_SEARCH_SEND:
4454         _mdns_search_send(action->data.search_add.search);
4455         break;
4456     case ACTION_SEARCH_END:
4457         _mdns_search_finish(action->data.search_add.search);
4458         break;
4459     case ACTION_TX_HANDLE:
4460         {
4461             mdns_tx_packet_t * p = _mdns_server->tx_queue_head;
4462             // packet to be handled should be at tx head, but must be consistent with the one pushed to action queue
4463             if (p && p==action->data.tx_handle.packet && p->queued) {
4464                 p->queued = false; // clearing, as the packet might be reused (pushed and transmitted again)
4465                 _mdns_server->tx_queue_head = p->next;
4466                 _mdns_tx_handle_packet(p);
4467             } else {
4468                 ESP_LOGD(TAG, "Skipping transmit of an unexpected packet!");
4469             }
4470         }
4471         break;
4472     case ACTION_RX_HANDLE:
4473         mdns_parse_packet(action->data.rx_handle.packet);
4474         _mdns_packet_free(action->data.rx_handle.packet);
4475         break;
4476     case ACTION_DELEGATE_HOSTNAME_ADD:
4477         _mdns_delegate_hostname_add(action->data.delegate_hostname.hostname,
4478                                     action->data.delegate_hostname.address_list);
4479         break;
4480     case ACTION_DELEGATE_HOSTNAME_REMOVE:
4481         _mdns_delegate_hostname_remove(action->data.delegate_hostname.hostname);
4482         free((char *)action->data.delegate_hostname.hostname);
4483         break;
4484     default:
4485         break;
4486     }
4487     free(action);
4488 }
4489 
4490 /**
4491  * @brief  Queue search action
4492  */
_mdns_send_search_action(mdns_action_type_t type,mdns_search_once_t * search)4493 static esp_err_t _mdns_send_search_action(mdns_action_type_t type, mdns_search_once_t * search)
4494 {
4495     mdns_action_t * action = NULL;
4496 
4497     action = (mdns_action_t *)malloc(sizeof(mdns_action_t));
4498     if (!action) {
4499         HOOK_MALLOC_FAILED;
4500         return ESP_ERR_NO_MEM;
4501     }
4502 
4503     action->type = type;
4504     action->data.search_add.search = search;
4505     if (xQueueSend(_mdns_server->action_queue, &action, (portTickType)0) != pdPASS) {
4506         free(action);
4507         return ESP_ERR_NO_MEM;
4508     }
4509     return ESP_OK;
4510 }
4511 
4512 /**
4513  * @brief  Called from timer task to run mDNS responder
4514  *
4515  * periodically checks first unqueued packet (from tx head).
4516  * if it is scheduled to be transmitted, then pushes the packet to action queue to be handled.
4517  *
4518  */
_mdns_scheduler_run(void)4519 static void _mdns_scheduler_run(void)
4520 {
4521     MDNS_SERVICE_LOCK();
4522     mdns_tx_packet_t * p = _mdns_server->tx_queue_head;
4523     mdns_action_t * action = NULL;
4524 
4525     // find first unqueued packet
4526     while (p && p->queued) {
4527         p = p->next;
4528     }
4529     if (!p) {
4530         MDNS_SERVICE_UNLOCK();
4531         return;
4532     }
4533     if ((int32_t)(p->send_at - (xTaskGetTickCount() * portTICK_PERIOD_MS)) < 0) {
4534         action = (mdns_action_t *)malloc(sizeof(mdns_action_t));
4535         if (action) {
4536             action->type = ACTION_TX_HANDLE;
4537             action->data.tx_handle.packet = p;
4538             p->queued = true;
4539             if (xQueueSend(_mdns_server->action_queue, &action, (portTickType)0) != pdPASS) {
4540                 free(action);
4541                 p->queued = false;
4542             }
4543         } else {
4544             HOOK_MALLOC_FAILED;
4545             // continue
4546         }
4547     }
4548     MDNS_SERVICE_UNLOCK();
4549 }
4550 
4551 /**
4552  * @brief  Called from timer task to run active searches
4553  */
_mdns_search_run(void)4554 static void _mdns_search_run(void)
4555 {
4556     MDNS_SERVICE_LOCK();
4557     mdns_search_once_t * s = _mdns_server->search_once;
4558     uint32_t now = xTaskGetTickCount() * portTICK_PERIOD_MS;
4559     if (!s) {
4560         MDNS_SERVICE_UNLOCK();
4561         return;
4562     }
4563     while (s) {
4564         if (s->state != SEARCH_OFF) {
4565             if (now > (s->started_at + s->timeout)) {
4566                 s->state = SEARCH_OFF;
4567                 if (_mdns_send_search_action(ACTION_SEARCH_END, s) != ESP_OK) {
4568                     s->state = SEARCH_RUNNING;
4569                 }
4570             } else if (s->state == SEARCH_INIT || (now - s->sent_at) > 1000) {
4571                 s->state = SEARCH_RUNNING;
4572                 s->sent_at = now;
4573                 if (_mdns_send_search_action(ACTION_SEARCH_SEND, s) != ESP_OK) {
4574                     s->sent_at -= 1000;
4575                 }
4576             }
4577         }
4578         s = s->next;
4579     }
4580     MDNS_SERVICE_UNLOCK();
4581 }
4582 
4583 /**
4584  * @brief  the main MDNS service task. Packets are received and parsed here
4585  */
_mdns_service_task(void * pvParameters)4586 static void _mdns_service_task(void *pvParameters)
4587 {
4588     mdns_action_t * a = NULL;
4589     for (;;) {
4590         if (_mdns_server && _mdns_server->action_queue) {
4591             if (xQueueReceive(_mdns_server->action_queue, &a, portMAX_DELAY) == pdTRUE) {
4592                 if (a->type == ACTION_TASK_STOP) {
4593                     break;
4594                 }
4595                 MDNS_SERVICE_LOCK();
4596                 _mdns_execute_action(a);
4597                 MDNS_SERVICE_UNLOCK();
4598             }
4599         } else {
4600             vTaskDelay(500 * portTICK_PERIOD_MS);
4601         }
4602     }
4603     _mdns_service_task_handle = NULL;
4604     vTaskDelete(NULL);
4605 }
4606 
_mdns_timer_cb(void * arg)4607 static void _mdns_timer_cb(void * arg)
4608 {
4609     _mdns_scheduler_run();
4610     _mdns_search_run();
4611 }
4612 
_mdns_start_timer(void)4613 static esp_err_t _mdns_start_timer(void){
4614     esp_timer_create_args_t timer_conf = {
4615         .callback = _mdns_timer_cb,
4616         .arg = NULL,
4617         .dispatch_method = ESP_TIMER_TASK,
4618         .name = "mdns_timer"
4619     };
4620     esp_err_t err = esp_timer_create(&timer_conf, &(_mdns_server->timer_handle));
4621     if (err) {
4622         return err;
4623     }
4624     return esp_timer_start_periodic(_mdns_server->timer_handle, MDNS_TIMER_PERIOD_US);
4625 }
4626 
_mdns_stop_timer(void)4627 static esp_err_t _mdns_stop_timer(void){
4628     esp_err_t err = ESP_OK;
4629     if (_mdns_server->timer_handle) {
4630         err = esp_timer_stop(_mdns_server->timer_handle);
4631         if (err) {
4632             return err;
4633         }
4634         err = esp_timer_delete(_mdns_server->timer_handle);
4635     }
4636     return err;
4637 }
4638 
4639 /**
4640  * @brief  Start the service thread if not running
4641  *
4642  * @return
4643  *      - ESP_OK on success
4644  *      - ESP_FAIL on error
4645  */
_mdns_service_task_start(void)4646 static esp_err_t _mdns_service_task_start(void)
4647 {
4648     if (!_mdns_service_semaphore) {
4649         _mdns_service_semaphore = xSemaphoreCreateMutex();
4650         if (!_mdns_service_semaphore) {
4651             return ESP_FAIL;
4652         }
4653     }
4654     MDNS_SERVICE_LOCK();
4655     if (_mdns_start_timer()) {
4656         MDNS_SERVICE_UNLOCK();
4657         return ESP_FAIL;
4658     }
4659     if (!_mdns_service_task_handle) {
4660         xTaskCreatePinnedToCore(_mdns_service_task, "mdns", MDNS_SERVICE_STACK_DEPTH, NULL, MDNS_TASK_PRIORITY,
4661                                 (TaskHandle_t * const)(&_mdns_service_task_handle), MDNS_TASK_AFFINITY);
4662         if (!_mdns_service_task_handle) {
4663             _mdns_stop_timer();
4664             MDNS_SERVICE_UNLOCK();
4665             vSemaphoreDelete(_mdns_service_semaphore);
4666             _mdns_service_semaphore = NULL;
4667             return ESP_FAIL;
4668         }
4669     }
4670     MDNS_SERVICE_UNLOCK();
4671     return ESP_OK;
4672 }
4673 
4674 /**
4675  * @brief  Stop the service thread
4676  *
4677  * @return
4678  *      - ESP_OK
4679  */
_mdns_service_task_stop(void)4680 static esp_err_t _mdns_service_task_stop(void)
4681 {
4682     _mdns_stop_timer();
4683     if (_mdns_service_task_handle) {
4684         mdns_action_t action;
4685         mdns_action_t * a = &action;
4686         action.type = ACTION_TASK_STOP;
4687         if (xQueueSend(_mdns_server->action_queue, &a, (portTickType)0) != pdPASS) {
4688             vTaskDelete(_mdns_service_task_handle);
4689             _mdns_service_task_handle = NULL;
4690         }
4691         while (_mdns_service_task_handle) {
4692             vTaskDelay(10 / portTICK_PERIOD_MS);
4693         }
4694     }
4695     vSemaphoreDelete(_mdns_service_semaphore);
4696     _mdns_service_semaphore = NULL;
4697     return ESP_OK;
4698 }
4699 
4700 /*
4701  * Public Methods
4702  * */
4703 
mdns_handle_system_event(void * ctx,system_event_t * event)4704 esp_err_t mdns_handle_system_event(void *ctx, system_event_t *event)
4705 {
4706     /* no-op, kept for compatibility */
4707     return ESP_OK;
4708 }
4709 
event_handler(void * arg,esp_event_base_t event_base,int32_t event_id,void * event_data)4710 static void event_handler(void* arg, esp_event_base_t event_base,
4711                      int32_t event_id, void* event_data)
4712 {
4713     if (!_mdns_server) {
4714         return;
4715     }
4716 
4717     mdns_action_t * action = (mdns_action_t *)calloc(1, sizeof(mdns_action_t));
4718     if (!action) {
4719         HOOK_MALLOC_FAILED;
4720         return;
4721     }
4722     action->type = ACTION_SYSTEM_EVENT;
4723     action->data.sys_event.event_base = event_base;
4724     action->data.sys_event.event_id = event_id;
4725     if (event_base == IP_EVENT && event_id == IP_EVENT_GOT_IP6) {
4726         ip_event_got_ip6_t* event = (ip_event_got_ip6_t*) event_data;
4727         action->data.sys_event.interface = event->esp_netif;
4728     }
4729 
4730     if (xQueueSend(_mdns_server->action_queue, &action, (portTickType)0) != pdPASS) {
4731         free(action);
4732     }
4733 }
4734 
mdns_init(void)4735 esp_err_t mdns_init(void)
4736 {
4737     esp_err_t err = ESP_OK;
4738 
4739     if (_mdns_server) {
4740         return err;
4741     }
4742 
4743     _mdns_server = (mdns_server_t *)malloc(sizeof(mdns_server_t));
4744     if (!_mdns_server) {
4745         HOOK_MALLOC_FAILED;
4746         return ESP_ERR_NO_MEM;
4747     }
4748     memset((uint8_t*)_mdns_server, 0, sizeof(mdns_server_t));
4749     // zero-out local copy of netifs to initiate a fresh search by interface key whenever a netif ptr is needed
4750     memset(s_esp_netifs, 0, sizeof(s_esp_netifs));
4751 
4752     _mdns_server->lock = xSemaphoreCreateMutex();
4753     if (!_mdns_server->lock) {
4754         err = ESP_ERR_NO_MEM;
4755         goto free_server;
4756     }
4757 
4758     _mdns_server->action_queue = xQueueCreate(MDNS_ACTION_QUEUE_LEN, sizeof(mdns_action_t *));
4759     if (!_mdns_server->action_queue) {
4760         err = ESP_ERR_NO_MEM;
4761         goto free_lock;
4762     }
4763     if ((err = esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL)) != ESP_OK) {
4764         goto free_event_handlers;
4765     }
4766     if ((err = esp_event_handler_register(IP_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL)) != ESP_OK) {
4767         goto free_event_handlers;
4768     }
4769 #if CONFIG_ETH_ENABLED
4770     if ((err = esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL)) != ESP_OK) {
4771         goto free_event_handlers;
4772     }
4773 #endif
4774     uint8_t i;
4775 #if CONFIG_LWIP_IPV6
4776     esp_ip6_addr_t tmp_addr6;
4777 #endif
4778     esp_netif_ip_info_t if_ip_info;
4779 
4780     for (i=0; i<MDNS_IF_MAX; i++) {
4781 #if CONFIG_LWIP_IPV6
4782         if (!esp_netif_get_ip6_linklocal(_mdns_get_esp_netif(i), &tmp_addr6) && !_ipv6_address_is_zero(tmp_addr6)) {
4783             _mdns_enable_pcb(i, MDNS_IP_PROTOCOL_V6);
4784         }
4785 #endif
4786         if (!esp_netif_get_ip_info(_mdns_get_esp_netif(i), &if_ip_info) && if_ip_info.ip.addr) {
4787             _mdns_enable_pcb(i, MDNS_IP_PROTOCOL_V4);
4788         }
4789     }
4790 
4791     if (_mdns_service_task_start()) {
4792         //service start failed!
4793         err = ESP_FAIL;
4794         goto free_all_and_disable_pcbs;
4795     }
4796 
4797     return ESP_OK;
4798 
4799 free_all_and_disable_pcbs:
4800     for (i=0; i<MDNS_IF_MAX; i++) {
4801         _mdns_disable_pcb(i, MDNS_IP_PROTOCOL_V6);
4802         _mdns_disable_pcb(i, MDNS_IP_PROTOCOL_V4);
4803     }
4804 free_event_handlers:
4805     esp_event_handler_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler);
4806     esp_event_handler_unregister(IP_EVENT, ESP_EVENT_ANY_ID, &event_handler);
4807 #if CONFIG_ETH_ENABLED
4808     esp_event_handler_unregister(ETH_EVENT, ESP_EVENT_ANY_ID, &event_handler);
4809 #endif
4810     vQueueDelete(_mdns_server->action_queue);
4811 free_lock:
4812     vSemaphoreDelete(_mdns_server->lock);
4813 free_server:
4814     free(_mdns_server);
4815     _mdns_server = NULL;
4816     return err;
4817 }
4818 
mdns_free(void)4819 void mdns_free(void)
4820 {
4821     uint8_t i, j;
4822     if (!_mdns_server) {
4823         return;
4824     }
4825 
4826     // Unregister handlers before destroying the mdns internals to avoid receiving async events while deinit
4827     esp_event_handler_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler);
4828     esp_event_handler_unregister(IP_EVENT, ESP_EVENT_ANY_ID, &event_handler);
4829 #if CONFIG_ETH_ENABLED
4830     esp_event_handler_unregister(ETH_EVENT, ESP_EVENT_ANY_ID, &event_handler);
4831 #endif
4832 
4833     mdns_service_remove_all();
4834     _mdns_service_task_stop();
4835     for (i=0; i<MDNS_IF_MAX; i++) {
4836         for (j=0; j<MDNS_IP_PROTOCOL_MAX; j++) {
4837             _mdns_pcb_deinit(i, j);
4838         }
4839     }
4840     free((char*)_mdns_server->hostname);
4841     free((char*)_mdns_server->instance);
4842     if (_mdns_server->action_queue) {
4843         mdns_action_t * c;
4844         while (xQueueReceive(_mdns_server->action_queue, &c, 0) == pdTRUE) {
4845             _mdns_free_action(c);
4846         }
4847         vQueueDelete(_mdns_server->action_queue);
4848     }
4849     _mdns_clear_tx_queue_head();
4850     while (_mdns_server->search_once) {
4851         mdns_search_once_t * h = _mdns_server->search_once;
4852         _mdns_server->search_once = h->next;
4853         free(h->instance);
4854         free(h->service);
4855         free(h->proto);
4856         vSemaphoreDelete(h->done_semaphore);
4857         if (h->result) {
4858             mdns_query_results_free(h->result);
4859         }
4860         free(h);
4861     }
4862     vSemaphoreDelete(_mdns_server->lock);
4863     free(_mdns_server);
4864     _mdns_server = NULL;
4865 }
4866 
mdns_hostname_set(const char * hostname)4867 esp_err_t mdns_hostname_set(const char * hostname)
4868 {
4869     if (!_mdns_server) {
4870         return ESP_ERR_INVALID_ARG;
4871     }
4872     if (_str_null_or_empty(hostname) || strlen(hostname) > (MDNS_NAME_BUF_LEN - 1)) {
4873         return ESP_ERR_INVALID_ARG;
4874     }
4875     char * new_hostname = strndup(hostname, MDNS_NAME_BUF_LEN - 1);
4876     if (!new_hostname) {
4877         return ESP_ERR_NO_MEM;
4878     }
4879 
4880     mdns_action_t * action = (mdns_action_t *)malloc(sizeof(mdns_action_t));
4881     if (!action) {
4882         HOOK_MALLOC_FAILED;
4883         free(new_hostname);
4884         return ESP_ERR_NO_MEM;
4885     }
4886     action->type = ACTION_HOSTNAME_SET;
4887     action->data.hostname_set.hostname = new_hostname;
4888     action->data.hostname_set.calling_task = xTaskGetCurrentTaskHandle();
4889     if (xQueueSend(_mdns_server->action_queue, &action, (portTickType)0) != pdPASS) {
4890         free(new_hostname);
4891         free(action);
4892         return ESP_ERR_NO_MEM;
4893     }
4894     xTaskNotifyWait(0, 0, NULL, portMAX_DELAY);
4895     return ESP_OK;
4896 }
4897 
mdns_delegate_hostname_add(const char * hostname,const mdns_ip_addr_t * address_list)4898 esp_err_t mdns_delegate_hostname_add(const char * hostname, const mdns_ip_addr_t * address_list)
4899 {
4900     if (!_mdns_server) {
4901         return ESP_ERR_INVALID_STATE;
4902     }
4903     if (_str_null_or_empty(hostname) || strlen(hostname) > (MDNS_NAME_BUF_LEN - 1) || address_list == NULL) {
4904         return ESP_ERR_INVALID_ARG;
4905     }
4906     char * new_hostname = strndup(hostname, MDNS_NAME_BUF_LEN - 1);
4907     if (!new_hostname) {
4908         return ESP_ERR_NO_MEM;
4909     }
4910 
4911     mdns_action_t * action = (mdns_action_t *)malloc(sizeof(mdns_action_t));
4912     if (!action) {
4913         HOOK_MALLOC_FAILED;
4914         free(new_hostname);
4915         return ESP_ERR_NO_MEM;
4916     }
4917     action->type = ACTION_DELEGATE_HOSTNAME_ADD;
4918     action->data.delegate_hostname.hostname = new_hostname;
4919     action->data.delegate_hostname.address_list = copy_address_list(address_list);
4920     if (xQueueSend(_mdns_server->action_queue, &action, (portTickType)0) != pdPASS) {
4921         free(new_hostname);
4922         free(action);
4923         return ESP_ERR_NO_MEM;
4924     }
4925     return ESP_OK;
4926 }
4927 
mdns_delegate_hostname_remove(const char * hostname)4928 esp_err_t mdns_delegate_hostname_remove(const char * hostname)
4929 {
4930     if (!_mdns_server) {
4931         return ESP_ERR_INVALID_STATE;
4932     }
4933     if (_str_null_or_empty(hostname) || strlen(hostname) > (MDNS_NAME_BUF_LEN - 1)) {
4934         return ESP_ERR_INVALID_ARG;
4935     }
4936     char * new_hostname = strndup(hostname, MDNS_NAME_BUF_LEN - 1);
4937     if (!new_hostname) {
4938         return ESP_ERR_NO_MEM;
4939     }
4940 
4941     mdns_action_t * action = (mdns_action_t *)malloc(sizeof(mdns_action_t));
4942     if (!action) {
4943         HOOK_MALLOC_FAILED;
4944         free(new_hostname);
4945         return ESP_ERR_NO_MEM;
4946     }
4947     action->type = ACTION_DELEGATE_HOSTNAME_REMOVE;
4948     action->data.delegate_hostname.hostname = new_hostname;
4949     if (xQueueSend(_mdns_server->action_queue, &action, (portTickType)0) != pdPASS) {
4950         free(new_hostname);
4951         free(action);
4952         return ESP_ERR_NO_MEM;
4953     }
4954     return ESP_OK;
4955 }
4956 
mdns_hostname_exists(const char * hostname)4957 bool mdns_hostname_exists(const char * hostname)
4958 {
4959     return _hostname_is_ours(hostname);
4960 }
4961 
mdns_instance_name_set(const char * instance)4962 esp_err_t mdns_instance_name_set(const char * instance)
4963 {
4964     if (!_mdns_server) {
4965         return ESP_ERR_INVALID_STATE;
4966     }
4967     if (_str_null_or_empty(instance) || strlen(instance) > (MDNS_NAME_BUF_LEN - 1)) {
4968         return ESP_ERR_INVALID_ARG;
4969     }
4970     char * new_instance = strndup(instance, MDNS_NAME_BUF_LEN - 1);
4971     if (!new_instance) {
4972         return ESP_ERR_NO_MEM;
4973     }
4974 
4975     mdns_action_t * action = (mdns_action_t *)malloc(sizeof(mdns_action_t));
4976     if (!action) {
4977         HOOK_MALLOC_FAILED;
4978         free(new_instance);
4979         return ESP_ERR_NO_MEM;
4980     }
4981     action->type = ACTION_INSTANCE_SET;
4982     action->data.instance = new_instance;
4983     if (xQueueSend(_mdns_server->action_queue, &action, (portTickType)0) != pdPASS) {
4984         free(new_instance);
4985         free(action);
4986         return ESP_ERR_NO_MEM;
4987     }
4988     return ESP_OK;
4989 }
4990 
4991 /*
4992  * MDNS SERVICES
4993  * */
4994 
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)4995 esp_err_t mdns_service_add_for_host(const char * instance, const char * service, const char * proto, const char * hostname,
4996                                     uint16_t port, mdns_txt_item_t txt[], size_t num_items)
4997 {
4998     if (!_mdns_server || _str_null_or_empty(service) || _str_null_or_empty(proto) || !port) {
4999         return ESP_ERR_INVALID_ARG;
5000     }
5001 
5002     if (!_mdns_can_add_more_services()) {
5003         return ESP_ERR_NO_MEM;
5004     }
5005 
5006 #if CONFIG_MDNS_MULTIPLE_INSTANCE
5007     mdns_srv_item_t * item = _mdns_get_service_item_instance(instance, service, proto, hostname);
5008 #else
5009     mdns_srv_item_t * item = _mdns_get_service_item(service, proto, hostname);
5010 #endif // CONFIG_MDNS_MULTIPLE_INSTANCE
5011     if (item) {
5012         return ESP_ERR_INVALID_ARG;
5013     }
5014 
5015     mdns_service_t * s = _mdns_create_service(service, proto, hostname, port, instance, num_items, txt);
5016     if (!s) {
5017         return ESP_ERR_NO_MEM;
5018     }
5019 
5020     item = (mdns_srv_item_t *)malloc(sizeof(mdns_srv_item_t));
5021     if (!item) {
5022         HOOK_MALLOC_FAILED;
5023         _mdns_free_service(s);
5024         return ESP_ERR_NO_MEM;
5025     }
5026 
5027     item->service = s;
5028     item->next = NULL;
5029 
5030     mdns_action_t * action = (mdns_action_t *)malloc(sizeof(mdns_action_t));
5031     if (!action) {
5032         HOOK_MALLOC_FAILED;
5033         _mdns_free_service(s);
5034         free(item);
5035         return ESP_ERR_NO_MEM;
5036     }
5037     action->type = ACTION_SERVICE_ADD;
5038     action->data.srv_add.service = item;
5039     if (xQueueSend(_mdns_server->action_queue, &action, (portTickType)0) != pdPASS) {
5040         _mdns_free_service(s);
5041         free(item);
5042         free(action);
5043         return ESP_ERR_NO_MEM;
5044     }
5045 
5046     size_t start = xTaskGetTickCount();
5047     size_t timeout_ticks = pdMS_TO_TICKS(MDNS_SERVICE_ADD_TIMEOUT_MS);
5048     while (_mdns_get_service_item(service, proto, hostname) == NULL) {
5049         uint32_t expired = xTaskGetTickCount() - start;
5050         if (expired >= timeout_ticks) {
5051             return ESP_FAIL; // Timeout
5052         }
5053         vTaskDelay(MIN(10 / portTICK_RATE_MS, timeout_ticks - expired));
5054     }
5055 
5056     return ESP_OK;
5057 }
5058 
mdns_service_add(const char * instance,const char * service,const char * proto,uint16_t port,mdns_txt_item_t txt[],size_t num_items)5059 esp_err_t mdns_service_add(const char * instance, const char * service, const char * proto, uint16_t port,
5060                            mdns_txt_item_t txt[], size_t num_items)
5061 {
5062     if (!_mdns_server) {
5063         return ESP_ERR_INVALID_STATE;
5064     }
5065     return mdns_service_add_for_host(instance, service, proto, _mdns_server->hostname, port, txt, num_items);
5066 }
5067 
mdns_service_exists(const char * service_type,const char * proto,const char * hostname)5068 bool mdns_service_exists(const char * service_type, const char * proto, const char * hostname)
5069 {
5070     return _mdns_get_service_item(service_type, proto, hostname) != NULL;
5071 }
5072 
mdns_service_exists_with_instance(const char * instance,const char * service_type,const char * proto,const char * hostname)5073 bool mdns_service_exists_with_instance(const char *instance, const char *service_type, const char *proto,
5074                                        const char *hostname)
5075 {
5076     return _mdns_get_service_item_instance(instance, service_type, proto, hostname) != NULL;
5077 }
5078 
mdns_service_port_set_for_host(const char * service,const char * proto,const char * hostname,uint16_t port)5079 esp_err_t mdns_service_port_set_for_host(const char * service, const char * proto, const char * hostname, uint16_t port)
5080 {
5081     if (!_mdns_server || !_mdns_server->services || _str_null_or_empty(service) || _str_null_or_empty(proto) || !port) {
5082         return ESP_ERR_INVALID_ARG;
5083     }
5084     mdns_srv_item_t * s = _mdns_get_service_item(service, proto, hostname);
5085     if (!s) {
5086         return ESP_ERR_NOT_FOUND;
5087     }
5088 
5089     mdns_action_t * action = (mdns_action_t *)malloc(sizeof(mdns_action_t));
5090     if (!action) {
5091         HOOK_MALLOC_FAILED;
5092         return ESP_ERR_NO_MEM;
5093     }
5094     action->type = ACTION_SERVICE_PORT_SET;
5095     action->data.srv_port.service = s;
5096     action->data.srv_port.port = port;
5097     if (xQueueSend(_mdns_server->action_queue, &action, (portTickType)0) != pdPASS) {
5098         free(action);
5099         return ESP_ERR_NO_MEM;
5100     }
5101     return ESP_OK;
5102 }
5103 
mdns_service_port_set(const char * service,const char * proto,uint16_t port)5104 esp_err_t mdns_service_port_set(const char * service, const char * proto, uint16_t port)
5105 {
5106     if (!_mdns_server) {
5107         return ESP_ERR_INVALID_STATE;
5108     }
5109     return mdns_service_port_set_for_host(service, proto, _mdns_server->hostname, port);
5110 }
5111 
mdns_service_txt_set_for_host(const char * service,const char * proto,const char * hostname,mdns_txt_item_t txt[],uint8_t num_items)5112 esp_err_t mdns_service_txt_set_for_host(const char * service, const char * proto, const char * hostname,
5113                                         mdns_txt_item_t txt[], uint8_t num_items)
5114 {
5115     if (!_mdns_server || !_mdns_server->services || _str_null_or_empty(service) || _str_null_or_empty(proto) || (num_items && txt == NULL)) {
5116         return ESP_ERR_INVALID_ARG;
5117     }
5118     mdns_srv_item_t * s = _mdns_get_service_item(service, proto, hostname);
5119     if (!s) {
5120         return ESP_ERR_NOT_FOUND;
5121     }
5122 
5123     mdns_txt_linked_item_t * new_txt = NULL;
5124     if (num_items){
5125         new_txt = _mdns_allocate_txt(num_items, txt);
5126         if (!new_txt) {
5127             return ESP_ERR_NO_MEM;
5128         }
5129     }
5130 
5131     mdns_action_t * action = (mdns_action_t *)malloc(sizeof(mdns_action_t));
5132     if (!action) {
5133         HOOK_MALLOC_FAILED;
5134         _mdns_free_linked_txt(new_txt);
5135         return ESP_ERR_NO_MEM;
5136     }
5137     action->type = ACTION_SERVICE_TXT_REPLACE;
5138     action->data.srv_txt_replace.service = s;
5139     action->data.srv_txt_replace.txt = new_txt;
5140 
5141     if (xQueueSend(_mdns_server->action_queue, &action, (portTickType)0) != pdPASS) {
5142         _mdns_free_linked_txt(new_txt);
5143         free(action);
5144         return ESP_ERR_NO_MEM;
5145     }
5146     return ESP_OK;
5147 }
5148 
mdns_service_txt_set(const char * service,const char * proto,mdns_txt_item_t txt[],uint8_t num_items)5149 esp_err_t mdns_service_txt_set(const char * service, const char * proto, mdns_txt_item_t txt[], uint8_t num_items)
5150 {
5151     if (!_mdns_server) {
5152         return ESP_ERR_INVALID_STATE;
5153     }
5154     return mdns_service_txt_set_for_host(service, proto, _mdns_server->hostname, txt, num_items);
5155 }
5156 
mdns_service_txt_item_set_for_host_with_explicit_value_len(const char * service,const char * proto,const char * hostname,const char * key,const char * value,uint8_t value_len)5157 esp_err_t mdns_service_txt_item_set_for_host_with_explicit_value_len(const char *service, const char *proto,
5158                                                                      const char *hostname, const char *key,
5159                                                                      const char *value, uint8_t value_len)
5160 {
5161     if (!_mdns_server || !_mdns_server->services || _str_null_or_empty(service) || _str_null_or_empty(proto) ||
5162         _str_null_or_empty(key) || (!value && value_len)) {
5163         return ESP_ERR_INVALID_ARG;
5164     }
5165     mdns_srv_item_t *s = _mdns_get_service_item(service, proto, hostname);
5166     if (!s) {
5167         return ESP_ERR_NOT_FOUND;
5168     }
5169     mdns_action_t *action = (mdns_action_t *)malloc(sizeof(mdns_action_t));
5170     if (!action) {
5171         HOOK_MALLOC_FAILED;
5172         return ESP_ERR_NO_MEM;
5173     }
5174 
5175     action->type = ACTION_SERVICE_TXT_SET;
5176     action->data.srv_txt_set.service = s;
5177     action->data.srv_txt_set.key = strdup(key);
5178     if (!action->data.srv_txt_set.key) {
5179         free(action);
5180         return ESP_ERR_NO_MEM;
5181     }
5182     if (value_len > 0) {
5183         action->data.srv_txt_set.value = (char *)malloc(value_len);
5184         if (!action->data.srv_txt_set.value) {
5185             free(action->data.srv_txt_set.key);
5186             free(action);
5187             return ESP_ERR_NO_MEM;
5188         }
5189         memcpy(action->data.srv_txt_set.value, value, value_len);
5190         action->data.srv_txt_set.value_len = value_len;
5191     } else {
5192         action->data.srv_txt_set.value = NULL;
5193         action->data.srv_txt_set.value_len = 0;
5194     }
5195     if (xQueueSend(_mdns_server->action_queue, &action, (portTickType)0) != pdPASS) {
5196         free(action->data.srv_txt_set.key);
5197         free(action->data.srv_txt_set.value);
5198         free(action);
5199         return ESP_ERR_NO_MEM;
5200     }
5201     return ESP_OK;
5202 }
5203 
mdns_service_txt_item_set_for_host(const char * service,const char * proto,const char * hostname,const char * key,const char * value)5204 esp_err_t mdns_service_txt_item_set_for_host(const char *service, const char *proto, const char *hostname,
5205                                              const char *key, const char *value)
5206 {
5207     return mdns_service_txt_item_set_for_host_with_explicit_value_len(service, proto, hostname, key, value,
5208                                                                       strlen(value));
5209 }
5210 
5211 
mdns_service_txt_item_set(const char * service,const char * proto,const char * key,const char * value)5212 esp_err_t mdns_service_txt_item_set(const char *service, const char *proto, const char *key, const char *value)
5213 {
5214     if (!_mdns_server) {
5215         return ESP_ERR_INVALID_STATE;
5216     }
5217     return mdns_service_txt_item_set_for_host_with_explicit_value_len(service, proto, _mdns_server->hostname, key,
5218                                                                       value, strlen(value));
5219 }
5220 
mdns_service_txt_item_set_with_explicit_value_len(const char * service,const char * proto,const char * key,const char * value,uint8_t value_len)5221 esp_err_t mdns_service_txt_item_set_with_explicit_value_len(const char *service, const char *proto, const char *key,
5222                                                             const char *value, uint8_t value_len)
5223 {
5224     if (!_mdns_server) {
5225         return ESP_ERR_INVALID_STATE;
5226     }
5227     return mdns_service_txt_item_set_for_host_with_explicit_value_len(service, proto, _mdns_server->hostname, key,
5228                                                                       value, value_len);
5229 }
5230 
mdns_service_txt_item_remove_for_host(const char * service,const char * proto,const char * hostname,const char * key)5231 esp_err_t mdns_service_txt_item_remove_for_host(const char * service, const char * proto, const char * hostname,
5232                                                 const char * key)
5233 {
5234     if (!_mdns_server || !_mdns_server->services || _str_null_or_empty(service) || _str_null_or_empty(proto) || _str_null_or_empty(key)) {
5235         return ESP_ERR_INVALID_ARG;
5236     }
5237     mdns_srv_item_t * s = _mdns_get_service_item(service, proto, hostname);
5238     if (!s) {
5239         return ESP_ERR_NOT_FOUND;
5240     }
5241     mdns_action_t * action = (mdns_action_t *)malloc(sizeof(mdns_action_t));
5242     if (!action) {
5243         HOOK_MALLOC_FAILED;
5244         return ESP_ERR_NO_MEM;
5245     }
5246 
5247     action->type = ACTION_SERVICE_TXT_DEL;
5248     action->data.srv_txt_del.service = s;
5249     action->data.srv_txt_del.key = strdup(key);
5250     if (!action->data.srv_txt_del.key) {
5251         free(action);
5252         return ESP_ERR_NO_MEM;
5253     }
5254     if (xQueueSend(_mdns_server->action_queue, &action, (portTickType)0) != pdPASS) {
5255         free(action->data.srv_txt_del.key);
5256         free(action);
5257         return ESP_ERR_NO_MEM;
5258     }
5259     return ESP_OK;
5260 }
5261 
mdns_service_txt_item_remove(const char * service,const char * proto,const char * key)5262 esp_err_t mdns_service_txt_item_remove(const char * service, const char * proto, const char * key)
5263 {
5264     if (!_mdns_server) {
5265         return ESP_ERR_INVALID_STATE;
5266     }
5267     return mdns_service_txt_item_remove_for_host(service, proto, _mdns_server->hostname, key);
5268 }
5269 
mdns_service_instance_name_set_for_host(const char * service,const char * proto,const char * hostname,const char * instance)5270 esp_err_t mdns_service_instance_name_set_for_host(const char * service, const char * proto, const char * hostname,
5271                                                   const char * instance)
5272 {
5273     if (!_mdns_server || !_mdns_server->services || _str_null_or_empty(service) || _str_null_or_empty(proto)) {
5274         return ESP_ERR_INVALID_ARG;
5275     }
5276     if (_str_null_or_empty(instance) || strlen(instance) > (MDNS_NAME_BUF_LEN - 1)) {
5277         return ESP_ERR_INVALID_ARG;
5278     }
5279     mdns_srv_item_t * s = _mdns_get_service_item(service, proto, hostname);
5280     if (!s) {
5281         return ESP_ERR_NOT_FOUND;
5282     }
5283     char * new_instance = strndup(instance, MDNS_NAME_BUF_LEN - 1);
5284     if (!new_instance) {
5285         return ESP_ERR_NO_MEM;
5286     }
5287 
5288     mdns_action_t * action = (mdns_action_t *)malloc(sizeof(mdns_action_t));
5289     if (!action) {
5290         HOOK_MALLOC_FAILED;
5291         free(new_instance);
5292         return ESP_ERR_NO_MEM;
5293     }
5294     action->type = ACTION_SERVICE_INSTANCE_SET;
5295     action->data.srv_instance.service = s;
5296     action->data.srv_instance.instance = new_instance;
5297     if (xQueueSend(_mdns_server->action_queue, &action, (portTickType)0) != pdPASS) {
5298         free(new_instance);
5299         free(action);
5300         return ESP_ERR_NO_MEM;
5301     }
5302     return ESP_OK;
5303 }
5304 
mdns_service_instance_name_set(const char * service,const char * proto,const char * instance)5305 esp_err_t mdns_service_instance_name_set(const char * service, const char * proto, const char * instance)
5306 {
5307     if (!_mdns_server) {
5308         return ESP_ERR_INVALID_STATE;
5309     }
5310     return mdns_service_instance_name_set_for_host(service, proto, _mdns_server->hostname, instance);
5311 }
5312 
mdns_service_remove_for_host(const char * service,const char * proto,const char * hostname)5313 esp_err_t mdns_service_remove_for_host(const char * service, const char * proto, const char * hostname)
5314 {
5315     if (!_mdns_server || !_mdns_server->services || _str_null_or_empty(service) || _str_null_or_empty(proto)) {
5316         return ESP_ERR_INVALID_ARG;
5317     }
5318     mdns_srv_item_t * s = _mdns_get_service_item(service, proto, hostname);
5319     if (!s) {
5320         return ESP_ERR_NOT_FOUND;
5321     }
5322 
5323     mdns_action_t * action = (mdns_action_t *)malloc(sizeof(mdns_action_t));
5324     if (!action) {
5325         HOOK_MALLOC_FAILED;
5326         return ESP_ERR_NO_MEM;
5327     }
5328     action->type = ACTION_SERVICE_DEL;
5329     action->data.srv_del.service = s;
5330     if (xQueueSend(_mdns_server->action_queue, &action, (portTickType)0) != pdPASS) {
5331         free(action);
5332         return ESP_ERR_NO_MEM;
5333     }
5334     return ESP_OK;
5335 }
5336 
mdns_service_remove(const char * service_type,const char * proto)5337 esp_err_t mdns_service_remove(const char * service_type, const char * proto)
5338 {
5339     if (!_mdns_server) {
5340         return ESP_ERR_INVALID_STATE;
5341     }
5342     return mdns_service_remove_for_host(service_type, proto, _mdns_server->hostname);
5343 }
5344 
mdns_service_remove_all(void)5345 esp_err_t mdns_service_remove_all(void)
5346 {
5347     if (!_mdns_server) {
5348         return ESP_ERR_INVALID_ARG;
5349     }
5350     if (!_mdns_server->services) {
5351         return ESP_OK;
5352     }
5353 
5354     mdns_action_t * action = (mdns_action_t *)malloc(sizeof(mdns_action_t));
5355     if (!action) {
5356         HOOK_MALLOC_FAILED;
5357         return ESP_ERR_NO_MEM;
5358     }
5359     action->type = ACTION_SERVICES_CLEAR;
5360     if (xQueueSend(_mdns_server->action_queue, &action, (portTickType)0) != pdPASS) {
5361         free(action);
5362         return ESP_ERR_NO_MEM;
5363     }
5364     return ESP_OK;
5365 }
5366 
5367 /*
5368  * MDNS QUERY
5369  * */
5370 
mdns_query_results_free(mdns_result_t * results)5371 void mdns_query_results_free(mdns_result_t * results)
5372 {
5373     mdns_result_t * r;
5374     mdns_ip_addr_t * a;
5375 
5376     while (results) {
5377         r = results;
5378 
5379         free((char *)(r->hostname));
5380         free((char *)(r->instance_name));
5381         free((char *)(r->service_type));
5382         free((char *)(r->proto));
5383 
5384         for (size_t i=0; i<r->txt_count; i++) {
5385             free((char *)(r->txt[i].key));
5386             free((char *)(r->txt[i].value));
5387         }
5388         free(r->txt);
5389         free(r->txt_value_len);
5390 
5391         while (r->addr) {
5392             a = r->addr;
5393             r->addr = r->addr->next;
5394             free(a);
5395         }
5396 
5397         results = results->next;
5398         free(r);
5399     }
5400 }
5401 
mdns_query_async_delete(mdns_search_once_t * search)5402 esp_err_t mdns_query_async_delete(mdns_search_once_t* search)
5403 {
5404     if (!search) {
5405         return ESP_ERR_INVALID_ARG;
5406     }
5407     if (search->state != SEARCH_OFF) {
5408         return ESP_ERR_INVALID_STATE;
5409     }
5410 
5411     MDNS_SERVICE_LOCK();
5412     _mdns_search_free(search);
5413     MDNS_SERVICE_UNLOCK();
5414 
5415     return ESP_OK;
5416 }
5417 
mdns_query_async_get_results(mdns_search_once_t * search,uint32_t timeout,mdns_result_t ** results)5418 bool mdns_query_async_get_results(mdns_search_once_t* search, uint32_t timeout, mdns_result_t ** results)
5419 {
5420     if (xSemaphoreTake(search->done_semaphore, pdMS_TO_TICKS(timeout)) == pdTRUE) {
5421         *results = search->result;
5422         return true;
5423     }
5424     return false;
5425 }
5426 
mdns_query_async_new(const char * name,const char * service,const char * proto,uint16_t type,uint32_t timeout,size_t max_results,mdns_query_notify_t notifier)5427 mdns_search_once_t *mdns_query_async_new(const char *name, const char *service, const char *proto, uint16_t type,
5428                                          uint32_t timeout, size_t max_results, mdns_query_notify_t notifier)
5429 {
5430     mdns_search_once_t *search = NULL;
5431 
5432     if (!_mdns_server || !timeout || _str_null_or_empty(service) != _str_null_or_empty(proto)) {
5433         return NULL;
5434     }
5435 
5436     search = _mdns_search_init(name, service, proto, type, timeout, max_results, notifier);
5437     if (!search) {
5438         return NULL;
5439     }
5440 
5441     if (_mdns_send_search_action(ACTION_SEARCH_ADD, search)) {
5442         _mdns_search_free(search);
5443         return NULL;
5444     }
5445 
5446     return search;
5447 }
5448 
mdns_query(const char * name,const char * service,const char * proto,uint16_t type,uint32_t timeout,size_t max_results,mdns_result_t ** results)5449 esp_err_t mdns_query(const char * name, const char * service, const char * proto, uint16_t type, uint32_t timeout, size_t max_results, mdns_result_t ** results)
5450 {
5451     mdns_search_once_t * search = NULL;
5452 
5453     *results = NULL;
5454 
5455     if (!_mdns_server) {
5456         return ESP_ERR_INVALID_STATE;
5457     }
5458 
5459     if (!timeout || _str_null_or_empty(service) != _str_null_or_empty(proto)) {
5460         return ESP_ERR_INVALID_ARG;
5461     }
5462 
5463     search = _mdns_search_init(name, service, proto, type, timeout, max_results, NULL);
5464     if (!search) {
5465         return ESP_ERR_NO_MEM;
5466     }
5467 
5468     if (_mdns_send_search_action(ACTION_SEARCH_ADD, search)) {
5469         _mdns_search_free(search);
5470         return ESP_ERR_NO_MEM;
5471     }
5472     xSemaphoreTake(search->done_semaphore, portMAX_DELAY);
5473 
5474     *results = search->result;
5475     _mdns_search_free(search);
5476 
5477     return ESP_OK;
5478 }
5479 
mdns_query_ptr(const char * service,const char * proto,uint32_t timeout,size_t max_results,mdns_result_t ** results)5480 esp_err_t mdns_query_ptr(const char * service, const char * proto, uint32_t timeout, size_t max_results, mdns_result_t ** results)
5481 {
5482     if (_str_null_or_empty(service) || _str_null_or_empty(proto)) {
5483         return ESP_ERR_INVALID_ARG;
5484     }
5485 
5486     return mdns_query(NULL, service, proto, MDNS_TYPE_PTR, timeout, max_results, results);
5487 }
5488 
mdns_query_srv(const char * instance,const char * service,const char * proto,uint32_t timeout,mdns_result_t ** result)5489 esp_err_t mdns_query_srv(const char * instance, const char * service, const char * proto, uint32_t timeout, mdns_result_t ** result)
5490 {
5491     if (_str_null_or_empty(instance) || _str_null_or_empty(service) || _str_null_or_empty(proto)) {
5492         return ESP_ERR_INVALID_ARG;
5493     }
5494 
5495     return mdns_query(instance, service, proto, MDNS_TYPE_SRV, timeout, 1, result);
5496 }
5497 
mdns_query_txt(const char * instance,const char * service,const char * proto,uint32_t timeout,mdns_result_t ** result)5498 esp_err_t mdns_query_txt(const char * instance, const char * service, const char * proto, uint32_t timeout, mdns_result_t ** result)
5499 {
5500     if (_str_null_or_empty(instance) || _str_null_or_empty(service) || _str_null_or_empty(proto)) {
5501         return ESP_ERR_INVALID_ARG;
5502     }
5503 
5504     return mdns_query(instance, service, proto, MDNS_TYPE_TXT, timeout, 1, result);
5505 }
5506 
mdns_query_a(const char * name,uint32_t timeout,esp_ip4_addr_t * addr)5507 esp_err_t mdns_query_a(const char * name, uint32_t timeout, esp_ip4_addr_t * addr)
5508 {
5509     mdns_result_t * result = NULL;
5510     esp_err_t err;
5511 
5512     if (_str_null_or_empty(name)) {
5513         return ESP_ERR_INVALID_ARG;
5514     }
5515 
5516     if (strstr(name, ".local")) {
5517         ESP_LOGW(TAG, "Please note that hostname must not contain domain name, as mDNS uses '.local' domain");
5518     }
5519 
5520     err = mdns_query(name, NULL, NULL, MDNS_TYPE_A, timeout, 1, &result);
5521 
5522     if (err) {
5523         return err;
5524     }
5525 
5526     if (!result) {
5527         return ESP_ERR_NOT_FOUND;
5528     }
5529 
5530     mdns_ip_addr_t * a = result->addr;
5531     while (a) {
5532         if (a->addr.type == ESP_IPADDR_TYPE_V4) {
5533             addr->addr = a->addr.u_addr.ip4.addr;
5534             mdns_query_results_free(result);
5535             return ESP_OK;
5536         }
5537         a = a->next;
5538     }
5539 
5540     mdns_query_results_free(result);
5541     return ESP_ERR_NOT_FOUND;
5542 }
5543 
5544 #if CONFIG_LWIP_IPV6
mdns_query_aaaa(const char * name,uint32_t timeout,esp_ip6_addr_t * addr)5545 esp_err_t mdns_query_aaaa(const char * name, uint32_t timeout, esp_ip6_addr_t * addr)
5546 {
5547     mdns_result_t * result = NULL;
5548     esp_err_t err;
5549 
5550     if (_str_null_or_empty(name)) {
5551         return ESP_ERR_INVALID_ARG;
5552     }
5553 
5554     if (strstr(name, ".local")) {
5555         ESP_LOGW(TAG, "Please note that hostname must not contain domain name, as mDNS uses '.local' domain");
5556     }
5557 
5558     err = mdns_query(name, NULL, NULL, MDNS_TYPE_AAAA, timeout, 1, &result);
5559 
5560     if (err) {
5561         return err;
5562     }
5563 
5564     if (!result) {
5565         return ESP_ERR_NOT_FOUND;
5566     }
5567 
5568     mdns_ip_addr_t * a = result->addr;
5569     while (a) {
5570         if (a->addr.type == ESP_IPADDR_TYPE_V6) {
5571             memcpy(addr->addr, a->addr.u_addr.ip6.addr, 16);
5572             mdns_query_results_free(result);
5573             return ESP_OK;
5574         }
5575         a = a->next;
5576     }
5577 
5578     mdns_query_results_free(result);
5579     return ESP_ERR_NOT_FOUND;
5580 }
5581 #endif
5582 
5583 #ifdef MDNS_ENABLE_DEBUG
5584 
mdns_debug_packet(const uint8_t * data,size_t len)5585 void mdns_debug_packet(const uint8_t * data, size_t len)
5586 {
5587     static mdns_name_t n;
5588     mdns_header_t header;
5589     const uint8_t * content = data + MDNS_HEAD_LEN;
5590     uint32_t t = xTaskGetTickCount() * portTICK_PERIOD_MS;
5591     mdns_name_t * name = &n;
5592     memset(name, 0, sizeof(mdns_name_t));
5593 
5594     _mdns_dbg_printf("Packet[%u]: ", t);
5595 
5596     header.id = _mdns_read_u16(data, MDNS_HEAD_ID_OFFSET);
5597     header.flags.value = _mdns_read_u16(data, MDNS_HEAD_FLAGS_OFFSET);
5598     header.questions = _mdns_read_u16(data, MDNS_HEAD_QUESTIONS_OFFSET);
5599     header.answers = _mdns_read_u16(data, MDNS_HEAD_ANSWERS_OFFSET);
5600     header.servers = _mdns_read_u16(data, MDNS_HEAD_SERVERS_OFFSET);
5601     header.additional = _mdns_read_u16(data, MDNS_HEAD_ADDITIONAL_OFFSET);
5602 
5603     _mdns_dbg_printf("%s",
5604         (header.flags.value == MDNS_FLAGS_AUTHORITATIVE)?"AUTHORITATIVE\n":
5605         (header.flags.value == MDNS_FLAGS_DISTRIBUTED)?"DISTRIBUTED\n":
5606         (header.flags.value == 0)?"\n":" "
5607     );
5608     if (header.flags.value && header.flags.value != MDNS_FLAGS_AUTHORITATIVE) {
5609         _mdns_dbg_printf("0x%04X\n", header.flags.value);
5610     }
5611 
5612     if (header.questions) {
5613         uint8_t qs = header.questions;
5614 
5615         while (qs--) {
5616             content = _mdns_parse_fqdn(data, content, name);
5617             if (!content) {
5618                 header.answers = 0;
5619                 header.additional = 0;
5620                 header.servers = 0;
5621                 _mdns_dbg_printf("ERROR: parse header questions\n");
5622                 break;
5623             }
5624 
5625             uint16_t type = _mdns_read_u16(content, MDNS_TYPE_OFFSET);
5626             uint16_t mdns_class = _mdns_read_u16(content, MDNS_CLASS_OFFSET);
5627             bool unicast = !!(mdns_class & 0x8000);
5628             mdns_class &= 0x7FFF;
5629             content = content + 4;
5630 
5631             _mdns_dbg_printf("    Q: ");
5632             if (unicast) {
5633                 _mdns_dbg_printf("*U* ");
5634             }
5635             if (type == MDNS_TYPE_PTR) {
5636                 _mdns_dbg_printf("%s.%s%s.%s.%s. PTR ", name->host, name->sub?"_sub.":"", name->service, name->proto, name->domain);
5637             } else if (type == MDNS_TYPE_SRV) {
5638                 _mdns_dbg_printf("%s.%s%s.%s.%s. SRV ", name->host, name->sub?"_sub.":"", name->service, name->proto, name->domain);
5639             } else if (type == MDNS_TYPE_TXT) {
5640                 _mdns_dbg_printf("%s.%s%s.%s.%s. TXT ", name->host, name->sub?"_sub.":"", name->service, name->proto, name->domain);
5641             } else if (type == MDNS_TYPE_A) {
5642                 _mdns_dbg_printf("%s.%s. A ", name->host, name->domain);
5643             } else if (type == MDNS_TYPE_AAAA) {
5644                 _mdns_dbg_printf("%s.%s. AAAA ", name->host, name->domain);
5645             } else if (type == MDNS_TYPE_NSEC) {
5646                 _mdns_dbg_printf("%s.%s%s.%s.%s. NSEC ", name->host, name->sub?"_sub.":"", name->service, name->proto, name->domain);
5647             } else if (type == MDNS_TYPE_ANY) {
5648                 _mdns_dbg_printf("%s.%s%s.%s.%s. ANY ", name->host, name->sub?"_sub.":"", name->service, name->proto, name->domain);
5649             } else {
5650                 _mdns_dbg_printf("%s.%s%s.%s.%s. %04X ", name->host, name->sub?"_sub.":"", name->service, name->proto, name->domain, type);
5651             }
5652 
5653             if (mdns_class == 0x0001) {
5654                 _mdns_dbg_printf("IN");
5655             } else {
5656                 _mdns_dbg_printf("%04X", mdns_class);
5657             }
5658             _mdns_dbg_printf("\n");
5659         }
5660     }
5661 
5662     if (header.answers || header.servers || header.additional) {
5663         uint16_t recordIndex = 0;
5664 
5665         while (content < (data + len)) {
5666 
5667             content = _mdns_parse_fqdn(data, content, name);
5668             if (!content) {
5669                 _mdns_dbg_printf("ERROR: parse mdns records\n");
5670                 break;
5671             }
5672 
5673             uint16_t type = _mdns_read_u16(content, MDNS_TYPE_OFFSET);
5674             uint16_t mdns_class = _mdns_read_u16(content, MDNS_CLASS_OFFSET);
5675             uint32_t ttl = _mdns_read_u32(content, MDNS_TTL_OFFSET);
5676             uint16_t data_len = _mdns_read_u16(content, MDNS_LEN_OFFSET);
5677             const uint8_t * data_ptr = content + MDNS_DATA_OFFSET;
5678             bool flush = !!(mdns_class & 0x8000);
5679             mdns_class &= 0x7FFF;
5680 
5681             content = data_ptr + data_len;
5682             if (content > (data + len)) {
5683                 _mdns_dbg_printf("ERROR: content length overflow\n");
5684                 break;
5685             }
5686 
5687             mdns_parsed_record_type_t record_type = MDNS_ANSWER;
5688 
5689             if (recordIndex >= (header.answers + header.servers)) {
5690                 record_type = MDNS_EXTRA;
5691             } else if (recordIndex >= (header.answers)) {
5692                 record_type = MDNS_NS;
5693             }
5694             recordIndex++;
5695 
5696             if (record_type == MDNS_EXTRA) {
5697                 _mdns_dbg_printf("    X");
5698             } else if (record_type == MDNS_NS) {
5699                 _mdns_dbg_printf("    S");
5700             } else {
5701                 _mdns_dbg_printf("    A");
5702             }
5703 
5704             if (type == MDNS_TYPE_PTR) {
5705                 _mdns_dbg_printf(": %s%s%s.%s.%s. PTR ", name->host, name->host[0]?".":"", name->service, name->proto, name->domain);
5706             } else if (type == MDNS_TYPE_SRV) {
5707                 _mdns_dbg_printf(": %s.%s.%s.%s. SRV ", name->host, name->service, name->proto, name->domain);
5708             } else if (type == MDNS_TYPE_TXT) {
5709                 _mdns_dbg_printf(": %s.%s.%s.%s. TXT ", name->host, name->service, name->proto, name->domain);
5710             } else if (type == MDNS_TYPE_A) {
5711                 _mdns_dbg_printf(": %s.%s. A ", name->host, name->domain);
5712             } else if (type == MDNS_TYPE_AAAA) {
5713                 _mdns_dbg_printf(": %s.%s. AAAA ", name->host, name->domain);
5714             } else if (type == MDNS_TYPE_NSEC) {
5715                 _mdns_dbg_printf(": %s.%s.%s.%s. NSEC ", name->host, name->service, name->proto, name->domain);
5716             } else if (type == MDNS_TYPE_ANY) {
5717                 _mdns_dbg_printf(": %s.%s.%s.%s. ANY ", name->host, name->service, name->proto, name->domain);
5718             } else if (type == MDNS_TYPE_OPT) {
5719                 _mdns_dbg_printf(": . OPT ");
5720             } else {
5721                 _mdns_dbg_printf(": %s.%s.%s.%s. %04X ", name->host, name->service, name->proto, name->domain, type);
5722             }
5723 
5724             if (mdns_class == 0x0001) {
5725                 _mdns_dbg_printf("IN ");
5726             } else {
5727                 _mdns_dbg_printf("%04X ", mdns_class);
5728             }
5729             if (flush) {
5730                 _mdns_dbg_printf("FLUSH ");
5731             }
5732             _mdns_dbg_printf("%u ", ttl);
5733             _mdns_dbg_printf("[%u] ", data_len);
5734             if (type == MDNS_TYPE_PTR) {
5735                 if (!_mdns_parse_fqdn(data, data_ptr, name)) {
5736                     _mdns_dbg_printf("ERROR: parse PTR\n");
5737                     continue;
5738                 }
5739                 _mdns_dbg_printf("%s.%s.%s.%s.\n", name->host, name->service, name->proto, name->domain);
5740             } else if (type == MDNS_TYPE_SRV) {
5741                 if (!_mdns_parse_fqdn(data, data_ptr + MDNS_SRV_FQDN_OFFSET, name)) {
5742                     _mdns_dbg_printf("ERROR: parse SRV\n");
5743                     continue;
5744                 }
5745                 uint16_t priority = _mdns_read_u16(data_ptr, MDNS_SRV_PRIORITY_OFFSET);
5746                 uint16_t weight = _mdns_read_u16(data_ptr, MDNS_SRV_WEIGHT_OFFSET);
5747                 uint16_t port = _mdns_read_u16(data_ptr, MDNS_SRV_PORT_OFFSET);
5748                 _mdns_dbg_printf("%u %u %u %s.%s.\n", priority, weight, port, name->host, name->domain);
5749             } else if (type == MDNS_TYPE_TXT) {
5750                 uint16_t i=0, y;
5751                 while (i < data_len) {
5752                     uint8_t partLen = data_ptr[i++];
5753                     if ((i+partLen) > data_len) {
5754                         _mdns_dbg_printf("ERROR: parse TXT\n");
5755                         break;
5756                     }
5757                     char txt[partLen+1];
5758                     for (y=0; y<partLen; y++) {
5759                         char d = data_ptr[i++];
5760                         txt[y] = d;
5761                     }
5762                     txt[partLen] = 0;
5763                     _mdns_dbg_printf("%s", txt);
5764                     if (i<data_len) {
5765                         _mdns_dbg_printf("; ");
5766                     }
5767                 }
5768                 _mdns_dbg_printf("\n");
5769             } else if (type == MDNS_TYPE_AAAA) {
5770                 esp_ip6_addr_t ip6;
5771                 memcpy(&ip6, data_ptr, sizeof(esp_ip6_addr_t));
5772                 _mdns_dbg_printf(IPV6STR "\n", IPV62STR(ip6));
5773             } else if (type == MDNS_TYPE_A) {
5774                 esp_ip4_addr_t ip;
5775                 memcpy(&ip, data_ptr, sizeof(esp_ip4_addr_t));
5776                 _mdns_dbg_printf(IPSTR "\n", IP2STR(&ip));
5777             } else if (type == MDNS_TYPE_NSEC) {
5778                 const uint8_t * old_ptr = data_ptr;
5779                 const uint8_t * new_ptr = _mdns_parse_fqdn(data, data_ptr, name);
5780                 if (new_ptr) {
5781                     _mdns_dbg_printf("%s.%s.%s.%s. ", name->host, name->service, name->proto, name->domain);
5782                     size_t diff = new_ptr - old_ptr;
5783                     data_len -= diff;
5784                     data_ptr = new_ptr;
5785                 }
5786                 size_t i;
5787                 for (i=0; i<data_len; i++) {
5788                     _mdns_dbg_printf(" %02x", data_ptr[i]);
5789                 }
5790                 _mdns_dbg_printf("\n");
5791             } else if (type == MDNS_TYPE_OPT) {
5792                 uint16_t opCode = _mdns_read_u16(data_ptr, 0);
5793                 uint16_t opLen = _mdns_read_u16(data_ptr, 2);
5794                 _mdns_dbg_printf(" Code: %04x Data[%u]:", opCode, opLen);
5795                 size_t i;
5796                 for (i=4; i<data_len; i++) {
5797                     _mdns_dbg_printf(" %02x", data_ptr[i]);
5798                 }
5799                 _mdns_dbg_printf("\n");
5800             } else {
5801                 size_t i;
5802                 for (i=0; i<data_len; i++) {
5803                     _mdns_dbg_printf(" %02x", data_ptr[i]);
5804                 }
5805                 _mdns_dbg_printf("\n");
5806             }
5807         }
5808     }
5809 }
5810 #endif
5811