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