dns_sd_responder.c
Go to the documentation of this file.
1 /**
2  * @file dns_sd_responder.c
3  * @brief DNS-SD responder (DNS-Based Service Discovery)
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2010-2026 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  * @section Description
28  *
29  * DNS-SD allows clients to discover a list of named instances of that
30  * desired service, using standard DNS queries. Refer to the following
31  * RFCs for complete details:
32  * - RFC 6763: DNS-Based Service Discovery
33  * - RFC 2782: A DNS RR for specifying the location of services (DNS SRV)
34  *
35  * @author Oryx Embedded SARL (www.oryx-embedded.com)
36  * @version 2.6.0
37  **/
38 
39 //Switch to the appropriate trace level
40 #define TRACE_LEVEL DNS_SD_TRACE_LEVEL
41 
42 //Dependencies
43 #include "core/net.h"
44 #include "mdns/mdns_responder.h"
47 #include "debug.h"
48 
49 //Check TCP/IP stack configuration
50 #if (DNS_SD_RESPONDER_SUPPORT == ENABLED)
51 
52 
53 /**
54  * @brief Initialize settings with default values
55  * @param[out] settings Structure that contains DNS-SD responder settings
56  **/
57 
59 {
60  //Underlying network interface
61  settings->interface = NULL;
62 
63  //DNS-SD services
64  settings->numServices = 0;
65  settings->services = NULL;
66 
67  //Number of announcement packets
69  //TTL resource record
70  settings->ttl = DNS_SD_DEFAULT_RR_TTL;
71  //FSM state change event
72  settings->stateChangeEvent = NULL;
73 }
74 
75 
76 /**
77  * @brief DNS-DS initialization
78  * @param[in] context Pointer to the DNS-SD responder context
79  * @param[in] settings DNS-SD specific settings
80  * @return Error code
81  **/
82 
84  const DnsSdResponderSettings *settings)
85 {
86  uint_t i;
87  NetInterface *interface;
88  DnsSdResponderService *service;
89 
90  //Debug message
91  TRACE_INFO("Initializing DNS-SD...\r\n");
92 
93  //Ensure the parameters are valid
94  if(context == NULL || settings == NULL)
96 
97  //Check settings
98  if(settings->interface == NULL || settings->services == NULL ||
99  settings->numServices < 1)
100  {
102  }
103 
104  //Point to the underlying network interface
105  interface = settings->interface;
106 
107  //Clear the DNS-SD responder context
108  osMemset(context, 0, sizeof(DnsSdResponderContext));
109 
110  //Attach TCP/IP stack context
111  context->netContext = settings->interface->netContext;
112 
113  //Save user settings
114  context->interface = settings->interface;
115  context->numServices = settings->numServices;
116  context->services = settings->services;
117  context->numAnnouncements = settings->numAnnouncements;
118  context->ttl = settings->ttl;
119  context->stateChangeEvent = settings->stateChangeEvent;
120 
121  //DNS-SD responder is currently suspended
122  context->running = FALSE;
123 
124  //Loop through the list of registered services
125  for(i = 0; i < context->numServices; i++)
126  {
127  //Point to the current entry
128  service = &context->services[i];
129 
130  //Clear entry
131  osMemset(service, 0, sizeof(DnsSdResponderService));
132 
133  //Attach DNS-SD responder context
134  service->context = context;
135  //Initialize state machine
136  service->state = MDNS_STATE_INIT;
137  }
138 
139  //Get exclusive access
140  netLock(context->netContext);
141  //Attach the DNS-SD responder context to the network interface
142  interface->dnsSdResponderContext = context;
143  //Release exclusive access
144  netUnlock(context->netContext);
145 
146  //Successful initialization
147  return NO_ERROR;
148 }
149 
150 
151 /**
152  * @brief Start DNS-SD responder
153  * @param[in] context Pointer to the DNS-SD responder context
154  * @return Error code
155  **/
156 
158 {
159  uint_t i;
160 
161  //Make sure the DNS-SD responder context is valid
162  if(context == NULL)
164 
165  //Debug message
166  TRACE_INFO("Starting DNS-SD...\r\n");
167 
168  //Get exclusive access
169  netLock(context->netContext);
170 
171  //Start DNS-SD responder
172  context->running = TRUE;
173 
174  //Loop through the list of registered services
175  for(i = 0; i < context->numServices; i++)
176  {
177  //Initialize state machine
178  context->services[i].state = MDNS_STATE_INIT;
179  }
180 
181  //Release exclusive access
182  netUnlock(context->netContext);
183 
184  //Successful processing
185  return NO_ERROR;
186 }
187 
188 
189 /**
190  * @brief Stop DNS-SD responder
191  * @param[in] context Pointer to the DNS-SD responder context
192  * @return Error code
193  **/
194 
196 {
197  uint_t i;
198 
199  //Make sure the DNS-SD responder context is valid
200  if(context == NULL)
202 
203  //Debug message
204  TRACE_INFO("Stopping DNS-SD...\r\n");
205 
206  //Get exclusive access
207  netLock(context->netContext);
208 
209  //Suspend DNS-SD responder
210  context->running = FALSE;
211 
212  //Loop through the list of registered services
213  for(i = 0; i < context->numServices; i++)
214  {
215  //Initialize state machine
216  context->services[i].state = MDNS_STATE_INIT;
217  }
218 
219  //Release exclusive access
220  netUnlock(context->netContext);
221 
222  //Successful processing
223  return NO_ERROR;
224 }
225 
226 
227 /**
228  * @brief Register a DNS-SD service
229  * @param[in] context Pointer to the DNS-SD responder context
230  * @param[in] index Zero-based index identifying a slot
231  * @param[in] instanceName NULL-terminated string that contains the service
232  * instance name
233  * @param[in] serviceName NULL-terminated string that contains the name of the
234  * service to be registered
235  * @param[in] priority Priority field
236  * @param[in] weight Weight field
237  * @param[in] port Port number
238  * @param[in] metadata NULL-terminated string that contains the discovery-time
239  * metadata (TXT record)
240  * @return Error code
241  **/
242 
244  uint_t index, const char_t *instanceName, const char_t *serviceName,
245  uint16_t priority, uint16_t weight, uint16_t port, const char_t *metadata)
246 {
247  size_t i;
248  size_t j;
249  size_t k;
250  size_t n;
251  DnsSdResponderService *service;
252 
253  //Check parameters
254  if(context == NULL || instanceName == NULL || serviceName == NULL ||
255  metadata == NULL)
256  {
258  }
259 
260  //The implementation limits the number of services that can be advertised
261  if(index >= context->numServices)
263 
264  //Make sure the length of the instance name is acceptable
265  if(osStrlen(instanceName) > DNS_SD_MAX_INSTANCE_NAME_LEN)
266  return ERROR_INVALID_LENGTH;
267 
268  //Make sure the length of the service name is acceptable
269  if(osStrlen(serviceName) > DNS_SD_MAX_SERVICE_NAME_LEN)
270  return ERROR_INVALID_LENGTH;
271 
272  //Get exclusive access
273  netLock(context->netContext);
274 
275  //Point to the specified entry
276  service = &context->services[index];
277 
278  //Valid service?
279  if(service->instanceName[0] != '\0' &&
280  service->serviceName[0] != '\0')
281  {
282  //Send a goodbye packet
283  dnsSdResponderSendGoodbye(service);
284  }
285 
286  //Instance name
287  osStrcpy(service->instanceName, instanceName);
288  //Service name
289  osStrcpy(service->serviceName, serviceName);
290 
291  //Priority field
292  service->priority = priority;
293  //Weight field
294  service->weight = weight;
295  //Port number
296  service->port = port;
297 
298  //Clear TXT record
299  service->metadataLen = 0;
300 
301  //Point to the beginning of the information string
302  i = 0;
303  j = 0;
304 
305  //Point to the beginning of the resulting TXT record data
306  k = 0;
307 
308  //Format TXT record
309  while(1)
310  {
311  //End of text data?
312  if(metadata[i] == '\0' || metadata[i] == ';')
313  {
314  //Calculate the length of the text data
315  n = MIN(i - j, UINT8_MAX);
316 
317  //Check the length of the resulting TXT record
318  if((service->metadataLen + n + 1) > DNS_SD_MAX_METADATA_LEN)
319  break;
320 
321  //Write length field
322  service->metadata[k] = n;
323  //Write text data
324  osMemcpy(service->metadata + k + 1, metadata + j, n);
325 
326  //Jump to the next text data
327  j = i + 1;
328  //Advance write index
329  k += n + 1;
330 
331  //Update the length of the TXT record
332  service->metadataLen += n + 1;
333 
334  //End of string detected?
335  if(metadata[i] == '\0')
336  break;
337  }
338 
339  //Advance read index
340  i++;
341  }
342 
343  //Empty TXT record?
344  if(service->metadataLen == 0)
345  {
346  //An empty TXT record shall contain a single zero byte
347  service->metadata[0] = 0;
348  service->metadataLen = 1;
349  }
350 
351  //Restart probing process
353 
354  //Release exclusive access
355  netUnlock(context->netContext);
356 
357  //Successful processing
358  return NO_ERROR;
359 }
360 
361 
362 /**
363  * @brief Unregister a DNS-SD service
364  * @param[in] context Pointer to the DNS-SD responder context
365  * @param[in] index Zero-based index identifying the service to be unregistered
366  * @return Error code
367  **/
368 
370  uint_t index)
371 {
372  DnsSdResponderService *service;
373 
374  //Check parameters
375  if(context == NULL)
377 
378  //The implementation limits the number of services that can be advertised
379  if(index >= context->numServices)
381 
382  //Get exclusive access
383  netLock(context->netContext);
384 
385  //Point to the specified entry
386  service = &context->services[index];
387 
388  //Valid service?
389  if(service->instanceName[0] != '\0' &&
390  service->serviceName[0] != '\0')
391  {
392  //Send a goodbye packet
393  dnsSdResponderSendGoodbye(service);
394  }
395 
396  //Remove the service from the list
397  service->instanceName[0] = '\0';
398  service->serviceName[0] = '\0';
399 
400  //Release exclusive access
401  netUnlock(context->netContext);
402 
403  //Successful processing
404  return NO_ERROR;
405 }
406 
407 
408 /**
409  * @brief Restart probing process
410  * @param[in] context Pointer to the DNS-SD responder context
411  * @return Error code
412  **/
413 
415 {
416  uint_t i;
417 
418  //Check parameter
419  if(context == NULL)
421 
422  //Loop through the list of registered services
423  for(i = 0; i < context->numServices; i++)
424  {
425  //Force DNS-SD to start probing again
426  context->services[i].state = MDNS_STATE_INIT;
427  }
428 
429  //Successful processing
430  return NO_ERROR;
431 }
432 
433 
434 /**
435  * @brief DNS-SD responder timer handler
436  *
437  * This routine must be periodically called by the TCP/IP stack to
438  * manage DNS-SD operation
439  *
440  * @param[in] context Pointer to the DNS-SD responder context
441  **/
442 
444 {
445  uint_t i;
446  systime_t time;
447  systime_t delay;
448  NetInterface *interface;
449  DnsSdResponderService *service;
450 
451  //Make sure the DNS-SD responder has been properly instantiated
452  if(context == NULL)
453  return;
454 
455  //Point to the underlying network interface
456  interface = context->interface;
457 
458  //Get current time
459  time = osGetSystemTime();
460 
461  //Loop through the list of registered services
462  for(i = 0; i < context->numServices; i++)
463  {
464  //Point to the current entry
465  service = &context->services[i];
466 
467  //Valid service?
468  if(service->instanceName[0] != '\0' &&
469  service->serviceName[0] != '\0')
470  {
471  //Check current state
472  if(service->state == MDNS_STATE_INIT)
473  {
474  //Ensure the mDNS and DNS-SD services are running
475  if(context->running && interface->mdnsResponderContext != NULL)
476  {
477  //Wait for mDNS probing to complete
478  if(interface->mdnsResponderContext->state == MDNS_STATE_IDLE)
479  {
480  //Initial random delay
481  delay = netGenerateRandRange(context->netContext,
483 
484  //Perform probing
486  }
487  }
488  }
489  else if(service->state == MDNS_STATE_PROBING)
490  {
491  //Probing failed?
492  if(service->conflict && service->retransmitCount > 0)
493  {
494  //Programmatically change the service instance name
496 
497  //Probe again, and repeat as necessary until a unique name is found
500  }
501  //Tie-break lost?
502  else if(service->tieBreakLost && service->retransmitCount > 0)
503  {
504  //The host defers to the winning host by waiting one second, and
505  //then begins probing for this record again
508  }
509  else
510  {
511  //Check current time
512  if(timeCompare(time, service->timestamp + service->timeout) >= 0)
513  {
514  //Probing is on-going?
515  if(service->retransmitCount < MDNS_PROBE_NUM)
516  {
517  //First probe?
518  if(service->retransmitCount == 0)
519  {
520  //Apparently conflicting mDNS responses received before
521  //the first probe packet is sent must be silently ignored
522  service->conflict = FALSE;
523  service->tieBreakLost = FALSE;
524  }
525 
526  //Send probe packet
527  dnsSdResponderSendProbe(service);
528 
529  //Save the time at which the packet was sent
530  service->timestamp = time;
531  //Time interval between subsequent probe packets
532  service->timeout = MDNS_PROBE_DELAY;
533  //Increment retransmission counter
534  service->retransmitCount++;
535  }
536  //Probing is complete?
537  else
538  {
539  //The mDNS responder must send unsolicited mDNS responses
540  //containing all of its newly registered resource records
541  if(context->numAnnouncements > 0)
542  {
544  }
545  else
546  {
548  }
549  }
550  }
551  }
552  }
553  else if(service->state == MDNS_STATE_ANNOUNCING)
554  {
555  //Whenever a mDNS responder receives any mDNS response (solicited or
556  //otherwise) containing a conflicting resource record, the conflict
557  //must be resolved
558  if(service->conflict)
559  {
560  //Probe again, and repeat as necessary until a unique name is
561  //found
563  }
564  else
565  {
566  //Check current time
567  if(timeCompare(time, service->timestamp + service->timeout) >= 0)
568  {
569  //Send announcement packet
571 
572  //Save the time at which the packet was sent
573  service->timestamp = time;
574  //Increment retransmission counter
575  service->retransmitCount++;
576 
577  //First announcement packet?
578  if(service->retransmitCount == 1)
579  {
580  //The mDNS responder must send at least two unsolicited
581  //responses, one second apart
582  service->timeout = MDNS_ANNOUNCE_DELAY;
583  }
584  else
585  {
586  //To provide increased robustness against packet loss, a
587  //mDNS responder may send up to eight unsolicited responses,
588  //provided that the interval between unsolicited responses
589  //increases by at least a factor of two with every response
590  //sent
591  service->timeout *= 2;
592  }
593 
594  //Last announcement packet?
595  if(service->retransmitCount >= context->numAnnouncements)
596  {
597  //A mDNS responder must not send regular periodic
598  //announcements
600  }
601  }
602  }
603  }
604  else if(service->state == MDNS_STATE_IDLE)
605  {
606  //Whenever a mDNS responder receives any mDNS response (solicited or
607  //otherwise) containing a conflicting resource record, the conflict
608  //must be resolved
609  if(service->conflict)
610  {
611  //Probe again, and repeat as necessary until a unique name is
612  //found
614  }
615  }
616  }
617  }
618 }
619 
620 
621 /**
622  * @brief Callback function for link change event
623  * @param[in] context Pointer to the DNS-SD responder context
624  **/
625 
627 {
628  uint_t i;
629 
630  //Make sure the DNS-SD responder has been properly instantiated
631  if(context == NULL)
632  return;
633 
634  //Loop through the list of registered services
635  for(i = 0; i < context->numServices; i++)
636  {
637  //Whenever a mDNS responder receives an indication of a link change
638  //event, it must perform probing and announcing
639  context->services[i].state = MDNS_STATE_INIT;
640  }
641 }
642 
643 
644 /**
645  * @brief Release DNS-SD responder context
646  * @param[in] context Pointer to the DNS-SD responder context
647  **/
648 
650 {
651  NetInterface *interface;
652 
653  //Make sure the DNS-SD responder context is valid
654  if(context != NULL)
655  {
656  //Get exclusive access
657  netLock(context->netContext);
658 
659  //Point to the underlying network interface
660  interface = context->interface;
661  //Detach the DNS-SD responder context from the network interface
662  interface->dnsSdResponderContext = NULL;
663 
664  //Release exclusive access
665  netUnlock(context->netContext);
666 
667  //Clear DNS-SD responder context
668  osMemset(context, 0, sizeof(DnsSdResponderContext));
669  }
670 }
671 
672 #endif
error_t dnsSdResponderSendGoodbye(DnsSdResponderService *service)
Send goodbye packet.
void netUnlock(NetContext *context)
Release exclusive access to the core of the TCP/IP stack.
Definition: net.c:319
error_t dnsSdResponderStop(DnsSdResponderContext *context)
Stop DNS-SD responder.
NetInterface * interface
Underlying network interface.
#define MDNS_PROBE_NUM
error_t dnsSdResponderInit(DnsSdResponderContext *context, const DnsSdResponderSettings *settings)
DNS-DS initialization.
uint16_t weight
Definition: dns_common.h:269
#define DNS_SD_MAX_METADATA_LEN
#define TRUE
Definition: os_port.h:50
#define DNS_SD_DEFAULT_RR_TTL
error_t dnsSdResponderSendAnnouncement(DnsSdResponderService *service)
Send announcement packet.
DnsSdStateChangeCallback stateChangeEvent
FSM state change event.
#define MDNS_PROBE_DEFER_DELAY
uint32_t ttl
TTL resource record.
error_t dnsSdResponderStart(DnsSdResponderContext *context)
Start DNS-SD responder.
@ MDNS_STATE_IDLE
#define osStrlen(s)
Definition: os_port.h:168
uint_t numServices
Maximum number of DNS-SD services that can be registered.
#define timeCompare(t1, t2)
Definition: os_port.h:40
error_t dnsSdResponderStartProbing(DnsSdResponderContext *context)
Restart probing process.
#define MDNS_RAND_DELAY_MIN
void dnsSdResponderTick(DnsSdResponderContext *context)
DNS-SD responder timer handler.
#define FALSE
Definition: os_port.h:46
#define MDNS_RAND_DELAY_MAX
@ ERROR_INVALID_PARAMETER
Invalid parameter.
Definition: error.h:47
#define osMemcpy(dest, src, length)
Definition: os_port.h:144
error_t
Error codes.
Definition: error.h:43
void dnsSdResponderChangeInstanceName(DnsSdResponderService *service)
Programmatically change the service instance name.
DNS-SD responder (DNS-Based Service Discovery)
@ MDNS_STATE_PROBING
#define NetInterface
Definition: net.h:40
@ ERROR_INVALID_LENGTH
Definition: error.h:111
#define DnsSdResponderContext
uint32_t netGenerateRandRange(NetContext *context, uint32_t min, uint32_t max)
Generate a random value in the specified range.
Definition: net_misc.c:983
#define MDNS_PROBE_DELAY
#define DnsSdResponderService
#define TRACE_INFO(...)
Definition: debug.h:105
#define DNS_SD_MAX_INSTANCE_NAME_LEN
#define MIN(a, b)
Definition: os_port.h:63
@ MDNS_STATE_INIT
error_t dnsSdResponderSendProbe(DnsSdResponderService *service)
Send probe packet.
DNS-SD responder settings.
uint32_t systime_t
System time.
uint16_t port
Definition: dns_common.h:270
char char_t
Definition: compiler_port.h:55
void dnsSdResponderChangeState(DnsSdResponderService *service, MdnsState newState, systime_t delay)
Update FSM state.
uint32_t time
Helper functions for DNS-SD responder.
@ MDNS_STATE_ANNOUNCING
uint8_t n
#define MDNS_PROBE_CONFLICT_DELAY
#define MDNS_ANNOUNCE_DELAY
uint_t numAnnouncements
Number of announcement packets.
void netLock(NetContext *context)
Get exclusive access to the core of the TCP/IP stack.
Definition: net.c:307
void dnsSdResponderDeinit(DnsSdResponderContext *context)
Release DNS-SD responder context.
error_t dnsSdResponderRegisterService(DnsSdResponderContext *context, uint_t index, const char_t *instanceName, const char_t *serviceName, uint16_t priority, uint16_t weight, uint16_t port, const char_t *metadata)
Register a DNS-SD service.
error_t dnsSdResponderUnregisterService(DnsSdResponderContext *context, uint_t index)
Unregister a DNS-SD service.
unsigned int uint_t
Definition: compiler_port.h:57
#define DNS_SD_MAX_SERVICE_NAME_LEN
#define osMemset(p, value, length)
Definition: os_port.h:138
TCP/IP stack core.
DnsSdResponderService * services
DNS-SD services.
uint16_t priority
Definition: dns_common.h:268
#define osStrcpy(s1, s2)
Definition: os_port.h:210
#define MDNS_ANNOUNCE_NUM
@ NO_ERROR
Success.
Definition: error.h:44
Debugging facilities.
void dnsSdResponderGetDefaultSettings(DnsSdResponderSettings *settings)
Initialize settings with default values.
void dnsSdResponderLinkChangeEvent(DnsSdResponderContext *context)
Callback function for link change event.
mDNS responder (Multicast DNS)
systime_t osGetSystemTime(void)
Retrieve system time.