mdns_responder.c
Go to the documentation of this file.
1 /**
2  * @file mdns_responder.c
3  * @brief mDNS responder (Multicast DNS)
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2010-2024 Oryx Embedded SARL. All rights reserved.
10  *
11  * This file is part of CycloneTCP Open.
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26  *
27  * @author Oryx Embedded SARL (www.oryx-embedded.com)
28  * @version 2.4.4
29  **/
30 
31 //Switch to the appropriate trace level
32 #define TRACE_LEVEL MDNS_TRACE_LEVEL
33 
34 //Dependencies
35 #include "core/net.h"
36 #include "dns/dns_debug.h"
37 #include "mdns/mdns_responder.h"
40 #include "debug.h"
41 
42 //Check TCP/IP stack configuration
43 #if (MDNS_RESPONDER_SUPPORT == ENABLED)
44 
45 //Tick counter to handle periodic operations
47 
48 
49 /**
50  * @brief Initialize settings with default values
51  * @param[out] settings Structure that contains mDNS responder settings
52  **/
53 
55 {
56  //Use default interface
57  settings->interface = netGetDefaultInterface();
58 
59  //Number of announcement packets
61  //TTL resource record
62  settings->ttl = MDNS_DEFAULT_RR_TTL;
63  //FSM state change event
64  settings->stateChangeEvent = NULL;
65 }
66 
67 
68 /**
69  * @brief mDNS responder initialization
70  * @param[in] context Pointer to the mDNS responder context
71  * @param[in] settings mDNS responder specific settings
72  * @return Error code
73  **/
74 
76  const MdnsResponderSettings *settings)
77 {
78  NetInterface *interface;
79 
80  //Debug message
81  TRACE_INFO("Initializing mDNS responder...\r\n");
82 
83  //Ensure the parameters are valid
84  if(context == NULL || settings == NULL)
86 
87  //Invalid network interface?
88  if(settings->interface == NULL)
90 
91  //Point to the underlying network interface
92  interface = settings->interface;
93 
94  //Clear the mDNS responder context
95  osMemset(context, 0, sizeof(MdnsResponderContext));
96  //Save user settings
97  context->settings = *settings;
98 
99  //mDNS responder is currently suspended
100  context->running = FALSE;
101  //Initialize state machine
102  context->state = MDNS_STATE_INIT;
103 
104  //Attach the mDNS responder context to the network interface
105  interface->mdnsResponderContext = context;
106 
107  //Successful initialization
108  return NO_ERROR;
109 }
110 
111 
112 /**
113  * @brief Start mDNS responder
114  * @param[in] context Pointer to the mDNS responder context
115  * @return Error code
116  **/
117 
119 {
120  //Make sure the mDNS responder context is valid
121  if(context == NULL)
123 
124  //Debug message
125  TRACE_INFO("Starting mDNS responder...\r\n");
126 
127  //Get exclusive access
129 
130  //Start mDNS responder
131  context->running = TRUE;
132  //Initialize state machine
133  context->state = MDNS_STATE_INIT;
134 
135  //Release exclusive access
137 
138  //Successful processing
139  return NO_ERROR;
140 }
141 
142 
143 /**
144  * @brief Stop mDNS responder
145  * @param[in] context Pointer to the mDNS responder context
146  * @return Error code
147  **/
148 
150 {
151  //Make sure the mDNS responder context is valid
152  if(context == NULL)
154 
155  //Debug message
156  TRACE_INFO("Stopping mDNS responder...\r\n");
157 
158  //Get exclusive access
160 
161  //Suspend mDNS responder
162  context->running = FALSE;
163  //Reinitialize state machine
164  context->state = MDNS_STATE_INIT;
165 
166  //Release exclusive access
168 
169  //Successful processing
170  return NO_ERROR;
171 }
172 
173 
174 /**
175  * @brief Retrieve current state
176  * @param[in] context Pointer to the mDNS responder context
177  * @return Current mDNS responder state
178  **/
179 
181 {
182  MdnsState state;
183 
184  //Get exclusive access
186  //Get current state
187  state = context->state;
188  //Release exclusive access
190 
191  //Return current state
192  return state;
193 }
194 
195 
196 /**
197  * @brief Set host name
198  * @param[in] context Pointer to the mDNS responder context
199  * @param[in] hostname NULL-terminated string that contains the host name
200  * @return Error code
201  **/
202 
204  const char_t *hostname)
205 {
206  NetInterface *interface;
207 
208  //Check parameters
209  if(context == NULL || hostname == NULL)
211 
212  //Make sure the length of the host name is acceptable
214  return ERROR_INVALID_LENGTH;
215 
216  //Get exclusive access
218 
219  //Point to the underlying network interface
220  interface = context->settings.interface;
221 
222  //Check whether a host name is already assigned
223  if(context->hostname[0] != '\0')
224  {
225  //Check whether the link is up
226  if(interface->linkState)
227  {
228  //Send a goodbye packet
229  mdnsResponderSendGoodbye(context);
230  }
231  }
232 
233  //Set host name
234  osStrcpy(context->hostname, hostname);
235 
236  //Restart probing process (host name)
237  mdnsResponderStartProbing(interface->mdnsResponderContext);
238 
239  //Release exclusive access
241 
242  //Successful processing
243  return NO_ERROR;
244 }
245 
246 
247 /**
248  * @brief Restart probing process
249  * @param[in] context Pointer to the mDNS responder context
250  * @return Error code
251  **/
252 
254 {
255  uint_t i;
256  NetInterface *interface;
257 
258  //Check whether the mDNS responder has been properly instantiated
259  if(context == NULL)
261 
262  //Point to the underlying network interface
263  interface = context->settings.interface;
264 
265  //Reset variables
266  context->ipv4AddrCount = 0;
267  context->ipv6AddrCount = 0;
268 
269 #if (IPV4_SUPPORT == ENABLED)
270  //Loop through the list of IPv4 addresses assigned to the interface
271  for(i = 0; i < IPV4_ADDR_LIST_SIZE; i++)
272  {
273  //Valid IPv4 address?
274  if(interface->ipv4Context.addrList[i].state == IPV4_ADDR_STATE_VALID)
275  {
276  MdnsIpv4AddrEntry *entry;
277 
278  //Point to the current entry
279  entry = &context->ipv4AddrList[i];
280 
281  //Format A resource record
282  entry->record.rtype = HTONS(DNS_RR_TYPE_A);
283  entry->record.rclass = HTONS(DNS_RR_CLASS_IN);
284  entry->record.ttl = htonl(MDNS_DEFAULT_RR_TTL);
285  entry->record.rdlength = HTONS(sizeof(Ipv4Addr));
286 
287  //Copy IPv4 address
288  ipv4CopyAddr(entry->record.rdata, &interface->ipv4Context.addrList[i].addr);
289 
290  //Generate domain name for reverse DNS lookup
291  dnsGenerateIpv4ReverseName(interface->ipv4Context.addrList[i].addr,
292  entry->reverseName);
293 
294  //The entry is valid
295  context->ipv4AddrList[i].valid = TRUE;
296 
297  //Increment the number of valid IPv4 addresses
298  context->ipv4AddrCount++;
299  }
300  else
301  {
302  //Invalidate the entry
303  context->ipv4AddrList[i].valid = FALSE;
304  }
305  }
306 #endif
307 
308 #if (IPV6_SUPPORT == ENABLED)
309  //Loop through the list of IPv6 addresses assigned to the interface
310  for(i = 0; i < IPV6_ADDR_LIST_SIZE; i++)
311  {
312  //Valid IPv6 address?
313  if(interface->ipv6Context.addrList[i].state == IPV6_ADDR_STATE_PREFERRED ||
314  interface->ipv6Context.addrList[i].state == IPV6_ADDR_STATE_DEPRECATED)
315  {
316  MdnsIpv6AddrEntry *entry;
317 
318  //Point to the current entry
319  entry = &context->ipv6AddrList[i];
320 
321  //Format AAAA resource record
322  entry->record.rtype = HTONS(DNS_RR_TYPE_AAAA);
323  entry->record.rclass = HTONS(DNS_RR_CLASS_IN);
324  entry->record.ttl = htonl(MDNS_DEFAULT_RR_TTL);
325  entry->record.rdlength = HTONS(sizeof(Ipv6Addr));
326 
327  //Copy IPv6 address
328  ipv6CopyAddr(entry->record.rdata, &interface->ipv6Context.addrList[i].addr);
329 
330  //Generate domain name for reverse DNS lookup
331  dnsGenerateIpv6ReverseName(&interface->ipv6Context.addrList[i].addr,
332  entry->reverseName);
333 
334  //The entry is valid
335  context->ipv6AddrList[i].valid = TRUE;
336 
337  //Increment the number of valid IPv6 addresses
338  context->ipv6AddrCount++;
339  }
340  else
341  {
342  //Invalidate the entry
343  context->ipv6AddrList[i].valid = FALSE;
344  }
345  }
346 #endif
347 
348  //Force mDNS responder to start probing again
349  context->state = MDNS_STATE_INIT;
350 
351 #if (DNS_SD_RESPONDER_SUPPORT == ENABLED)
352  //Restart probing process (service instance name)
353  dnsSdResponderStartProbing(interface->dnsSdResponderContext);
354 #endif
355 
356  //Successful processing
357  return NO_ERROR;
358 }
359 
360 
361 /**
362  * @brief mDNS responder timer handler
363  *
364  * This routine must be periodically called by the TCP/IP stack to
365  * manage mDNS operation
366  *
367  * @param[in] context Pointer to the mDNS responder context
368  **/
369 
371 {
372  systime_t time;
373  systime_t delay;
374  NetInterface *interface;
376 
377  //Make sure the mDNS responder has been properly instantiated
378  if(context == NULL)
379  return;
380 
381  //Point to the underlying network interface
382  interface = context->settings.interface;
383 
384  //Get current time
385  time = osGetSystemTime();
386 
387  //Check current state
388  if(context->state == MDNS_STATE_INIT)
389  {
390  //Wait for the link to be up before starting mDNS responder
391  if(context->running && interface->linkState)
392  {
393  //Valid host name?
394  if(context->hostname[0] != '\0')
395  {
396  //Check whether a valid IPv4 or IPv6 address has been assigned
397  if(context->ipv4AddrCount > 0 || context->ipv6AddrCount > 0)
398  {
400  }
401  }
402  }
403  }
404  else if(context->state == MDNS_STATE_WAITING)
405  {
406  //Check current time
407  if(timeCompare(time, context->timestamp + MDNS_INIT_DELAY) >= 0)
408  {
409  //Initial random delay
411  //Start probing
413  }
414  }
415  else if(context->state == MDNS_STATE_PROBING)
416  {
417  //Probing failed?
418  if(context->conflict && context->retransmitCount > 0)
419  {
420  //Programmatically change the host name
422 
423  //Probe again, and repeat as necessary until a unique name is found
426  }
427  //Tie-break lost?
428  else if(context->tieBreakLost && context->retransmitCount > 0)
429  {
430  //The host defers to the winning host by waiting one second, and
431  //then begins probing for this record again
434  }
435  else
436  {
437  //Check current time
438  if(timeCompare(time, context->timestamp + context->timeout) >= 0)
439  {
440  //Probing is on-going?
441  if(context->retransmitCount < MDNS_PROBE_NUM)
442  {
443  //First probe?
444  if(context->retransmitCount == 0)
445  {
446  //Apparently conflicting mDNS responses received before the
447  //first probe packet is sent must be silently ignored
448  context->conflict = FALSE;
449  context->tieBreakLost = FALSE;
450  }
451 
452  //Send probe packet
453  mdnsResponderSendProbe(context);
454 
455  //Save the time at which the packet was sent
456  context->timestamp = time;
457  //Time interval between subsequent probe packets
458  context->timeout = MDNS_PROBE_DELAY;
459  //Increment retransmission counter
460  context->retransmitCount++;
461  }
462  //Probing is complete?
463  else
464  {
465  //The mDNS responder must send unsolicited mDNS responses
466  //containing all of its newly registered resource records
467  if(context->settings.numAnnouncements > 0)
468  {
470  }
471  else
472  {
474  }
475  }
476  }
477  }
478  }
479  else if(context->state == MDNS_STATE_ANNOUNCING)
480  {
481  //Whenever a mDNS responder receives any mDNS response (solicited or
482  //otherwise) containing a conflicting resource record, the conflict
483  //must be resolved
484  if(context->conflict)
485  {
486  //Probe again, and repeat as necessary until a unique name is found
488  }
489  else
490  {
491  //Check current time
492  if(timeCompare(time, context->timestamp + context->timeout) >= 0)
493  {
494  //Send announcement packet
496 
497  //Save the time at which the packet was sent
498  context->timestamp = time;
499  //Increment retransmission counter
500  context->retransmitCount++;
501 
502  //First announcement packet?
503  if(context->retransmitCount == 1)
504  {
505  //The mDNS responder must send at least two unsolicited
506  //responses, one second apart
507  context->timeout = MDNS_ANNOUNCE_DELAY;
508  }
509  else
510  {
511  //To provide increased robustness against packet loss, a mDNS
512  //responder may send up to eight unsolicited responses, provided
513  //that the interval between unsolicited responses increases by
514  //at least a factor of two with every response sent
515  context->timeout *= 2;
516  }
517 
518  //Last announcement packet?
519  if(context->retransmitCount >= context->settings.numAnnouncements)
520  {
521  //A mDNS responder must not send regular periodic announcements
523  }
524  }
525  }
526  }
527  else if(context->state == MDNS_STATE_IDLE)
528  {
529  //Whenever a mDNS responder receives any mDNS response (solicited or
530  //otherwise) containing a conflicting resource record, the conflict
531  //must be resolved
532  if(context->conflict)
533  {
534  //Probe again, and repeat as necessary until a unique name is found
536  }
537  }
538 
539 #if (IPV4_SUPPORT == ENABLED)
540  //Any response message pending to be sent?
541  if(context->ipv4Response.buffer != NULL)
542  {
543  //Check whether the time delay has elapsed
544  if(timeCompare(time, context->ipv4Response.timestamp +
545  context->ipv4Response.timeout) >= 0)
546  {
547 #if (DNS_SD_RESPONDER_SUPPORT == ENABLED)
548  //Generate additional records (DNS-SD)
550  &context->ipv4Response, FALSE);
551 #endif
552  //Generate additional records (mDNS)
554  &context->ipv4Response, FALSE);
555 
556  //Use mDNS IPv4 multicast address
557  destIpAddr.length = sizeof(Ipv4Addr);
559 
560  //Send mDNS response message
561  mdnsSendMessage(interface, &context->ipv4Response, &destIpAddr,
562  MDNS_PORT);
563 
564  //Free previously allocated memory
565  mdnsDeleteMessage(&context->ipv4Response);
566  }
567  }
568 #endif
569 
570 #if (IPV6_SUPPORT == ENABLED)
571  //Any response message pending to be sent?
572  if(context->ipv6Response.buffer != NULL)
573  {
574  //Check whether the time delay has elapsed
575  if(timeCompare(time, context->ipv6Response.timestamp +
576  context->ipv6Response.timeout) >= 0)
577  {
578 #if (DNS_SD_RESPONDER_SUPPORT == ENABLED)
579  //Generate additional records (DNS-SD)
581  &context->ipv6Response, FALSE);
582 #endif
583  //Generate additional records (mDNS)
585  &context->ipv6Response, FALSE);
586 
587  //Use mDNS IPv6 multicast address
588  destIpAddr.length = sizeof(Ipv6Addr);
590 
591  //Send mDNS response message
592  mdnsSendMessage(interface, &context->ipv6Response, &destIpAddr,
593  MDNS_PORT);
594 
595  //Free previously allocated memory
596  mdnsDeleteMessage(&context->ipv6Response);
597  }
598  }
599 #endif
600 }
601 
602 
603 /**
604  * @brief Callback function for link change event
605  * @param[in] context Pointer to the mDNS responder context
606  **/
607 
609 {
610  //Make sure the mDNS responder has been properly instantiated
611  if(context == NULL)
612  return;
613 
614 #if (IPV4_SUPPORT == ENABLED)
615  //Free any response message pending to be sent
616  mdnsDeleteMessage(&context->ipv4Response);
617 #endif
618 
619 #if (IPV6_SUPPORT == ENABLED)
620  //Free any response message pending to be sent
621  mdnsDeleteMessage(&context->ipv6Response);
622 #endif
623 
624  //Whenever a mDNS responder receives an indication of a link
625  //change event, it must perform probing and announcing
627 }
628 
629 #endif
#define MdnsResponderContext
const Ipv6Addr MDNS_IPV6_MULTICAST_ADDR
Definition: mdns_common.c:59
DnsIpv4AddrResourceRecord record
A resource record.
#define MDNS_PROBE_NUM
#define netMutex
Definition: net_legacy.h:195
IP network address.
Definition: ip.h:90
#define TRUE
Definition: os_port.h:50
char_t reverseName[DNS_MAX_IPV6_REVERSE_NAME_LEN+1]
Reverse DNS lookup for IPv6.
Ipv6Addr
Definition: ipv6.h:260
#define MDNS_PROBE_DEFER_DELAY
@ DNS_RR_CLASS_IN
Internet.
Definition: dns_common.h:124
@ MDNS_STATE_IDLE
#define osStrlen(s)
Definition: os_port.h:165
mDNS responder settings
#define timeCompare(t1, t2)
Definition: os_port.h:40
uint32_t Ipv4Addr
IPv4 network address.
Definition: ipv4.h:297
error_t dnsSdResponderStartProbing(DnsSdResponderContext *context)
Restart probing process.
MdnsState
mDNS responder states
#define MDNS_RAND_DELAY_MIN
error_t mdnsResponderSetHostname(MdnsResponderContext *context, const char_t *hostname)
Set host name.
#define MDNS_PORT
Definition: mdns_common.h:53
error_t mdnsResponderSendGoodbye(MdnsResponderContext *context)
Send goodbye packet.
#define FALSE
Definition: os_port.h:46
#define MDNS_RAND_DELAY_MAX
@ ERROR_INVALID_PARAMETER
Invalid parameter.
Definition: error.h:47
#define htonl(value)
Definition: cpu_endian.h:414
uint32_t netGenerateRandRange(uint32_t min, uint32_t max)
Generate a random value in the specified range.
Definition: net_misc.c:948
error_t
Error codes.
Definition: error.h:43
void mdnsResponderChangeHostname(MdnsResponderContext *context)
Programmatically change the host name.
uint32_t ttl
TTL resource record.
error_t mdnsResponderInit(MdnsResponderContext *context, const MdnsResponderSettings *settings)
mDNS responder initialization
@ MDNS_STATE_PROBING
#define NetInterface
Definition: net.h:36
@ ERROR_INVALID_LENGTH
Definition: error.h:111
NetInterface * netGetDefaultInterface(void)
Get default network interface.
Definition: net.c:467
@ DNS_RR_TYPE_A
Host address.
Definition: dns_common.h:137
#define MDNS_PROBE_DELAY
error_t mdnsResponderStart(MdnsResponderContext *context)
Start mDNS responder.
#define TRACE_INFO(...)
Definition: debug.h:95
#define IPV4_ADDR_LIST_SIZE
Definition: ipv4.h:69
@ MDNS_STATE_WAITING
MdnsState mdnsResponderGetState(MdnsResponderContext *context)
Retrieve current state.
void mdnsResponderTick(MdnsResponderContext *context)
mDNS responder timer handler
uint_t numAnnouncements
Number of announcement packets.
void dnsGenerateIpv4ReverseName(Ipv4Addr ipv4Addr, char_t *buffer)
Generate domain name for reverse DNS lookup (IPv4)
Definition: dns_common.c:479
@ MDNS_STATE_INIT
NetInterface * interface
Underlying network interface.
uint32_t systime_t
System time.
error_t mdnsResponderSendProbe(MdnsResponderContext *context)
Send probe packet.
#define MDNS_RESPONDER_MAX_HOSTNAME_LEN
void mdnsResponderGetDefaultSettings(MdnsResponderSettings *settings)
Initialize settings with default values.
char char_t
Definition: compiler_port.h:48
void dnsSdResponderGenerateAdditionalRecords(NetInterface *interface, MdnsMessage *response, bool_t legacyUnicast)
Additional record generation.
void mdnsResponderChangeState(MdnsResponderContext *context, MdnsState newState, systime_t delay)
Update FSM state.
#define MDNS_INIT_DELAY
uint32_t time
#define ipv6CopyAddr(destIpAddr, srcIpAddr)
Definition: ipv6.h:123
void mdnsResponderLinkChangeEvent(MdnsResponderContext *context)
Callback function for link change event.
void mdnsResponderGenerateAdditionalRecords(MdnsResponderContext *context, MdnsMessage *response, bool_t legacyUnicast)
Generate additional records.
Helper functions for DNS-SD responder.
void mdnsDeleteMessage(MdnsMessage *message)
release a mDNS message
Definition: mdns_common.c:434
@ MDNS_STATE_ANNOUNCING
#define HTONS(value)
Definition: cpu_endian.h:410
#define IPV6_ADDR_LIST_SIZE
Definition: ipv6.h:65
void osAcquireMutex(OsMutex *mutex)
Acquire ownership of the specified mutex object.
MdnsResponderStateChangeCallback stateChangeEvent
FSM state change event.
Helper functions for mDNS responder.
void osReleaseMutex(OsMutex *mutex)
Release ownership of the specified mutex object.
#define MDNS_PROBE_CONFLICT_DELAY
#define MDNS_ANNOUNCE_DELAY
IPv6 address entry.
error_t mdnsResponderStop(MdnsResponderContext *context)
Stop mDNS responder.
char_t hostname[]
Definition: tls.h:1606
#define ipv4CopyAddr(destIpAddr, srcIpAddr)
Definition: ipv4.h:155
char_t reverseName[DNS_MAX_IPV4_REVERSE_NAME_LEN+1]
Reverse DNS lookup for IPv4.
@ IPV4_ADDR_STATE_VALID
An address assigned to an interface whose use is unrestricted.
Definition: ipv4.h:204
IPv4 address entry.
void dnsGenerateIpv6ReverseName(const Ipv6Addr *ipv6Addr, char_t *buffer)
Generate domain name for reverse DNS lookup (IPv6)
Definition: dns_common.c:498
@ IPV6_ADDR_STATE_PREFERRED
An address assigned to an interface whose use is unrestricted.
Definition: ipv6.h:175
#define MDNS_IPV4_MULTICAST_ADDR
Definition: mdns_common.h:65
unsigned int uint_t
Definition: compiler_port.h:50
#define osMemset(p, value, length)
Definition: os_port.h:135
TCP/IP stack core.
@ IPV6_ADDR_STATE_DEPRECATED
An address assigned to an interface whose use is discouraged.
Definition: ipv6.h:176
#define MDNS_DEFAULT_RR_TTL
Definition: mdns_common.h:47
@ DNS_RR_TYPE_AAAA
IPv6 address.
Definition: dns_common.h:147
Data logging functions for debugging purpose (DNS)
#define osStrcpy(s1, s2)
Definition: os_port.h:207
systime_t mdnsResponderTickCounter
#define MDNS_ANNOUNCE_NUM
error_t mdnsResponderStartProbing(MdnsResponderContext *context)
Restart probing process.
error_t mdnsResponderSendAnnouncement(MdnsResponderContext *context)
Send announcement packet.
@ NO_ERROR
Success.
Definition: error.h:44
Debugging facilities.
DnsIpv6AddrResourceRecord record
AAAA resource record.
error_t mdnsSendMessage(NetInterface *interface, const MdnsMessage *message, const IpAddr *destIpAddr, uint_t destPort)
Send mDNS message.
Definition: mdns_common.c:458
mDNS responder (Multicast DNS)
systime_t osGetSystemTime(void)
Retrieve system time.
Ipv4Addr destIpAddr
Definition: ipcp.h:80