dns_sd.c
Go to the documentation of this file.
1 /**
2  * @file dns_sd.c
3  * @brief DNS-SD (DNS-Based Service Discovery)
4  *
5  * @section License
6  *
7  * Copyright (C) 2010-2018 Oryx Embedded SARL. All rights reserved.
8  *
9  * This file is part of CycloneTCP Open.
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software Foundation,
23  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
24  *
25  * @section Description
26  *
27  * DNS-SD allows clients to discover a list of named instances of that
28  * desired service, using standard DNS queries. Refer to the following
29  * RFCs for complete details:
30  * - RFC 6763: DNS-Based Service Discovery
31  * - RFC 2782: A DNS RR for specifying the location of services (DNS SRV)
32  *
33  * @author Oryx Embedded SARL (www.oryx-embedded.com)
34  * @version 1.9.0
35  **/
36 
37 //Switch to the appropriate trace level
38 #define TRACE_LEVEL DNS_SD_TRACE_LEVEL
39 
40 //Dependencies
41 #include <stdlib.h>
42 #include <ctype.h>
43 #include "core/net.h"
44 #include "mdns/mdns_common.h"
45 #include "mdns/mdns_responder.h"
46 #include "dns/dns_debug.h"
47 #include "dns_sd/dns_sd.h"
48 #include "str.h"
49 #include "debug.h"
50 
51 //Check TCP/IP stack configuration
52 #if (DNS_SD_SUPPORT == ENABLED)
53 
54 //Tick counter to handle periodic operations
56 
57 
58 /**
59  * @brief Initialize settings with default values
60  * @param[out] settings Structure that contains DNS-SD settings
61  **/
62 
64 {
65  //Use default interface
66  settings->interface = netGetDefaultInterface();
67 
68  //Number of announcement packets
70  //TTL resource record
71  settings->ttl = DNS_SD_DEFAULT_RR_TTL;
72  //FSM state change event
73  settings->stateChangeEvent = NULL;
74 }
75 
76 
77 /**
78  * @brief DNS-DS initialization
79  * @param[in] context Pointer to the DNS-SD context
80  * @param[in] settings DNS-SD specific settings
81  * @return Error code
82  **/
83 
84 error_t dnsSdInit(DnsSdContext *context, const DnsSdSettings *settings)
85 {
86  NetInterface *interface;
87 
88  //Debug message
89  TRACE_INFO("Initializing DNS-SD...\r\n");
90 
91  //Ensure the parameters are valid
92  if(context == NULL || settings == NULL)
94 
95  //Invalid network interface?
96  if(settings->interface == NULL)
98 
99  //Point to the underlying network interface
100  interface = settings->interface;
101 
102  //Clear the DNS-SD context
103  memset(context, 0, sizeof(DnsSdContext));
104  //Save user settings
105  context->settings = *settings;
106 
107  //DNS-SD is currently suspended
108  context->running = FALSE;
109  //Initialize state machine
110  context->state = MDNS_STATE_INIT;
111 
112  //Attach the DNS-SD context to the network interface
113  interface->dnsSdContext = context;
114 
115  //Successful initialization
116  return NO_ERROR;
117 }
118 
119 
120 /**
121  * @brief Start mDNS responder
122  * @param[in] context Pointer to the DNS-SD context
123  * @return Error code
124  **/
125 
127 {
128  //Check parameter
129  if(context == NULL)
131 
132  //Debug message
133  TRACE_INFO("Starting DNS-SD...\r\n");
134 
135  //Get exclusive access
137 
138  //Start DNS-SD
139  context->running = TRUE;
140  //Initialize state machine
141  context->state = MDNS_STATE_INIT;
142 
143  //Release exclusive access
145 
146  //Successful processing
147  return NO_ERROR;
148 }
149 
150 
151 /**
152  * @brief Stop mDNS responder
153  * @param[in] context Pointer to the DNS-SD context
154  * @return Error code
155  **/
156 
158 {
159  //Check parameter
160  if(context == NULL)
162 
163  //Debug message
164  TRACE_INFO("Stopping DNS-SD...\r\n");
165 
166  //Get exclusive access
168 
169  //Suspend DNS-SD
170  context->running = FALSE;
171  //Reinitialize state machine
172  context->state = MDNS_STATE_INIT;
173 
174  //Release exclusive access
176 
177  //Successful processing
178  return NO_ERROR;
179 }
180 
181 
182 /**
183  * @brief Retrieve current state
184  * @param[in] context Pointer to the DNS-SD context
185  * @return Current DNS-SD state
186  **/
187 
189 {
190  MdnsState state;
191 
192  //Get exclusive access
194  //Get current state
195  state = context->state;
196  //Release exclusive access
198 
199  //Return current state
200  return state;
201 }
202 
203 
204 /**
205  * @brief Set service instance name
206  * @param[in] context Pointer to the DNS-SD context
207  * @param[in] instanceName NULL-terminated string that contains the service
208  * instance name
209  * @return Error code
210  **/
211 
212 error_t dnsSdSetInstanceName(DnsSdContext *context, const char_t *instanceName)
213 {
214  NetInterface *interface;
215 
216  //Check parameters
217  if(context == NULL || instanceName == NULL)
219 
220  //Get exclusive access
222 
223  //Point to the underlying network interface
224  interface = context->settings.interface;
225 
226  //Any registered services?
227  if(dnsSdGetNumServices(context) > 0)
228  {
229  //Check whether the link is up
230  if(interface->linkState)
231  {
232  //Send a goodbye packet
233  dnsSdSendGoodbye(context, NULL);
234  }
235  }
236 
237  //Set instance name
238  strSafeCopy(context->instanceName, instanceName,
240 
241  //Restart probing process
242  dnsSdStartProbing(context);
243 
244  //Release exclusive access
246 
247  //Successful processing
248  return NO_ERROR;
249 }
250 
251 
252 /**
253  * @brief Register a DNS-SD service
254  * @param[in] context Pointer to the DNS-SD context
255  * @param[in] serviceName NULL-terminated string that contains the name of the
256  * service to be registered
257  * @param[in] priority Priority field
258  * @param[in] weight Weight field
259  * @param[in] port Port number
260  * @param[in] metadata NULL-terminated string that contains the discovery-time
261  * metadata (TXT record)
262  * @return Error code
263  **/
264 
265 error_t dnsSdRegisterService(DnsSdContext *context, const char_t *serviceName,
266  uint16_t priority, uint16_t weight, uint16_t port, const char_t *metadata)
267 {
268  error_t error;
269  size_t i;
270  size_t j;
271  size_t k;
272  size_t n;
273  DnsSdService *entry;
274  DnsSdService *firstFreeEntry;
275 
276  //Check parameters
277  if(context == NULL || serviceName == NULL || metadata == NULL)
279 
280  //Get exclusive access
282 
283  //Keep track of the first free entry
284  firstFreeEntry = NULL;
285 
286  //Loop through the list of registered services
287  for(i = 0; i < DNS_SD_SERVICE_LIST_SIZE; i++)
288  {
289  //Point to the current entry
290  entry = &context->serviceList[i];
291 
292  //Check if the entry is currently in use
293  if(entry->name[0] != '\0')
294  {
295  //Check whether the specified service is already registered
296  if(!strcasecmp(entry->name, serviceName))
297  break;
298  }
299  else
300  {
301  //Keep track of the first free entry
302  if(firstFreeEntry == NULL)
303  firstFreeEntry = entry;
304  }
305  }
306 
307  //If the specified service is not yet registered, then a new
308  //entry should be created
309  if(i >= DNS_SD_SERVICE_LIST_SIZE)
310  entry = firstFreeEntry;
311 
312  //Check whether the service list runs out of space
313  if(entry != NULL)
314  {
315  //Service name
316  strSafeCopy(entry->name, serviceName, DNS_SD_MAX_SERVICE_NAME_LEN);
317 
318  //Priority field
319  entry->priority = priority;
320  //Weight field
321  entry->weight = weight;
322  //Port number
323  entry->port = port;
324 
325  //Clear TXT record
326  entry->metadataLength = 0;
327 
328  //Point to the beginning of the information string
329  i = 0;
330  j = 0;
331 
332  //Point to the beginning of the resulting TXT record data
333  k = 0;
334 
335  //Format TXT record
336  while(1)
337  {
338  //End of text data?
339  if(metadata[i] == '\0' || metadata[i] == ';')
340  {
341  //Calculate the length of the text data
342  n = MIN(i - j, UINT8_MAX);
343 
344  //Check the length of the resulting TXT record
345  if((entry->metadataLength + n + 1) > DNS_SD_MAX_METADATA_LEN)
346  break;
347 
348  //Write length field
349  entry->metadata[k] = n;
350  //Write text data
351  memcpy(entry->metadata + k + 1, metadata + j, n);
352 
353  //Jump to the next text data
354  j = i + 1;
355  //Advance write index
356  k += n + 1;
357 
358  //Update the length of the TXT record
359  entry->metadataLength += n + 1;
360 
361  //End of string detected?
362  if(metadata[i] == '\0')
363  break;
364  }
365 
366  //Advance read index
367  i++;
368  }
369 
370  //Empty TXT record?
371  if(!entry->metadataLength)
372  {
373  //An empty TXT record shall contain a single zero byte
374  entry->metadata[0] = 0;
375  entry->metadataLength = 1;
376  }
377 
378  //Restart probing process
379  dnsSdStartProbing(context);
380 
381  //Successful processing
382  error = NO_ERROR;
383  }
384  else
385  {
386  //The service list is full
387  error = ERROR_FAILURE;
388  }
389 
390  //Release exclusive access
392 
393  //Return error code
394  return error;
395 }
396 
397 
398 /**
399  * @brief Unregister a DNS-SD service
400  * @param[in] context Pointer to the DNS-SD context
401  * @param[in] serviceName NULL-terminated string that contains the name of the
402  * service to be unregistered
403  * @return Error code
404  **/
405 
406 error_t dnsSdUnregisterService(DnsSdContext *context, const char_t *serviceName)
407 {
408  uint_t i;
409  DnsSdService *entry;
410 
411  //Check parameters
412  if(context == NULL || serviceName == NULL)
414 
415  //Get exclusive access
417 
418  //Loop through the list of registered services
419  for(i = 0; i < DNS_SD_SERVICE_LIST_SIZE; i++)
420  {
421  //Point to the current entry
422  entry = &context->serviceList[i];
423 
424  //Service name found?
425  if(!strcasecmp(entry->name, serviceName))
426  {
427  //Send a goodbye packet
428  dnsSdSendGoodbye(context, entry);
429  //Remove the service from the list
430  entry->name[0] = '\0';
431  }
432  }
433 
434  //Release exclusive access
436 
437  //Successful processing
438  return NO_ERROR;
439 }
440 
441 
442 /**
443  * @brief Get the number of registered services
444  * @param[in] context Pointer to the DNS-SD context
445  * @return Number of registered services
446  **/
447 
449 {
450  uint_t i;
451  uint_t n;
452 
453  //Number of registered services
454  n = 0;
455 
456  //Check parameter
457  if(context != NULL)
458  {
459  //Valid instance name?
460  if(context->instanceName[0] != '\0')
461  {
462  //Loop through the list of registered services
463  for(i = 0; i < DNS_SD_SERVICE_LIST_SIZE; i++)
464  {
465  //Check if the entry is currently in use
466  if(context->serviceList[i].name[0] != '\0')
467  n++;
468  }
469  }
470  }
471 
472  //Return the number of registered services
473  return n;
474 }
475 
476 
477 /**
478  * @brief Restart probing process
479  * @param[in] context Pointer to the DNS-SD context
480  * @return Error code
481  **/
482 
484 {
485  //Check parameter
486  if(context == NULL)
488 
489  //Force DNS-SD to start probing again
490  context->state = MDNS_STATE_INIT;
491 
492  //Successful processing
493  return NO_ERROR;
494 }
495 
496 
497 /**
498  * @brief DNS-SD responder timer handler
499  *
500  * This routine must be periodically called by the TCP/IP stack to
501  * manage DNS-SD operation
502  *
503  * @param[in] context Pointer to the DNS-SD context
504  **/
505 
506 void dnsSdTick(DnsSdContext *context)
507 {
508  systime_t time;
509  systime_t delay;
510  NetInterface *interface;
511  MdnsState state;
512 
513  //Make sure DNS-SD has been properly instantiated
514  if(context == NULL)
515  return;
516 
517  //Point to the underlying network interface
518  interface = context->settings.interface;
519 
520  //Get current time
521  time = osGetSystemTime();
522 
523  //Check current state
524  if(context->state == MDNS_STATE_INIT)
525  {
526  //Check whether the mDNS responder has been properly instantiated
527  if(interface->mdnsResponderContext != NULL)
528  state = interface->mdnsResponderContext->state;
529  else
530  state = MDNS_STATE_INIT;
531 
532  //Wait for mDNS probing to complete
533  if(state == MDNS_STATE_ANNOUNCING || state == MDNS_STATE_IDLE)
534  {
535  //Any registered services?
536  if(dnsSdGetNumServices(context) > 0)
537  {
538  //Initial random delay
540  //Perform probing
541  dnsSdChangeState(context, MDNS_STATE_PROBING, delay);
542  }
543  }
544  }
545  else if(context->state == MDNS_STATE_PROBING)
546  {
547  //Probing failed?
548  if(context->conflict && context->retransmitCount > 0)
549  {
550  //Programmatically change the service instance name
551  dnsSdChangeInstanceName(context);
552  //Probe again, and repeat as necessary until a unique name is found
554  }
555  //Tie-break lost?
556  else if(context->tieBreakLost && context->retransmitCount > 0)
557  {
558  //The host defers to the winning host by waiting one second, and
559  //then begins probing for this record again
561  }
562  else
563  {
564  //Check current time
565  if(timeCompare(time, context->timestamp + context->timeout) >= 0)
566  {
567  //Probing is on-going?
568  if(context->retransmitCount < MDNS_PROBE_NUM)
569  {
570  //First probe?
571  if(context->retransmitCount == 0)
572  {
573  //Apparently conflicting mDNS responses received before the
574  //first probe packet is sent must be silently ignored
575  context->conflict = FALSE;
576  context->tieBreakLost = FALSE;
577  }
578 
579  //Send probe packet
580  dnsSdSendProbe(context);
581 
582  //Save the time at which the packet was sent
583  context->timestamp = time;
584  //Time interval between subsequent probe packets
585  context->timeout = MDNS_PROBE_DELAY;
586  //Increment retransmission counter
587  context->retransmitCount++;
588  }
589  //Probing is complete?
590  else
591  {
592  //The mDNS responder must send unsolicited mDNS responses
593  //containing all of its newly registered resource records
594  if(context->settings.numAnnouncements > 0)
596  else
597  dnsSdChangeState(context, MDNS_STATE_IDLE, 0);
598  }
599  }
600  }
601  }
602  else if(context->state == MDNS_STATE_ANNOUNCING)
603  {
604  //Whenever a mDNS responder receives any mDNS response (solicited or
605  //otherwise) containing a conflicting resource record, the conflict
606  //must be resolved
607  if(context->conflict)
608  {
609  //Probe again, and repeat as necessary until a unique name is found
611  }
612  else
613  {
614  //Check current time
615  if(timeCompare(time, context->timestamp + context->timeout) >= 0)
616  {
617  //Send announcement packet
618  dnsSdSendAnnouncement(context);
619 
620  //Save the time at which the packet was sent
621  context->timestamp = time;
622  //Increment retransmission counter
623  context->retransmitCount++;
624 
625  //First announcement packet?
626  if(context->retransmitCount == 1)
627  {
628  //The mDNS responder must send at least two unsolicited
629  //responses, one second apart
630  context->timeout = MDNS_ANNOUNCE_DELAY;
631  }
632  else
633  {
634  //To provide increased robustness against packet loss, a mDNS
635  //responder may send up to eight unsolicited responses, provided
636  //that the interval between unsolicited responses increases by
637  //at least a factor of two with every response sent
638  context->timeout *= 2;
639  }
640 
641  //Last announcement packet?
642  if(context->retransmitCount >= context->settings.numAnnouncements)
643  {
644  //A mDNS responder must not send regular periodic announcements
645  dnsSdChangeState(context, MDNS_STATE_IDLE, 0);
646  }
647  }
648  }
649  }
650  else if(context->state == MDNS_STATE_IDLE)
651  {
652  //Whenever a mDNS responder receives any mDNS response (solicited or
653  //otherwise) containing a conflicting resource record, the conflict
654  //must be resolved
655  if(context->conflict)
656  {
657  //Probe again, and repeat as necessary until a unique name is found
659  }
660  }
661 }
662 
663 
664 /**
665  * @brief Callback function for link change event
666  * @param[in] context Pointer to the DNS-SD context
667  **/
668 
670 {
671  //Make sure DNS-SD has been properly instantiated
672  if(context == NULL)
673  return;
674 
675  //Whenever a mDNS responder receives an indication of a link
676  //change event, it must perform probing and announcing
677  dnsSdChangeState(context, MDNS_STATE_INIT, 0);
678 }
679 
680 
681 /**
682  * @brief Update FSM state
683  * @param[in] context Pointer to the DNS-SD context
684  * @param[in] newState New state to switch to
685  * @param[in] delay Initial delay
686  **/
687 
689  MdnsState newState, systime_t delay)
690 {
691  NetInterface *interface;
692 
693  //Point to the underlying network interface
694  interface = context->settings.interface;
695 
696  //Set time stamp
697  context->timestamp = osGetSystemTime();
698  //Set initial delay
699  context->timeout = delay;
700  //Reset retransmission counter
701  context->retransmitCount = 0;
702  //Switch to the new state
703  context->state = newState;
704 
705  //Any registered callback?
706  if(context->settings.stateChangeEvent != NULL)
707  {
708  //Release exclusive access
710  //Invoke user callback function
711  context->settings.stateChangeEvent(context, interface, newState);
712  //Get exclusive access
714  }
715 }
716 
717 
718 /**
719  * @brief Programmatically change the service instance name
720  * @param[in] context Pointer to the DNS-SD context
721  **/
722 
724 {
725  size_t i;
726  size_t m;
727  size_t n;
728  uint32_t index;
729  char_t s[16];
730 
731  //Retrieve the length of the string
732  n = strlen(context->instanceName);
733 
734  //Parse the string backwards
735  for(i = n; i > 0; i--)
736  {
737  //Last character?
738  if(i == n)
739  {
740  //Check whether the last character is a bracket
741  if(context->instanceName[i - 1] != ')')
742  break;
743  }
744  else
745  {
746  //Check whether the current character is a digit
747  if(!isdigit((uint8_t) context->instanceName[i - 1]))
748  break;
749  }
750  }
751 
752  //Any number following the service instance name?
753  if(context->instanceName[i] != '\0')
754  {
755  //Retrieve the number at the end of the name
756  index = atoi(context->instanceName + i);
757  //Increment the value
758  index++;
759 
760  //Check the length of the name
761  if(i >= 2)
762  {
763  //Discard any space and bracket that may precede the number
764  if(context->instanceName[i - 2] == ' ' &&
765  context->instanceName[i - 1] == '(')
766  {
767  i -= 2;
768  }
769  }
770 
771  //Strip the digits
772  context->instanceName[i] = '\0';
773  }
774  else
775  {
776  //Append the digit "2" to the name
777  index = 2;
778  }
779 
780  //Convert the number to a string of characters
781  m = sprintf(s, " (%" PRIu32 ")", index);
782 
783  //Sanity check
784  if((i + m) <= DNS_SD_MAX_INSTANCE_NAME_LEN)
785  {
786  //Programmatically change the service instance name
787  strcat(context->instanceName, s);
788  }
789 }
790 
791 
792 /**
793  * @brief Send probe packet
794  * @param[in] context Pointer to the DNS-SD context
795  * @return Error code
796  **/
797 
799 {
800  error_t error;
801  uint_t i;
802  NetInterface *interface;
803  DnsQuestion *dnsQuestion;
804  DnsSdService *service;
806 
807  //Point to the underlying network interface
808  interface = context->settings.interface;
809 
810  //Create an empty mDNS query message
811  error = mdnsCreateMessage(&message, FALSE);
812  //Any error to report?
813  if(error)
814  return error;
815 
816  //Start of exception handling block
817  do
818  {
819  //For all those resource records that a mDNS responder desires to be
820  //unique on the local link, it must send a mDNS query asking for those
821  //resource records, to see if any of them are already in use
822  if(dnsSdGetNumServices(context) > 0)
823  {
824  //Loop through the list of registered services
825  for(i = 0; i < DNS_SD_SERVICE_LIST_SIZE; i++)
826  {
827  //Point to the current entry
828  service = &context->serviceList[i];
829 
830  //Valid service?
831  if(service->name[0] != '\0')
832  {
833  //Encode the service name using DNS notation
834  message.length += mdnsEncodeName(context->instanceName, service->name,
835  ".local", (uint8_t *) message.dnsHeader + message.length);
836 
837  //Point to the corresponding question structure
838  dnsQuestion = DNS_GET_QUESTION(message.dnsHeader, message.length);
839 
840  //The probes should be sent as QU questions with the unicast-response
841  //bit set, to allow a defending host to respond immediately via unicast
842  dnsQuestion->qtype = HTONS(DNS_RR_TYPE_ANY);
843  dnsQuestion->qclass = HTONS(MDNS_QCLASS_QU | DNS_RR_CLASS_IN);
844 
845  //Update the length of the mDNS query message
846  message.length += sizeof(DnsQuestion);
847 
848  //Number of questions in the Question Section
849  message.dnsHeader->qdcount++;
850  }
851  }
852  }
853 
854  //A probe query can be distinguished from a normal query by the fact that
855  //a probe query contains a proposed record in the Authority Section that
856  //answers the question in the Question Section
857  if(dnsSdGetNumServices(context) > 0)
858  {
859  //Loop through the list of registered services
860  for(i = 0; i < DNS_SD_SERVICE_LIST_SIZE; i++)
861  {
862  //Point to the current entry
863  service = &context->serviceList[i];
864 
865  //Valid service?
866  if(service->name[0] != '\0')
867  {
868  //Format SRV resource record
869  error = dnsSdAddSrvRecord(interface, &message,
870  service, FALSE, DNS_SD_DEFAULT_RR_TTL);
871  //Any error to report?
872  if(error)
873  break;
874 
875  //Format TXT resource record
876  error = dnsSdAddTxtRecord(interface, &message,
877  service, FALSE, DNS_SD_DEFAULT_RR_TTL);
878  //Any error to report?
879  if(error)
880  break;
881  }
882  }
883  }
884 
885  //Propagate exception if necessary
886  if(error)
887  break;
888 
889  //Number of resource records in the Authority Section
890  message.dnsHeader->nscount = message.dnsHeader->ancount;
891  //Number of resource records in the Answer Section
892  message.dnsHeader->ancount = 0;
893 
894  //Send mDNS message
895  error = mdnsSendMessage(interface, &message, NULL, MDNS_PORT);
896 
897  //End of exception handling block
898  } while(0);
899 
900  //Free previously allocated memory
902 
903  //Return status code
904  return error;
905 }
906 
907 
908 /**
909  * @brief Send announcement packet
910  * @param[in] context Pointer to the DNS-SD context
911  * @return Error code
912  **/
913 
915 {
916  error_t error;
917  uint_t i;
918  NetInterface *interface;
919  DnsSdService *service;
921 
922  //Point to the underlying network interface
923  interface = context->settings.interface;
924 
925  //Create an empty mDNS response message
926  error = mdnsCreateMessage(&message, TRUE);
927  //Any error to report?
928  if(error)
929  return error;
930 
931  //Start of exception handling block
932  do
933  {
934  //Send an unsolicited mDNS response containing, in the Answer Section,
935  //all of its newly registered resource records
936  if(dnsSdGetNumServices(context) > 0)
937  {
938  //Loop through the list of registered services
939  for(i = 0; i < DNS_SD_SERVICE_LIST_SIZE; i++)
940  {
941  //Point to the current entry
942  service = &context->serviceList[i];
943 
944  //Valid service?
945  if(service->name[0] != '\0')
946  {
947  //Format PTR resource record (service type enumeration)
948  error = dnsSdAddServiceEnumPtrRecord(interface,
949  &message, service, DNS_SD_DEFAULT_RR_TTL);
950  //Any error to report?
951  if(error)
952  break;
953 
954  //Format PTR resource record
955  error = dnsSdAddPtrRecord(interface, &message,
956  service, DNS_SD_DEFAULT_RR_TTL);
957  //Any error to report?
958  if(error)
959  break;
960 
961  //Format SRV resource record
962  error = dnsSdAddSrvRecord(interface, &message,
963  service, TRUE, DNS_SD_DEFAULT_RR_TTL);
964  //Any error to report?
965  if(error)
966  break;
967 
968  //Format TXT resource record
969  error = dnsSdAddTxtRecord(interface, &message,
970  service, TRUE, DNS_SD_DEFAULT_RR_TTL);
971  //Any error to report?
972  if(error)
973  break;
974  }
975  }
976  }
977 
978  //Propagate exception if necessary
979  if(error)
980  break;
981 
982  //Send mDNS message
983  error = mdnsSendMessage(interface, &message, NULL, MDNS_PORT);
984 
985  //End of exception handling block
986  } while(0);
987 
988  //Free previously allocated memory
990 
991  //Return status code
992  return error;
993 }
994 
995 
996 /**
997  * @brief Send goodbye packet
998  * @param[in] context Pointer to the DNS-SD context
999  * @param[in] service Pointer to a DNS-SD service
1000  * @return Error code
1001  **/
1002 
1004 {
1005  error_t error;
1006  uint_t i;
1007  NetInterface *interface;
1008  DnsSdService *entry;
1010 
1011  //Point to the underlying network interface
1012  interface = context->settings.interface;
1013 
1014  //Create an empty mDNS response message
1015  error = mdnsCreateMessage(&message, TRUE);
1016  //Any error to report?
1017  if(error)
1018  return error;
1019 
1020  //Loop through the list of registered services
1021  for(i = 0; i < DNS_SD_SERVICE_LIST_SIZE; i++)
1022  {
1023  //Point to the current entry
1024  entry = &context->serviceList[i];
1025 
1026  //Valid service?
1027  if(entry->name[0] != '\0')
1028  {
1029  if(service == entry || service == NULL)
1030  {
1031  //Format PTR resource record (service type enumeration)
1032  error = dnsSdAddServiceEnumPtrRecord(interface, &message, entry, 0);
1033  //Any error to report?
1034  if(error)
1035  break;
1036 
1037  //Format PTR resource record
1038  error = dnsSdAddPtrRecord(interface, &message, entry, 0);
1039  //Any error to report?
1040  if(error)
1041  break;
1042 
1043  //Format SRV resource record
1044  error = dnsSdAddSrvRecord(interface, &message, entry, TRUE, 0);
1045  //Any error to report?
1046  if(error)
1047  break;
1048 
1049  //Format TXT resource record
1050  error = dnsSdAddTxtRecord(interface, &message, entry, TRUE, 0);
1051  //Any error to report?
1052  if(error)
1053  break;
1054  }
1055  }
1056  }
1057 
1058  //Check status code
1059  if(!error)
1060  {
1061  //Send mDNS message
1062  error = mdnsSendMessage(interface, &message, NULL, MDNS_PORT);
1063  }
1064 
1065  //Free previously allocated memory
1067 
1068  //Return status code
1069  return error;
1070 }
1071 
1072 
1073 /**
1074  * @brief Parse a question
1075  * @param[in] interface Underlying network interface
1076  * @param[in] query Incoming mDNS query message
1077  * @param[in] offset Offset to first byte of the question
1078  * @param[in] question Pointer to the question
1079  * @param[in,out] response mDNS response message
1080  * @return Error code
1081  **/
1082 
1084  size_t offset, const DnsQuestion *question, MdnsMessage *response)
1085 {
1086  error_t error;
1087  uint_t i;
1088  uint16_t qclass;
1089  uint16_t qtype;
1090  uint32_t ttl;
1091  bool_t cacheFlush;
1092  DnsSdContext *context;
1093  DnsSdService *service;
1094 
1095  //Point to the DNS-SD context
1096  context = interface->dnsSdContext;
1097  //Make sure DNS-SD has been properly instantiated
1098  if(context == NULL)
1099  return NO_ERROR;
1100 
1101  //Check the state of the mDNS responder
1102  if(context->state != MDNS_STATE_ANNOUNCING &&
1103  context->state != MDNS_STATE_IDLE)
1104  {
1105  //Do not respond to mDNS queries during probing
1106  return NO_ERROR;
1107  }
1108 
1109  //Convert the query class to host byte order
1110  qclass = ntohs(question->qclass);
1111  //Discard QU flag
1112  qclass &= ~MDNS_QCLASS_QU;
1113 
1114  //Convert the query type to host byte order
1115  qtype = ntohs(question->qtype);
1116 
1117  //Get the TTL resource record
1118  ttl = context->settings.ttl;
1119 
1120  //Check whether the querier originating the query is a simple resolver
1121  if(ntohs(query->udpHeader->srcPort) != MDNS_PORT)
1122  {
1123  //The resource record TTL given in a legacy unicast response should
1124  //not be greater than ten seconds, even if the true TTL of the mDNS
1125  //resource record is higher
1127 
1128  //The cache-flush bit must not be set in legacy unicast responses
1129  cacheFlush = FALSE;
1130  }
1131  else
1132  {
1133  //The cache-bit should be set for unique resource records
1134  cacheFlush = TRUE;
1135  }
1136 
1137  //Any registered services?
1138  if(dnsSdGetNumServices(context) > 0)
1139  {
1140  //Loop through the list of registered services
1141  for(i = 0; i < DNS_SD_SERVICE_LIST_SIZE; i++)
1142  {
1143  //Point to the current entry
1144  service = &context->serviceList[i];
1145 
1146  //Valid service?
1147  if(service->name[0] != '\0')
1148  {
1149  //Check the class of the query
1151  {
1152  //Compare service name
1153  if(!mdnsCompareName(query->dnsHeader, query->length,
1154  offset, "", "_services._dns-sd._udp", ".local", 0))
1155  {
1156  //PTR query?
1158  {
1159  //Format PTR resource record (service type enumeration)
1160  error = dnsSdAddServiceEnumPtrRecord(interface,
1161  response, service, ttl);
1162  //Any error to report?
1163  if(error)
1164  return error;
1165 
1166  //Update the number of shared resource records
1167  response->sharedRecordCount++;
1168  }
1169  }
1170  else if(!mdnsCompareName(query->dnsHeader, query->length,
1171  offset, "", service->name, ".local", 0))
1172  {
1173  //PTR query?
1175  {
1176  //Format PTR resource record
1177  error = dnsSdAddPtrRecord(interface, response,
1178  service, ttl);
1179  //Any error to report?
1180  if(error)
1181  return error;
1182 
1183  //Update the number of shared resource records
1184  response->sharedRecordCount++;
1185  }
1186  }
1187  else if(!mdnsCompareName(query->dnsHeader, query->length, offset,
1188  context->instanceName, service->name, ".local", 0))
1189  {
1190  //SRV query?
1192  {
1193  //Format SRV resource record
1194  error = dnsSdAddSrvRecord(interface, response,
1195  service, cacheFlush, ttl);
1196  //Any error to report?
1197  if(error)
1198  return error;
1199  }
1200 
1201  //TXT query?
1203  {
1204  //Format TXT resource record
1205  error = dnsSdAddTxtRecord(interface, response,
1206  service, cacheFlush, ttl);
1207  //Any error to report?
1208  if(error)
1209  return error;
1210  }
1211 
1212  //NSEC query?
1214  {
1215  //Format NSEC resource record
1216  error = dnsSdAddNsecRecord(interface, response,
1217  service, cacheFlush, ttl);
1218  //Any error to report?
1219  if(error)
1220  return error;
1221  }
1222  }
1223  }
1224  }
1225  }
1226  }
1227 
1228  //Successful processing
1229  return NO_ERROR;
1230 }
1231 
1232 
1233 
1234 /**
1235  * @brief Parse a resource record from the Authority Section
1236  * @param[in] interface Underlying network interface
1237  * @param[in] query Incoming mDNS query message
1238  * @param[in] offset Offset to first byte of the resource record
1239  * @param[in] record Pointer to the resource record
1240  **/
1241 
1242 void dnsSdParseNsRecord(NetInterface *interface, const MdnsMessage *query,
1243  size_t offset, const DnsResourceRecord *record)
1244 {
1245  uint_t i;
1246  uint16_t rclass;
1247  DnsSdContext *context;
1248  DnsSdService *service;
1249  DnsSrvResourceRecord *srvRecord;
1250 
1251  //Point to the DNS-SD context
1252  context = interface->dnsSdContext;
1253  //Make sure DNS-SD has been properly instantiated
1254  if(context == NULL)
1255  return;
1256 
1257  //Any services registered?
1258  if(dnsSdGetNumServices(context) > 0)
1259  {
1260  //Loop through the list of registered services
1261  for(i = 0; i < DNS_SD_SERVICE_LIST_SIZE; i++)
1262  {
1263  //Point to the current entry
1264  service = &context->serviceList[i];
1265 
1266  //Valid service?
1267  if(service->name[0] != '\0')
1268  {
1269  //Apply tie-breaking rules
1270  if(!mdnsCompareName(query->dnsHeader, query->length, offset,
1271  context->instanceName, service->name, ".local", 0))
1272  {
1273  //Convert the class to host byte order
1274  rclass = ntohs(record->rclass);
1275  //Discard Cache Flush flag
1277 
1278  //Check the class of the resource record
1279  if(rclass == DNS_RR_CLASS_IN)
1280  {
1281  //SRV resource record found?
1282  if(ntohs(record->rtype) == DNS_RR_TYPE_SRV)
1283  {
1284  //Cast resource record
1285  srvRecord = (DnsSrvResourceRecord *) record;
1286 
1287  //Compare Priority fields
1288  if(ntohs(srvRecord->priority) > service->priority)
1289  {
1290  context->tieBreakLost = TRUE;
1291  }
1292  else if(ntohs(srvRecord->priority) == service->priority)
1293  {
1294  //Compare Weight fields
1295  if(ntohs(srvRecord->weight) > service->weight)
1296  {
1297  context->tieBreakLost = TRUE;
1298  }
1299  else if(ntohs(srvRecord->weight) == service->weight)
1300  {
1301  //Compare Port fields
1302  if(ntohs(srvRecord->port) > service->port)
1303  {
1304  context->tieBreakLost = TRUE;
1305  }
1306  else if(ntohs(srvRecord->port) == service->port)
1307  {
1308  //Compute the offset of the first byte of the target
1309  offset = srvRecord->target - (uint8_t *) query->dnsHeader;
1310 
1311  if(mdnsCompareName(query->dnsHeader, query->length, offset,
1312  context->instanceName, "", ".local", 0) > 0)
1313  {
1314  //The host has lost the tie-break
1315  context->tieBreakLost = TRUE;
1316  }
1317  }
1318  }
1319  }
1320  }
1321  }
1322  }
1323  }
1324  }
1325  }
1326 }
1327 
1328 
1329 /**
1330  * @brief Parse a resource record from the Answer Section
1331  * @param[in] interface Underlying network interface
1332  * @param[in] response Incoming mDNS response message
1333  * @param[in] offset Offset to first byte of the resource record to be checked
1334  * @param[in] record Pointer to the resource record
1335  **/
1336 
1337 void dnsSdParseAnRecord(NetInterface *interface, const MdnsMessage *response,
1338  size_t offset, const DnsResourceRecord *record)
1339 {
1340  uint_t i;
1341  uint16_t rclass;
1342  DnsSdContext *context;
1343  DnsSdService *service;
1344 
1345  //Point to the DNS-SD context
1346  context = interface->dnsSdContext;
1347  //Make sure DNS-SD has been properly instantiated
1348  if(context == NULL)
1349  return;
1350 
1351  //Any services registered?
1352  if(dnsSdGetNumServices(context) > 0)
1353  {
1354  //Loop through the list of registered services
1355  for(i = 0; i < DNS_SD_SERVICE_LIST_SIZE; i++)
1356  {
1357  //Point to the current entry
1358  service = &context->serviceList[i];
1359 
1360  //Valid service?
1361  if(service->name[0] != '\0')
1362  {
1363  //Check for conflicts
1364  if(!mdnsCompareName(response->dnsHeader, response->length, offset,
1365  context->instanceName, service->name, ".local", 0))
1366  {
1367  //Convert the class to host byte order
1368  rclass = ntohs(record->rclass);
1369  //Discard Cache Flush flag
1371 
1372  //Check the class of the resource record
1373  if(rclass == DNS_RR_CLASS_IN)
1374  {
1375  //SRV resource record found?
1376  if(ntohs(record->rtype) == DNS_RR_TYPE_SRV)
1377  {
1378  //Compute the offset of the first byte of the rdata
1379  offset = record->rdata - (uint8_t *) response->dnsHeader;
1380 
1381  //A conflict occurs when a mDNS responder has a unique record for
1382  //which it is currently authoritative, and it receives a mDNS
1383  //response message containing a record with the same name, rrtype
1384  //and rrclass, but inconsistent rdata
1385  if(mdnsCompareName(response->dnsHeader, response->length, offset,
1386  context->instanceName, "", ".local", 0))
1387  {
1388  //The service instance name is already in use by some other host
1389  context->conflict = TRUE;
1390  }
1391  }
1392  }
1393  }
1394  }
1395  }
1396  }
1397 }
1398 
1399 
1400 /**
1401  * @brief Additional record generation
1402  * @param[in] interface Underlying network interface
1403  * @param[in,out] response mDNS response message
1404  * @param[in] legacyUnicast This flag is set for legacy unicast responses
1405  **/
1406 
1408  MdnsMessage *response, bool_t legacyUnicast)
1409 {
1410  error_t error;
1411  uint_t i;
1412  uint_t j;
1413  size_t n;
1414  size_t offset;
1415  uint_t ancount;
1416  uint16_t rclass;
1417  uint32_t ttl;
1418  bool_t cacheFlush;
1419  DnsSdContext *context;
1420  DnsSdService *service;
1421  DnsResourceRecord *record;
1422 
1423  //Point to the DNS-SD context
1424  context = interface->dnsSdContext;
1425  //Make sure DNS-SD has been properly instantiated
1426  if(context == NULL)
1427  return;
1428 
1429  //No registered services?
1430  if(dnsSdGetNumServices(context) == 0)
1431  return;
1432 
1433  //mDNS responses must not contain any questions in the Question Section
1434  if(response->dnsHeader->qdcount != 0)
1435  return;
1436 
1437  //Get the TTL resource record
1438  ttl = context->settings.ttl;
1439 
1440  //Check whether the querier originating the query is a simple resolver
1441  if(legacyUnicast)
1442  {
1443  //The resource record TTL given in a legacy unicast response should
1444  //not be greater than ten seconds, even if the true TTL of the mDNS
1445  //resource record is higher
1447 
1448  //The cache-flush bit must not be set in legacy unicast responses
1449  cacheFlush = FALSE;
1450  }
1451  else
1452  {
1453  //The cache-bit should be set for unique resource records
1454  cacheFlush = TRUE;
1455  }
1456 
1457  //Point to the first resource record
1458  offset = sizeof(DnsHeader);
1459 
1460  //Save the number of resource records in the Answer Section
1461  ancount = response->dnsHeader->ancount;
1462 
1463  //Parse the Answer Section
1464  for(i = 0; i < ancount; i++)
1465  {
1466  //Parse resource record name
1467  n = dnsParseName(response->dnsHeader, response->length, offset, NULL, 0);
1468  //Invalid name?
1469  if(!n)
1470  break;
1471 
1472  //Point to the associated resource record
1473  record = DNS_GET_RESOURCE_RECORD(response->dnsHeader, n);
1474  //Point to the resource data
1475  n += sizeof(DnsResourceRecord);
1476 
1477  //Make sure the resource record is valid
1478  if(n > response->length)
1479  break;
1480  if((n + ntohs(record->rdlength)) > response->length)
1481  break;
1482 
1483  //Convert the record class to host byte order
1484  rclass = ntohs(record->rclass);
1485  //Discard the cache-flush bit
1487 
1488  //Loop through the list of registered services
1489  for(j = 0; j < DNS_SD_SERVICE_LIST_SIZE; j++)
1490  {
1491  //Point to the current entry
1492  service = &context->serviceList[j];
1493 
1494  //Valid service?
1495  if(service->name[0] != '\0')
1496  {
1497  //Check the class of the resource record
1498  if(rclass == DNS_RR_CLASS_IN)
1499  {
1500  //PTR record?
1501  if(ntohs(record->rtype) == DNS_RR_TYPE_PTR)
1502  {
1503  //Compare service name
1504  if(!mdnsCompareName(response->dnsHeader, response->length,
1505  offset, "", service->name, ".local", 0))
1506  {
1507  //Format SRV resource record
1508  error = dnsSdAddSrvRecord(interface,
1509  response, service, cacheFlush, ttl);
1510  //Any error to report?
1511  if(error)
1512  return;
1513 
1514  //Format TXT resource record
1515  error = dnsSdAddTxtRecord(interface,
1516  response, service, cacheFlush, ttl);
1517  //Any error to report?
1518  if(error)
1519  return;
1520  }
1521  }
1522  //SRV record?
1523  else if(ntohs(record->rtype) == DNS_RR_TYPE_SRV)
1524  {
1525  //Compare service name
1526  if(!mdnsCompareName(response->dnsHeader, response->length,
1527  offset, context->instanceName, service->name, ".local", 0))
1528  {
1529  //Format TXT resource record
1530  error = dnsSdAddTxtRecord(interface,
1531  response, service, cacheFlush, ttl);
1532  //Any error to report?
1533  if(error)
1534  return;
1535  }
1536  }
1537  }
1538  }
1539  }
1540 
1541  //Point to the next resource record
1542  offset = n + ntohs(record->rdlength);
1543  }
1544 
1545  //Number of resource records in the Additional Section
1546  response->dnsHeader->arcount += response->dnsHeader->ancount - ancount;
1547  //Number of resource records in the Answer Section
1548  response->dnsHeader->ancount = ancount;
1549 }
1550 
1551 
1552 /**
1553  * @brief Add PTR record to a mDNS message (in response to a meta-query)
1554  * @param[in] interface Underlying network interface
1555  * @param[in,out] message Pointer to the mDNS message
1556  * @param[in] service Pointer to a DNS-SD service
1557  * @param[in] ttl Resource record TTL (cache lifetime)
1558  * @return Error code
1559  **/
1560 
1562  MdnsMessage *message, const DnsSdService *service, uint32_t ttl)
1563 {
1564  size_t n;
1565  size_t offset;
1566  DnsResourceRecord *record;
1567 
1568  //Set the position to the end of the buffer
1569  offset = message->length;
1570 
1571  //The first pass calculates the length of the DNS encoded service name
1572  n = mdnsEncodeName("", "_services._dns-sd._udp", ".local", NULL);
1573 
1574  //Check the length of the resulting mDNS message
1575  if((offset + n) > MDNS_MESSAGE_MAX_SIZE)
1576  return ERROR_MESSAGE_TOO_LONG;
1577 
1578  //The second pass encodes the service name using the DNS name notation
1579  offset += mdnsEncodeName("", "_services._dns-sd._udp",
1580  ".local", (uint8_t *) message->dnsHeader + offset);
1581 
1582  //Consider the length of the resource record itself
1583  if((offset + sizeof(DnsResourceRecord)) > MDNS_MESSAGE_MAX_SIZE)
1584  return ERROR_MESSAGE_TOO_LONG;
1585 
1586  //Point to the corresponding resource record
1587  record = DNS_GET_RESOURCE_RECORD(message->dnsHeader, offset);
1588 
1589  //Fill in resource record
1590  record->rtype = HTONS(DNS_RR_TYPE_PTR);
1591  record->rclass = HTONS(DNS_RR_CLASS_IN);
1592  record->ttl = htonl(ttl);
1593 
1594  //Advance write index
1595  offset += sizeof(DnsResourceRecord);
1596 
1597  //The first pass calculates the length of the DNS encoded service name
1598  n = mdnsEncodeName("", service->name, ".local", NULL);
1599 
1600  //Check the length of the resulting mDNS message
1601  if((offset + n) > MDNS_MESSAGE_MAX_SIZE)
1602  return ERROR_MESSAGE_TOO_LONG;
1603 
1604  //The second pass encodes the service name using DNS notation
1605  n = mdnsEncodeName("", service->name,
1606  ".local", record->rdata);
1607 
1608  //Convert length field to network byte order
1609  record->rdlength = htons(n);
1610 
1611  //Number of resource records in the answer section
1612  message->dnsHeader->ancount++;
1613  //Update the length of the DNS message
1614  message->length = offset + n;
1615 
1616  //Successful processing
1617  return NO_ERROR;
1618 }
1619 
1620 
1621 /**
1622  * @brief Add PTR record to a mDNS message
1623  * @param[in] interface Underlying network interface
1624  * @param[in,out] message Pointer to the mDNS message
1625  * @param[in] service Pointer to a DNS-SD service
1626  * @param[in] ttl Resource record TTL (cache lifetime)
1627  * @return Error code
1628  **/
1629 
1631  MdnsMessage *message, const DnsSdService *service, uint32_t ttl)
1632 {
1633  size_t n;
1634  size_t offset;
1635  bool_t duplicate;
1636  DnsSdContext *context;
1637  DnsResourceRecord *record;
1638 
1639  //Point to the DNS-SD context
1640  context = interface->dnsSdContext;
1641 
1642  //Check whether the resource record is already present in the Answer
1643  //Section of the message
1644  duplicate = mdnsCheckDuplicateRecord(message, "",
1645  service->name, ".local", DNS_RR_TYPE_PTR);
1646 
1647  //The duplicates should be suppressed and the resource record should
1648  //appear only once in the list
1649  if(!duplicate)
1650  {
1651  //Set the position to the end of the buffer
1652  offset = message->length;
1653 
1654  //The first pass calculates the length of the DNS encoded service name
1655  n = mdnsEncodeName("", service->name, ".local", NULL);
1656 
1657  //Check the length of the resulting mDNS message
1658  if((offset + n) > MDNS_MESSAGE_MAX_SIZE)
1659  return ERROR_MESSAGE_TOO_LONG;
1660 
1661  //Encode the service name using the DNS name notation
1662  offset += mdnsEncodeName("", service->name,
1663  ".local", (uint8_t *) message->dnsHeader + offset);
1664 
1665  //Consider the length of the resource record itself
1666  if((offset + sizeof(DnsResourceRecord)) > MDNS_MESSAGE_MAX_SIZE)
1667  return ERROR_MESSAGE_TOO_LONG;
1668 
1669  //Point to the corresponding resource record
1670  record = DNS_GET_RESOURCE_RECORD(message->dnsHeader, offset);
1671 
1672  //Fill in resource record
1673  record->rtype = HTONS(DNS_RR_TYPE_PTR);
1674  record->rclass = HTONS(DNS_RR_CLASS_IN);
1675  record->ttl = htonl(ttl);
1676 
1677  //Advance write index
1678  offset += sizeof(DnsResourceRecord);
1679 
1680  //The first pass calculates the length of the DNS encoded instance name
1681  n = mdnsEncodeName(context->instanceName, service->name, ".local", NULL);
1682 
1683  //Check the length of the resulting mDNS message
1684  if((offset + n) > MDNS_MESSAGE_MAX_SIZE)
1685  return ERROR_MESSAGE_TOO_LONG;
1686 
1687  //The second pass encodes the instance name using DNS notation
1688  n = mdnsEncodeName(context->instanceName,
1689  service->name, ".local", record->rdata);
1690 
1691  //Convert length field to network byte order
1692  record->rdlength = htons(n);
1693 
1694  //Number of resource records in the answer section
1695  message->dnsHeader->ancount++;
1696  //Update the length of the DNS message
1697  message->length = offset + n;
1698  }
1699 
1700  //Successful processing
1701  return NO_ERROR;
1702 }
1703 
1704 
1705 /**
1706  * @brief Add SRV record to a mDNS message
1707  * @param[in] interface Underlying network interface
1708  * @param[in,out] message Pointer to the mDNS message
1709  * @param[in] service Pointer to a DNS-SD service
1710  * @param[in] cacheFlush Cache-flush bit
1711  * @param[in] ttl Resource record TTL (cache lifetime)
1712  * @return Error code
1713  **/
1714 
1716  const DnsSdService *service, bool_t cacheFlush, uint32_t ttl)
1717 {
1718  size_t n;
1719  size_t offset;
1720  bool_t duplicate;
1721  MdnsResponderContext *mdnsResponderContext;
1722  DnsSdContext *dnsSdContext;
1723  DnsSrvResourceRecord *record;
1724 
1725  //Point to the mDNS responder context
1726  mdnsResponderContext = interface->mdnsResponderContext;
1727  //Point to the DNS-SD context
1728  dnsSdContext = interface->dnsSdContext;
1729 
1730  //Check whether the resource record is already present in the Answer
1731  //Section of the message
1732  duplicate = mdnsCheckDuplicateRecord(message, dnsSdContext->instanceName,
1733  service->name, ".local", DNS_RR_TYPE_SRV);
1734 
1735  //The duplicates should be suppressed and the resource record should
1736  //appear only once in the list
1737  if(!duplicate)
1738  {
1739  //Set the position to the end of the buffer
1740  offset = message->length;
1741 
1742  //The first pass calculates the length of the DNS encoded instance name
1743  n = mdnsEncodeName(dnsSdContext->instanceName,
1744  service->name, ".local", NULL);
1745 
1746  //Check the length of the resulting mDNS message
1747  if((offset + n) > MDNS_MESSAGE_MAX_SIZE)
1748  return ERROR_MESSAGE_TOO_LONG;
1749 
1750  //The second pass encodes the instance name using DNS notation
1751  offset += mdnsEncodeName(dnsSdContext->instanceName,
1752  service->name, ".local", (uint8_t *) message->dnsHeader + offset);
1753 
1754  //Consider the length of the resource record itself
1755  if((offset + sizeof(DnsSrvResourceRecord)) > MDNS_MESSAGE_MAX_SIZE)
1756  return ERROR_MESSAGE_TOO_LONG;
1757 
1758  //Point to the corresponding resource record
1759  record = (DnsSrvResourceRecord *) DNS_GET_RESOURCE_RECORD(message->dnsHeader, offset);
1760 
1761  //Fill in resource record
1762  record->rtype = HTONS(DNS_RR_TYPE_SRV);
1763  record->rclass = HTONS(DNS_RR_CLASS_IN);
1764  record->ttl = htonl(ttl);
1765  record->priority = htons(service->priority);
1766  record->weight = htons(service->weight);
1767  record->port = htons(service->port);
1768 
1769  //Check whether the cache-flush bit should be set
1770  if(cacheFlush)
1771  record->rclass |= HTONS(MDNS_RCLASS_CACHE_FLUSH);
1772 
1773  //Advance write index
1774  offset += sizeof(DnsSrvResourceRecord);
1775 
1776  //The first pass calculates the length of the DNS encoded target name
1777  n = mdnsEncodeName("", mdnsResponderContext->hostname,
1778  ".local", NULL);
1779 
1780  //Check the length of the resulting mDNS message
1781  if((offset + n) > MDNS_MESSAGE_MAX_SIZE)
1782  return ERROR_MESSAGE_TOO_LONG;
1783 
1784  //The second pass encodes the target name using DNS notation
1785  n = mdnsEncodeName("", mdnsResponderContext->hostname,
1786  ".local", record->target);
1787 
1788  //Calculate data length
1789  record->rdlength = htons(sizeof(DnsSrvResourceRecord) -
1790  sizeof(DnsResourceRecord) + n);
1791 
1792  //Number of resource records in the answer section
1793  message->dnsHeader->ancount++;
1794  //Update the length of the DNS message
1795  message->length = offset + n;
1796  }
1797 
1798  //Successful processing
1799  return NO_ERROR;
1800 }
1801 
1802 
1803 /**
1804  * @brief Add TXT record to a mDNS message
1805  * @param[in] interface Underlying network interface
1806  * @param[in,out] message Pointer to the mDNS message
1807  * @param[in] service Pointer to a DNS-SD service
1808  * @param[in] cacheFlush Cache-flush bit
1809  * @param[in] ttl Resource record TTL (cache lifetime)
1810  * @return Error code
1811  **/
1812 
1814  const DnsSdService *service, bool_t cacheFlush, uint32_t ttl)
1815 {
1816  size_t n;
1817  size_t offset;
1818  bool_t duplicate;
1819  DnsSdContext *context;
1820  DnsResourceRecord *record;
1821 
1822  //Point to the DNS-SD context
1823  context = interface->dnsSdContext;
1824 
1825  //Check whether the resource record is already present in the Answer
1826  //Section of the message
1827  duplicate = mdnsCheckDuplicateRecord(message, context->instanceName,
1828  service->name, ".local", DNS_RR_TYPE_TXT);
1829 
1830  //The duplicates should be suppressed and the resource record should
1831  //appear only once in the list
1832  if(!duplicate)
1833  {
1834  //Set the position to the end of the buffer
1835  offset = message->length;
1836 
1837  //The first pass calculates the length of the DNS encoded instance name
1838  n = mdnsEncodeName(context->instanceName, service->name, ".local", NULL);
1839 
1840  //Check the length of the resulting mDNS message
1841  if((offset + n) > MDNS_MESSAGE_MAX_SIZE)
1842  return ERROR_MESSAGE_TOO_LONG;
1843 
1844  //The second pass encodes the instance name using DNS notation
1845  offset += mdnsEncodeName(context->instanceName,
1846  service->name, ".local", (uint8_t *) message->dnsHeader + offset);
1847 
1848  //Consider the length of the resource record itself
1849  if((offset + sizeof(DnsResourceRecord)) > MDNS_MESSAGE_MAX_SIZE)
1850  return ERROR_MESSAGE_TOO_LONG;
1851 
1852  //Point to the corresponding resource record
1853  record = DNS_GET_RESOURCE_RECORD(message->dnsHeader, offset);
1854 
1855  //Fill in resource record
1856  record->rtype = HTONS(DNS_RR_TYPE_TXT);
1857  record->rclass = HTONS(DNS_RR_CLASS_IN);
1858  record->ttl = htonl(ttl);
1859  record->rdlength = htons(service->metadataLength);
1860 
1861  //Check whether the cache-flush bit should be set
1862  if(cacheFlush)
1863  record->rclass |= HTONS(MDNS_RCLASS_CACHE_FLUSH);
1864 
1865  //Advance write index
1866  offset += sizeof(DnsResourceRecord);
1867 
1868  //Check the length of the resulting mDNS message
1869  if((offset + service->metadataLength) > MDNS_MESSAGE_MAX_SIZE)
1870  return ERROR_MESSAGE_TOO_LONG;
1871 
1872  //Copy metadata
1873  memcpy(record->rdata, service->metadata, service->metadataLength);
1874 
1875  //Update the length of the DNS message
1876  message->length = offset + service->metadataLength;
1877  //Number of resource records in the answer section
1878  message->dnsHeader->ancount++;
1879  }
1880 
1881  //Successful processing
1882  return NO_ERROR;
1883 }
1884 
1885 
1886 /**
1887  * @brief Add NSEC record to a mDNS message
1888  * @param[in] interface Underlying network interface
1889  * @param[in,out] message Pointer to the mDNS message
1890  * @param[in] service Pointer to a DNS-SD service
1891  * @param[in] cacheFlush Cache-flush bit
1892  * @param[in] ttl Resource record TTL (cache lifetime)
1893  * @return Error code
1894  **/
1895 
1897  const DnsSdService *service, bool_t cacheFlush, uint32_t ttl)
1898 {
1899  size_t n;
1900  size_t offset;
1901  bool_t duplicate;
1902  size_t bitmapLength;
1903  uint8_t bitmap[8];
1904  DnsSdContext *context;
1905  DnsResourceRecord *record;
1906 
1907  //Point to the DNS-SD context
1908  context = interface->dnsSdContext;
1909 
1910  //Check whether the resource record is already present in the Answer
1911  //Section of the message
1912  duplicate = mdnsCheckDuplicateRecord(message, context->instanceName,
1913  service->name, ".local", DNS_RR_TYPE_NSEC);
1914 
1915  //The duplicates should be suppressed and the resource record should
1916  //appear only once in the list
1917  if(!duplicate)
1918  {
1919  //The bitmap identifies the resource record types that exist
1920  memset(bitmap, 0, sizeof(bitmap));
1921 
1922  //TXT resource record is supported
1924  //SRV resource record is supported
1926 
1927  //Compute the length of the bitmap
1928  for(bitmapLength = sizeof(bitmap); bitmapLength > 0; bitmapLength--)
1929  {
1930  //Trailing zero octets in the bitmap must be omitted...
1931  if(bitmap[bitmapLength - 1] != 0x00)
1932  break;
1933  }
1934 
1935  //Set the position to the end of the buffer
1936  offset = message->length;
1937 
1938  //The first pass calculates the length of the DNS encoded instance name
1939  n = mdnsEncodeName(context->instanceName, service->name, ".local", NULL);
1940 
1941  //Check the length of the resulting mDNS message
1942  if((offset + n) > MDNS_MESSAGE_MAX_SIZE)
1943  return ERROR_MESSAGE_TOO_LONG;
1944 
1945  //The second pass encodes the instance name using the DNS name notation
1946  offset += mdnsEncodeName(context->instanceName, service->name,
1947  ".local", (uint8_t *) message->dnsHeader + offset);
1948 
1949  //Consider the length of the resource record itself
1950  if((offset + sizeof(DnsResourceRecord)) > MDNS_MESSAGE_MAX_SIZE)
1951  return ERROR_MESSAGE_TOO_LONG;
1952 
1953  //Point to the corresponding resource record
1954  record = DNS_GET_RESOURCE_RECORD(message->dnsHeader, offset);
1955 
1956  //Fill in resource record
1957  record->rtype = HTONS(DNS_RR_TYPE_NSEC);
1958  record->rclass = HTONS(DNS_RR_CLASS_IN);
1959  record->ttl = htonl(ttl);
1960 
1961  //Check whether the cache-flush bit should be set
1962  if(cacheFlush)
1963  record->rclass |= HTONS(MDNS_RCLASS_CACHE_FLUSH);
1964 
1965  //Advance write index
1966  offset += sizeof(DnsResourceRecord);
1967 
1968  //Check the length of the resulting mDNS message
1969  if((offset + n + 2) > MDNS_MESSAGE_MAX_SIZE)
1970  return ERROR_MESSAGE_TOO_LONG;
1971 
1972  //The Next Domain Name field contains the record's own name
1973  mdnsEncodeName(context->instanceName, service->name,
1974  ".local", record->rdata);
1975 
1976  //DNS NSEC record is limited to Window Block number zero
1977  record->rdata[n++] = 0;
1978  //The Bitmap Length is a value in the range 1-32
1979  record->rdata[n++] = bitmapLength;
1980 
1981  //The Bitmap data identifies the resource record types that exist
1982  memcpy(record->rdata + n, bitmap, bitmapLength);
1983 
1984  //Convert length field to network byte order
1985  record->rdlength = htons(n + bitmapLength);
1986 
1987  //Number of resource records in the answer section
1988  message->dnsHeader->ancount++;
1989  //Update the length of the DNS message
1990  message->length = offset + n + bitmapLength;
1991  }
1992 
1993  //Successful processing
1994  return NO_ERROR;
1995 }
1996 
1997 #endif
uint16_t priority
Definition: dns_common.h:219
#define DNS_GET_QUESTION(message, offset)
Definition: dns_common.h:61
__start_packed struct @151 DnsQuestion
Question format.
#define DNS_GET_RESOURCE_RECORD(message, offset)
Definition: dns_common.h:62
uint32_t systime_t
Definition: compiler_port.h:44
error_t dnsSdSendAnnouncement(DnsSdContext *context)
Send announcement packet.
Definition: dns_sd.c:914
mDNS responder (Multicast DNS)
#define timeCompare(t1, t2)
Definition: os_port.h:40
char char_t
Definition: compiler_port.h:41
void dnsSdGenerateAdditionalRecords(NetInterface *interface, MdnsMessage *response, bool_t legacyUnicast)
Additional record generation.
Definition: dns_sd.c:1407
#define MDNS_PORT
Definition: mdns_common.h:51
systime_t osGetSystemTime(void)
Retrieve system time.
uint32_t time
#define MDNS_RCLASS_CACHE_FLUSH
Definition: mdns_common.h:60
TCP/IP stack core.
char_t name[DNS_SD_MAX_SERVICE_NAME_LEN+1]
Service name.
Definition: dns_sd.h:123
Debugging facilities.
Internet.
Definition: dns_common.h:108
uint16_t qtype
Definition: dns_common.h:190
uint_t numAnnouncements
Number of announcement packets.
Definition: dns_sd.h:111
Generic error code.
Definition: error.h:43
uint8_t message[]
Definition: chap.h:150
size_t mdnsEncodeName(const char_t *instance, const char_t *service, const char_t *domain, uint8_t *dest)
Encode instance, service and domain names using the DNS name notation.
Definition: mdns_common.c:538
error_t dnsSdStartProbing(DnsSdContext *context)
Restart probing process.
Definition: dns_sd.c:483
Invalid parameter.
Definition: error.h:45
error_t dnsSdInit(DnsSdContext *context, const DnsSdSettings *settings)
DNS-DS initialization.
Definition: dns_sd.c:84
mDNS message
Definition: mdns_common.h:75
int_t mdnsCompareName(const DnsHeader *message, size_t length, size_t pos, const char_t *instance, const char_t *service, const char_t *domain, uint_t level)
Compare instance, service and domain names.
Definition: mdns_common.c:628
#define MDNS_PROBE_DELAY
Text strings.
Definition: dns_common.h:130
error_t dnsSdUnregisterService(DnsSdContext *context, const char_t *serviceName)
Unregister a DNS-SD service.
Definition: dns_sd.c:406
error_t dnsSdAddTxtRecord(NetInterface *interface, MdnsMessage *message, const DnsSdService *service, bool_t cacheFlush, uint32_t ttl)
Add TXT record to a mDNS message.
Definition: dns_sd.c:1813
error_t mdnsSendMessage(NetInterface *interface, const MdnsMessage *message, const IpAddr *destIpAddr, uint_t destPort)
Send mDNS message.
Definition: mdns_common.c:452
DNS-SD settings.
Definition: dns_sd.h:108
String manipulation helper functions.
#define MDNS_ANNOUNCE_NUM
uint16_t port
Port on the target host of this service.
Definition: dns_sd.h:126
#define htons(value)
Definition: cpu_endian.h:390
uint16_t ancount
Definition: dns_common.h:177
uint16_t priority
Priority of the target host.
Definition: dns_sd.h:124
#define DNS_SD_DEFAULT_RR_TTL
Definition: dns_sd.h:81
#define MdnsResponderContext
uint8_t m
Definition: ndp.h:299
#define HTONS(value)
Definition: cpu_endian.h:388
#define TRUE
Definition: os_port.h:48
error_t dnsSdParseQuestion(NetInterface *interface, const MdnsMessage *query, size_t offset, const DnsQuestion *question, MdnsMessage *response)
Parse a question.
Definition: dns_sd.c:1083
#define strcasecmp
uint16_t qclass
Definition: dns_common.h:191
Data logging functions for debugging purpose (DNS)
size_t length
Definition: mdns_common.h:79
error_t dnsSdSendProbe(DnsSdContext *context)
Send probe packet.
Definition: dns_sd.c:798
#define MDNS_MESSAGE_MAX_SIZE
Definition: mdns_common.h:38
#define DNS_SD_MAX_SERVICE_NAME_LEN
Definition: dns_sd.h:60
error_t dnsSdAddPtrRecord(NetInterface *interface, MdnsMessage *message, const DnsSdService *service, uint32_t ttl)
Add PTR record to a mDNS message.
Definition: dns_sd.c:1630
#define MDNS_PROBE_DEFER
__start_packed struct @150 DnsHeader
DNS message header.
error_t dnsSdStop(DnsSdContext *context)
Stop mDNS responder.
Definition: dns_sd.c:157
#define ntohs(value)
Definition: cpu_endian.h:396
DnsHeader * dnsHeader
Definition: mdns_common.h:82
void dnsSdParseAnRecord(NetInterface *interface, const MdnsMessage *response, size_t offset, const DnsResourceRecord *record)
Parse a resource record from the Answer Section.
Definition: dns_sd.c:1337
#define htonl(value)
Definition: cpu_endian.h:391
uint16_t rclass
Definition: dns_common.h:202
A request for all records.
Definition: dns_common.h:139
error_t dnsSdStart(DnsSdContext *context)
Start mDNS responder.
Definition: dns_sd.c:126
uint32_t ttl
Definition: dns_common.h:203
void dnsSdTick(DnsSdContext *context)
DNS-SD responder timer handler.
Definition: dns_sd.c:506
NetInterface * netGetDefaultInterface(void)
Get default network interface.
Definition: net.c:1495
#define MDNS_LEGACY_UNICAST_RR_TTL
Definition: mdns_common.h:55
#define MIN(a, b)
Definition: os_port.h:60
void dnsSdGetDefaultSettings(DnsSdSettings *settings)
Initialize settings with default values.
Definition: dns_sd.c:63
uint16_t weight
Definition: dns_common.h:220
#define MDNS_PROBE_NUM
MdnsState
mDNS responder states
#define DNS_SET_NSEC_BITMAP(bitmap, type)
Definition: dns_common.h:64
uint16_t weight
Server selection mechanism.
Definition: dns_sd.h:125
error_t dnsSdSendGoodbye(DnsSdContext *context, const DnsSdService *service)
Send goodbye packet.
Definition: dns_sd.c:1003
DnsSdStateChangeCallback stateChangeEvent
FSM state change event.
Definition: dns_sd.h:113
uint8_t s
error_t dnsSdRegisterService(DnsSdContext *context, const char_t *serviceName, uint16_t priority, uint16_t weight, uint16_t port, const char_t *metadata)
Register a DNS-SD service.
Definition: dns_sd.c:265
#define TRACE_INFO(...)
Definition: debug.h:86
size_t dnsParseName(const DnsHeader *message, size_t length, size_t pos, char_t *dest, uint_t level)
Decode a domain name that uses the DNS name encoding.
Definition: dns_common.c:128
Success.
Definition: error.h:42
#define MDNS_RAND_DELAY_MAX
uint8_t metadata[DNS_SD_MAX_METADATA_LEN]
Discovery-time metadata (TXT record)
Definition: dns_sd.h:127
NetInterface * interface
Underlying network interface.
Definition: dns_sd.h:110
NSEC record.
Definition: dns_common.h:135
int32_t netGetRandRange(int32_t min, int32_t max)
Get a random value in the specified range.
Definition: net.c:1554
#define DNS_SD_MAX_METADATA_LEN
Definition: dns_sd.h:74
error_t dnsSdAddSrvRecord(NetInterface *interface, MdnsMessage *message, const DnsSdService *service, bool_t cacheFlush, uint32_t ttl)
Add SRV record to a mDNS message.
Definition: dns_sd.c:1715
void dnsSdLinkChangeEvent(DnsSdContext *context)
Callback function for link change event.
Definition: dns_sd.c:669
error_t
Error codes.
Definition: error.h:40
void dnsSdParseNsRecord(NetInterface *interface, const MdnsMessage *query, size_t offset, const DnsResourceRecord *record)
Parse a resource record from the Authority Section.
Definition: dns_sd.c:1242
systime_t dnsSdTickCounter
Definition: dns_sd.c:55
unsigned int uint_t
Definition: compiler_port.h:43
Server selection.
Definition: dns_common.h:133
#define DNS_SD_SERVICE_LIST_SIZE
Definition: dns_sd.h:53
Any class.
Definition: dns_common.h:111
void osReleaseMutex(OsMutex *mutex)
Release ownership of the specified mutex object.
error_t strSafeCopy(char_t *dest, const char_t *src, size_t destSize)
Copy string.
Definition: str.c:157
uint32_t ttl
TTL resource record.
Definition: dns_sd.h:112
#define NetInterface
Definition: net.h:34
uint_t sharedRecordCount
Definition: mdns_common.h:85
#define MDNS_RAND_DELAY_MIN
error_t dnsSdSetInstanceName(DnsSdContext *context, const char_t *instanceName)
Set service instance name.
Definition: dns_sd.c:212
void dnsSdChangeInstanceName(DnsSdContext *context)
Programmatically change the service instance name.
Definition: dns_sd.c:723
DNS-SD (DNS-Based Service Discovery)
uint16_t port
Definition: dns_common.h:221
const UdpHeader * udpHeader
Definition: mdns_common.h:81
#define DNS_SD_MAX_INSTANCE_NAME_LEN
Definition: dns_sd.h:67
Domain name pointer.
Definition: dns_common.h:126
__start_packed struct @152 DnsResourceRecord
Resource record format.
Definitions common to mDNS client and mDNS responder.
OsMutex netMutex
Definition: net.c:70
#define MDNS_ANNOUNCE_DELAY
void dnsSdChangeState(DnsSdContext *context, MdnsState newState, systime_t delay)
Update FSM state.
Definition: dns_sd.c:688
error_t mdnsCreateMessage(MdnsMessage *message, bool_t queryResponse)
Create an empty mDNS message.
Definition: mdns_common.c:352
uint_t dnsSdGetNumServices(DnsSdContext *context)
Get the number of registered services.
Definition: dns_sd.c:448
#define MDNS_QCLASS_QU
Definition: mdns_common.h:58
uint8_t n
size_t metadataLength
Length of the metadata.
Definition: dns_sd.h:128
MdnsState dnsSdGetState(DnsSdContext *context)
Retrieve current state.
Definition: dns_sd.c:188
error_t dnsSdAddServiceEnumPtrRecord(NetInterface *interface, MdnsMessage *message, const DnsSdService *service, uint32_t ttl)
Add PTR record to a mDNS message (in response to a meta-query)
Definition: dns_sd.c:1561
__start_packed struct @153 DnsSrvResourceRecord
SRV resource record format.
#define FALSE
Definition: os_port.h:44
int bool_t
Definition: compiler_port.h:47
DNS-SD service descriptor.
Definition: dns_sd.h:121
bool_t mdnsCheckDuplicateRecord(const MdnsMessage *message, const char_t *instance, const char_t *service, const char_t *domain, uint16_t rtype)
Check for duplicate resource records.
Definition: mdns_common.c:887
void mdnsDeleteMessage(MdnsMessage *message)
release a mDNS message
Definition: mdns_common.c:428
void osAcquireMutex(OsMutex *mutex)
Acquire ownership of the specified mutex object.
#define DnsSdContext
Definition: dns_sd.h:88
error_t dnsSdAddNsecRecord(NetInterface *interface, MdnsMessage *message, const DnsSdService *service, bool_t cacheFlush, uint32_t ttl)
Add NSEC record to a mDNS message.
Definition: dns_sd.c:1896