mdns_common.c
Go to the documentation of this file.
1 /**
2  * @file mdns_common.c
3  * @brief Definitions common to mDNS client and mDNS responder
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2010-2023 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  * Multicast DNS and its companion technology DNS-Based Service Discovery
30  * were created to provide ease-of-use and autoconfiguration to IP networks.
31  * Refer to the following RFCs for complete details:
32  * - RFC 6762: Multicast DNS
33  * - RFC 6763: DNS-Based Service Discovery
34  *
35  * @author Oryx Embedded SARL (www.oryx-embedded.com)
36  * @version 2.2.4
37  **/
38 
39 //Switch to the appropriate trace level
40 #define TRACE_LEVEL MDNS_TRACE_LEVEL
41 
42 //Dependencies
43 #include <stdlib.h>
44 #include <string.h>
45 #include "core/net.h"
46 #include "ipv4/ipv4_misc.h"
47 #include "ipv6/ipv6_misc.h"
48 #include "mdns/mdns_client.h"
49 #include "mdns/mdns_responder.h"
51 #include "dns_sd/dns_sd_misc.h"
52 #include "dns/dns_debug.h"
53 #include "debug.h"
54 
55 //Check TCP/IP stack configuration
56 #if (MDNS_CLIENT_SUPPORT == ENABLED || MDNS_RESPONDER_SUPPORT == ENABLED)
57 
58 //mDNS IPv6 multicast group (ff02::fb)
60  IPV6_ADDR(0xFF02, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00FB);
61 
62 
63 /**
64  * @brief mDNS related initialization
65  * @param[in] interface Underlying network interface
66  * @return Error code
67  **/
68 
70 {
71  error_t error;
72 
73 #if (IPV4_SUPPORT == ENABLED)
74  //Join the mDNS IPv4 multicast group
76  //Any error to report?
77  if(error)
78  return error;
79 #endif
80 
81 #if (IPV6_SUPPORT == ENABLED)
82  //Join the mDNS IPv6 multicast group
84  //Any error to report?
85  if(error)
86  return error;
87 #endif
88 
89  //Callback function to be called when a mDNS message is received
90  error = udpAttachRxCallback(interface, MDNS_PORT, mdnsProcessMessage, NULL);
91  //Any error to report?
92  if(error)
93  return error;
94 
95  //Successful initialization
96  return NO_ERROR;
97 }
98 
99 
100 /**
101  * @brief Process incoming mDNS message
102  * @param[in] interface Underlying network interface
103  * @param[in] pseudoHeader UDP pseudo header
104  * @param[in] udpHeader UDP header
105  * @param[in] buffer Multi-part buffer containing the incoming mDNS message
106  * @param[in] offset Offset to the first byte of the mDNS message
107  * @param[in] ancillary Additional options passed to the stack along with
108  * the packet
109  * @param[in] param Callback function parameter (not used)
110  **/
111 
113  const IpPseudoHeader *pseudoHeader, const UdpHeader *udpHeader,
114  const NetBuffer *buffer, size_t offset, const NetRxAncillary *ancillary,
115  void *param)
116 {
117  size_t length;
118  DnsHeader *dnsHeader;
120 
121  //Retrieve the length of the mDNS message
122  length = netBufferGetLength(buffer) - offset;
123 
124  //Ensure the mDNS message is valid
125  if(length < sizeof(DnsHeader))
126  return;
128  return;
129 
130  //Point to the mDNS message header
131  dnsHeader = netBufferAt(buffer, offset);
132  //Sanity check
133  if(dnsHeader == NULL)
134  return;
135 
136  //Debug message
137  TRACE_INFO("mDNS message received (%" PRIuSIZE " bytes)...\r\n", length);
138  //Dump message
139  dnsDumpMessage(dnsHeader, length);
140 
141  //mDNS messages received with an opcode other than zero must be silently
142  //ignored
143  if(dnsHeader->opcode != DNS_OPCODE_QUERY)
144  return;
145 
146  //mDNS messages received with non-zero response codes must be silently
147  //ignored
148  if(dnsHeader->rcode != DNS_RCODE_NO_ERROR)
149  return;
150 
151  //Save mDNS message
152  message.buffer = (NetBuffer *) buffer;
153  message.offset = offset;
154  message.length = length;
155  message.pseudoHeader = pseudoHeader;
156  message.udpHeader = udpHeader;
157  message.dnsHeader = dnsHeader;
158 
159  //mDNS query received?
160  if(!dnsHeader->qr)
161  {
162 #if (MDNS_RESPONDER_SUPPORT == ENABLED)
163  //Process incoming mDNS query message
164  mdnsResponderProcessQuery(interface, &message);
165 #endif
166  }
167  //mDNS response received?
168  else
169  {
170  //Process incoming mDNS response message
171  mdnsProcessResponse(interface, &message);
172  }
173 }
174 
175 
176 /**
177  * @brief Process mDNS response message
178  * @param[in] interface Underlying network interface
179  * @param[in] response Incoming mDNS response message
180  **/
181 
182 void mdnsProcessResponse(NetInterface *interface, MdnsMessage *response)
183 {
184  uint_t i;
185  uint_t k;
186  size_t n;
187  size_t offset;
188  DnsResourceRecord *record;
189 
190  //Source address check (refer to RFC 6762 section 11)
191  if(!mdnsCheckSourceAddr(interface, response->pseudoHeader))
192  return;
193 
194  //mDNS implementations must silently ignore any mDNS responses they
195  //receive where the source UDP port is not 5353
196  if(ntohs(response->udpHeader->srcPort) != MDNS_PORT)
197  return;
198 
199  //Point to the question section
200  offset = sizeof(DnsHeader);
201 
202  //Any questions in the question section of a received mDNS response
203  //must be silently ignored
204  for(i = 0; i < ntohs(response->dnsHeader->qdcount); i++)
205  {
206  //Parse domain name
207  offset = dnsParseName(response->dnsHeader, response->length, offset, NULL, 0);
208  //Invalid name?
209  if(!offset)
210  break;
211 
212  //Point to the next question
213  offset += sizeof(DnsQuestion);
214  //Make sure the mDNS message is valid
215  if(offset > response->length)
216  break;
217  }
218 
219  //Malformed mDNS message?
220  if(i != ntohs(response->dnsHeader->qdcount))
221  return;
222 
223  //Compute the total number of resource records
224  k = ntohs(response->dnsHeader->ancount) +
225  ntohs(response->dnsHeader->nscount) +
226  ntohs(response->dnsHeader->arcount);
227 
228  //Loop through the resource records
229  for(i = 0; i < k; i++)
230  {
231  //Parse resource record name
232  n = dnsParseName(response->dnsHeader, response->length, offset, NULL, 0);
233  //Invalid name?
234  if(!n)
235  break;
236 
237  //Point to the associated resource record
238  record = DNS_GET_RESOURCE_RECORD(response->dnsHeader, n);
239  //Point to the resource data
240  n += sizeof(DnsResourceRecord);
241 
242  //Make sure the resource record is valid
243  if(n > response->length)
244  break;
245  if((n + ntohs(record->rdlength)) > response->length)
246  break;
247 
248 #if (MDNS_CLIENT_SUPPORT == ENABLED)
249  //Parse the resource record
250  mdnsClientParseAnRecord(interface, response, offset, record);
251 #endif
252 
253 #if (MDNS_RESPONDER_SUPPORT == ENABLED)
254  //Parse the resource record
255  mdnsResponderParseAnRecord(interface, response, offset, record);
256 #endif
257 
258 #if (DNS_SD_SUPPORT == ENABLED)
259  //Parse the resource record
260  dnsSdParseAnRecord(interface, response, offset, record);
261 #endif
262 
263  //Point to the next resource record
264  offset = n + ntohs(record->rdlength);
265  }
266 }
267 
268 
269 /**
270  * @brief Source address check
271  * @param[in] interface Underlying network interface
272  * @param[in] pseudoHeader UDP pseudo header
273  * @return TRUE if the source address is valid, else FALSE
274  **/
275 
277  const IpPseudoHeader *pseudoHeader)
278 {
279  bool_t valid;
280 
281 #if (IPV4_SUPPORT == ENABLED)
282  //IPv4 packet received?
283  if(pseudoHeader->length == sizeof(Ipv4PseudoHeader))
284  {
285  //Perform source address check (refer to RFC 6762 section 11)
286  if(pseudoHeader->ipv4Data.destAddr == MDNS_IPV4_MULTICAST_ADDR)
287  {
288  //All responses received with the destination address 224.0.0.251
289  //are necessarily deemed to have originated on the local link,
290  //regardless of source IP address
291  valid = TRUE;
292  }
293  else if(ipv4IsLinkLocalAddr(pseudoHeader->ipv4Data.srcAddr) ||
294  ipv4IsLinkLocalAddr(pseudoHeader->ipv4Data.destAddr))
295  {
296  //Packets with a link-local source or destination address
297  //originate from the local link
298  valid = TRUE;
299  }
300  else if(ipv4IsOnLink(interface, pseudoHeader->ipv4Data.srcAddr))
301  {
302  //The source IP address is on the local link
303  valid = TRUE;
304  }
305  else
306  {
307  //Only accept responses that originate from the local link, and
308  //silently discard any other response packets
309  valid = FALSE;
310  }
311  }
312  else
313 #endif
314 #if (IPV6_SUPPORT == ENABLED)
315  //IPv6 packet received?
316  if(pseudoHeader->length == sizeof(Ipv6PseudoHeader))
317  {
318  //Perform source address check (refer to RFC 6762 section 11)
319  if(ipv6CompAddr(&pseudoHeader->ipv6Data.destAddr, &MDNS_IPV6_MULTICAST_ADDR))
320  {
321  //All responses received with the destination address ff02::fb
322  //are necessarily deemed to have originated on the local link,
323  //regardless of source IP address
324  valid = TRUE;
325  }
326  else if(ipv6IsOnLink(interface, &pseudoHeader->ipv6Data.srcAddr))
327  {
328  //The source IP address is on the local link
329  valid = TRUE;
330  }
331  else
332  {
333  //Only accept responses that originate from the local link, and
334  //silently discard any other response packets
335  valid = FALSE;
336  }
337  }
338  else
339 #endif
340  //Invalid packet received?
341  {
342  //Discard the response packet
343  valid = FALSE;
344  }
345 
346  //Return flag value
347  return valid;
348 }
349 
350 
351 /**
352  * @brief Create an empty mDNS message
353  * @param[in,out] message Newly created mDNS message
354  * @param[in] queryResponse This flag specifies whether the message is a query or a response
355  * @return Error code
356  **/
357 
359 {
360  error_t error;
361 
362  //Allocate a memory buffer to hold the mDNS message
364 
365  //Successful memory allocation?
366  if(message->buffer != NULL)
367  {
368  //Point to the mDNS message header
369  message->dnsHeader = netBufferAt(message->buffer, message->offset);
370 
371  //Sanity check
372  if(message->dnsHeader != NULL)
373  {
374  //Format mDNS message header
375  message->dnsHeader->id = 0;
376  message->dnsHeader->opcode = DNS_OPCODE_QUERY;
377  message->dnsHeader->tc = 0;
378  message->dnsHeader->rd = 0;
379  message->dnsHeader->ra = 0;
380  message->dnsHeader->z = 0;
381  message->dnsHeader->rcode = DNS_RCODE_NO_ERROR;
382  message->dnsHeader->qdcount = 0;
383  message->dnsHeader->ancount = 0;
384  message->dnsHeader->nscount = 0;
385  message->dnsHeader->arcount = 0;
386 
387  //Query or response mDNS message?
388  if(!queryResponse)
389  {
390  //In query messages, QR and AA bits must be set to zero
391  message->dnsHeader->qr = 0;
392  message->dnsHeader->aa = 0;
393  }
394  else
395  {
396  //In response messages, QR and AA bits must be set to one
397  message->dnsHeader->qr = 1;
398  message->dnsHeader->aa = 1;
399  }
400 
401  //Number of shared resource records
402  message->sharedRecordCount = 0;
403  //Length of the mDNS message
404  message->length = sizeof(DnsHeader);
405 
406  //Successful processing
407  error = NO_ERROR;
408  }
409  else
410  {
411  //Clean up side effects
413 
414  //Report an error
415  error = ERROR_FAILURE;
416  }
417  }
418  else
419  {
420  //Failed to allocate memory
421  error = ERROR_OUT_OF_RESOURCES;
422  }
423 
424  //Return status code
425  return error;
426 }
427 
428 
429 /**
430  * @brief release a mDNS message
431  * @param[in] message mDNS message to be released
432  **/
433 
435 {
436  //Valid mDNS message?
437  if(message->buffer != NULL)
438  {
439  //Free previously allocated memory
440  netBufferFree(message->buffer);
441 
442  //The mDNS message is no more valid
443  message->buffer = NULL;
444  message->length = 0;
445  }
446 }
447 
448 
449 /**
450  * @brief Send mDNS message
451  * @param[in] interface Underlying network interface
452  * @param[in] message mDNS message to be sent
453  * @param[in] destIpAddr Destination IP address (optional parameter)
454  * @param[in] destPort Destination port
455  * @return Error code
456  **/
457 
460 {
461  error_t error;
462  IpAddr ipAddr;
463  NetTxAncillary ancillary;
464 
465  //Convert 16-bit values to network byte order
466  message->dnsHeader->qdcount = htons(message->dnsHeader->qdcount);
467  message->dnsHeader->nscount = htons(message->dnsHeader->nscount);
468  message->dnsHeader->ancount = htons(message->dnsHeader->ancount);
469  message->dnsHeader->arcount = htons(message->dnsHeader->arcount);
470 
471  //Adjust the length of the multi-part buffer
472  error = netBufferSetLength(message->buffer, message->offset + message->length);
473 
474  //Check status code
475  if(!error)
476  {
477  //Debug message
478  TRACE_INFO("Sending mDNS message (%" PRIuSIZE " bytes)...\r\n", message->length);
479  //Dump message
480  dnsDumpMessage(message->dnsHeader, message->length);
481 
482  //Check whether the message should be sent to a specific IP address
483  if(destIpAddr != NULL)
484  {
485  //Additional options can be passed to the stack along with the packet
486  ancillary = NET_DEFAULT_TX_ANCILLARY;
487 
488  //All multicast DNS responses should be sent with an IP TTL set to 255
489  //(refer to RFC 6762, section 11)
490  ancillary.ttl = MDNS_DEFAULT_IP_TTL;
491 
492  //Send mDNS message
493  udpSendBuffer(interface, NULL, MDNS_PORT, destIpAddr, destPort,
494  message->buffer, message->offset, &ancillary);
495  }
496  else
497  {
498 #if (IPV4_SUPPORT == ENABLED)
499  //Select the relevant multicast address (224.0.0.251)
500  ipAddr.length = sizeof(Ipv4Addr);
501  ipAddr.ipv4Addr = MDNS_IPV4_MULTICAST_ADDR;
502 
503  //Additional options can be passed to the stack along with the packet
504  ancillary = NET_DEFAULT_TX_ANCILLARY;
505  //Set the TTL value to be used
506  ancillary.ttl = MDNS_DEFAULT_IP_TTL;
507 
508  //Send mDNS message
509  udpSendBuffer(interface, NULL, MDNS_PORT, &ipAddr, MDNS_PORT,
510  message->buffer, message->offset, &ancillary);
511 #endif
512 
513 #if (IPV6_SUPPORT == ENABLED)
514  //Select the relevant multicast address (ff02::fb)
515  ipAddr.length = sizeof(Ipv6Addr);
516  ipAddr.ipv6Addr = MDNS_IPV6_MULTICAST_ADDR;
517 
518  //Additional options can be passed to the stack along with the packet
519  ancillary = NET_DEFAULT_TX_ANCILLARY;
520  //Set the TTL value to be used
521  ancillary.ttl = MDNS_DEFAULT_IP_TTL;
522 
523  //Send mDNS message
524  udpSendBuffer(interface, NULL, MDNS_PORT, &ipAddr, MDNS_PORT,
525  message->buffer, message->offset, &ancillary);
526 #endif
527  }
528  }
529 
530  //Return status code
531  return error;
532 }
533 
534 
535 /**
536  * @brief Encode instance, service and domain names using the DNS name notation
537  * @param[in] instance Instance name
538  * @param[in] service Service name
539  * @param[in] domain Domain name
540  * @param[out] dest Pointer to the encoded name (optional parameter)
541  * @return Length of the encoded domain name
542  **/
543 
544 size_t mdnsEncodeName(const char_t *instance, const char_t *service,
545  const char_t *domain, uint8_t *dest)
546 {
547  size_t n;
548  size_t length;
549 
550  //Total length of the encoded name
551  length = 0;
552 
553  //Any instance name?
554  if(*instance != '\0')
555  {
556  //Encode instance name
557  n = dnsEncodeName(instance, dest);
558 
559  //Failed to encode instance name?
560  if(!n)
561  return 0;
562 
563  //Update the length of the encoded name
564  length += n;
565  }
566 
567  //Any service name?
568  if(*service != '\0')
569  {
570  //If an instance name precedes the service name, then
571  //remove the null label
572  if(length > 0)
573  {
574  length--;
575  }
576 
577  //Encode service name
578  if(dest != NULL)
579  {
580  n = dnsEncodeName(service, dest + length);
581  }
582  else
583  {
584  n = dnsEncodeName(service, NULL);
585  }
586 
587  //Failed to encode instance name?
588  if(!n)
589  return 0;
590 
591  //Update the length of the encoded name
592  length += n;
593  }
594 
595  //Skip the separator that may precede the domain name
596  if(*domain == '.')
597  {
598  domain++;
599  }
600 
601  //Any domain name to encode?
602  if(*domain != '\0')
603  {
604  //If an instance or a service name precedes the domain name, then
605  //remove the null label
606  if(length > 0)
607  {
608  length--;
609  }
610 
611  //Encode domain name
612  if(dest != NULL)
613  {
614  n = dnsEncodeName(domain, dest + length);
615  }
616  else
617  {
618  n = dnsEncodeName(domain, NULL);
619  }
620 
621  //Failed to encode instance name?
622  if(!n)
623  return 0;
624 
625  //Update the length of the encoded name
626  length += n;
627  }
628 
629  //Return the length of the encoded string
630  return length;
631 }
632 
633 
634 /**
635  * @brief Compare instance, service and domain names
636  * @param[in] message Pointer to the DNS message
637  * @param[in] length Length of the DNS message
638  * @param[in] pos Offset of the encoded name
639  * @param[in] instance Instance name
640  * @param[in] service Service name
641  * @param[in] domain Domain name
642  * @param[in] level Current level of recursion
643  * @return The function returns 0 if the domain names match, -1 if the first
644  * domain name lexicographically precedes the second name, or 1 if the
645  * second domain name lexicographically precedes the first name
646  **/
647 
648 int_t mdnsCompareName(const DnsHeader *message, size_t length, size_t pos,
649  const char_t *instance, const char_t *service, const char_t *domain, uint_t level)
650 {
651  int_t res;
652  size_t n;
653  size_t pointer;
654  uint8_t *p;
655 
656  //Check parameters
657  if(instance == NULL || service == NULL || domain == NULL)
658  return -2;
659 
660  //Recursion limit exceeded?
662  return -2;
663 
664  //Cast the DNS message to byte array
665  p = (uint8_t *) message;
666 
667  //Skip the separator that may precede the domain name
668  if(*domain == '.')
669  domain++;
670 
671  //Parse encoded domain name
672  while(pos < length)
673  {
674  //Retrieve the length of the current label
675  n = p[pos];
676 
677  //End marker found?
678  if(n == 0)
679  {
680  //The domain name which still has remaining data is deemed
681  //lexicographically later
682  if(*instance != '\0' || *service != '\0' || *domain != '\0')
683  return -1;
684 
685  //The domain names match each other
686  return 0;
687  }
688  //Compression tag found?
689  if(n >= DNS_COMPRESSION_TAG)
690  {
691  //Malformed DNS message?
692  if((pos + 1) >= length)
693  return -2;
694 
695  //Read the most significant byte of the pointer
696  pointer = (p[pos] & ~DNS_COMPRESSION_TAG) << 8;
697  //Read the least significant byte of the pointer
698  pointer |= p[pos + 1];
699 
700  //Compare the remaining part
702  instance, service, domain, level + 1);
703 
704  //Return comparison result
705  return res;
706  }
707  else
708  {
709  //Advance data pointer
710  pos++;
711 
712  //Malformed DNS message?
713  if((pos + n) > length)
714  return -2;
715 
716  //Compare current label
717  if(*instance != '\0')
718  {
719  //Compare instance name
720  res = osStrncasecmp((char_t *) p + pos, instance, n);
721  //Any mismatch?
722  if(res)
723  return res;
724 
725  //Advance data pointer
726  instance += n;
727 
728  //The instance name which still has remaining data is deemed
729  //lexicographically later
730  if(*instance != '\0' && *instance != '.')
731  return -1;
732 
733  //Skip the separator character, if any
734  if(*instance == '.')
735  instance++;
736  }
737  else if(*service != '\0')
738  {
739  //Compare service name
740  res = osStrncasecmp((char_t *) p + pos, service, n);
741  //Any mismatch?
742  if(res)
743  return res;
744 
745  //Advance data pointer
746  service += n;
747 
748  //The service name which still has remaining data is deemed
749  //lexicographically later
750  if(*service != '\0' && *service != '.')
751  return -1;
752 
753  //Any separator in service name?
754  if(*service == '.')
755  service++;
756  }
757  else
758  {
759  //Compare domain name
760  res = osStrncasecmp((char_t *) p + pos, domain, n);
761  //Any mismatch?
762  if(res)
763  return res;
764 
765  //Advance data pointer
766  domain += n;
767 
768  //The domain name which still has remaining data is deemed
769  //lexicographically later
770  if(*domain != '\0' && *domain != '.')
771  return -1;
772 
773  //Any separator in domain name?
774  if(*domain == '.')
775  domain++;
776  }
777 
778  //Advance data pointer
779  pos += n;
780  }
781  }
782 
783  //Malformed DNS message
784  return -2;
785 }
786 
787 
788 /**
789  * @brief Compare resource records
790  * @param[in] message1 Pointer to the first mDNS message
791  * @param[in] record1 Pointer the first resource record
792  * @param[in] message2 Pointer to the second mDNS message
793  * @param[in] record2 Pointer the second resource record
794  * @return The function returns 0 if the resource records match, -1 if the first
795  * resource record lexicographically precedes the second one, or 1 if the
796  * second resource record lexicographically precedes the first one
797  **/
798 
800  const DnsResourceRecord *record1, const MdnsMessage *message2,
801  const DnsResourceRecord *record2)
802 {
803  int_t res;
804  size_t n1;
805  size_t n2;
806  uint16_t value1;
807  uint16_t value2;
808 
809  //Convert the record class to host byte order
810  value1 = ntohs(record1->rclass);
811  value2 = ntohs(record2->rclass);
812 
813  //Discard cache-flush bit
814  value1 &= ~MDNS_RCLASS_CACHE_FLUSH;
815  value2 &= ~MDNS_RCLASS_CACHE_FLUSH;
816 
817  //The determination of lexicographically later record is performed by
818  //first comparing the record class (excluding the cache-flush bit)
819  if(value1 < value2)
820  {
821  return -1;
822  }
823  else if(value1 > value2)
824  {
825  return 1;
826  }
827  else
828  {
829  }
830 
831  //Convert the record type to host byte order
832  value1 = ntohs(record1->rtype);
833  value2 = ntohs(record2->rtype);
834 
835  //Then compare the record type
836  if(value1 < value2)
837  {
838  return -1;
839  }
840  else if(value1 > value2)
841  {
842  return 1;
843  }
844  else
845  {
846  }
847 
848  //If the rrtype and rrclass both match, then the rdata is compared
849  if(value1 == DNS_RR_TYPE_NS || value1 == DNS_RR_TYPE_SOA ||
850  value1 == DNS_RR_TYPE_CNAME || value1 == DNS_RR_TYPE_PTR)
851  {
852  //Compute the offset of the first byte of the rdata
853  n1 = record1->rdata - (uint8_t *) message1->dnsHeader;
854  n2 = record2->rdata - (uint8_t *) message2->dnsHeader;
855 
856  //The names must be uncompressed before comparison
857  res = dnsCompareEncodedName(message1->dnsHeader, message1->length,
858  n1, message2->dnsHeader, message2->length, n2, 0);
859  }
860  else
861  {
862  //Retrieve the length of the rdata fields
863  n1 = htons(record1->rdlength);
864  n2 = htons(record2->rdlength);
865 
866  //The bytes of the raw uncompressed rdata are compared in turn, interpreting
867  //the bytes as eight-bit unsigned values, until a byte is found whose value
868  //is greater than that of its counterpart (in which case, the rdata whose
869  //byte has the greater value is deemed lexicographically later) or one of the
870  //resource records runs out of rdata (in which case, the resource record which
871  //still has remaining data first is deemed lexicographically later)
872  if(n1 < n2)
873  {
874  //Raw comparison of the binary content of the rdata
875  res = osMemcmp(record1->rdata, record2->rdata, n1);
876 
877  //Check comparison result
878  if(!res)
879  {
880  //The first resource records runs out of rdata
881  res = -1;
882  }
883  }
884  else if(n1 > n2)
885  {
886  //Raw comparison of the binary content of the rdata
887  res = osMemcmp(record1->rdata, record2->rdata, n2);
888 
889  //Check comparison result
890  if(!res)
891  {
892  //The second resource records runs out of rdata
893  res = 1;
894  }
895  }
896  else
897  {
898  //Raw comparison of the binary content of the rdata
899  res = osMemcmp(record1->rdata, record2->rdata, n1);
900  }
901  }
902 
903  //Return comparison result
904  return res;
905 }
906 
907 
908 /**
909  * @brief Check for duplicate resource records
910  * @param[in] message Pointer to the mDNS message
911  * @param[in] instance Instance name
912  * @param[in] service Service name
913  * @param[in] domain Domain name
914  * @param[in] rtype Resource record type
915  * @param[in] rdata Resource record data
916  * @param[in] rdlength Length of the resource record data, in bytes
917  * @return The function returns TRUE is the specified resource record is a
918  * duplicate. Otherwise FALSE is returned
919  **/
920 
922  const char_t *instance, const char_t *service, const char_t *domain,
923  uint16_t rtype, const uint8_t *rdata, size_t rdlength)
924 {
925  uint_t i;
926  uint_t k;
927  size_t n;
928  size_t offset;
929  uint16_t rclass;
930  bool_t duplicate;
931  DnsResourceRecord *record;
932 
933  //Clear flag
934  duplicate = FALSE;
935 
936  //Point to the first question
937  offset = sizeof(DnsHeader);
938 
939  //Parse the Question Section
940  for(i = 0; i < message->dnsHeader->qdcount; i++)
941  {
942  //Parse domain name
943  offset = dnsParseName(message->dnsHeader, message->length, offset, NULL, 0);
944  //Invalid name?
945  if(!offset)
946  break;
947 
948  //Point to the next question
949  offset += sizeof(DnsQuestion);
950  //Make sure the mDNS message is valid
951  if(offset > message->length)
952  break;
953  }
954 
955  //Successful processing?
956  if(i == message->dnsHeader->qdcount)
957  {
958  //Compute the total number of resource records
959  k = message->dnsHeader->ancount + message->dnsHeader->nscount +
960  message->dnsHeader->arcount;
961 
962  //Loop through the resource records
963  for(i = 0; i < k; i++)
964  {
965  //Parse resource record name
966  n = dnsParseName(message->dnsHeader, message->length, offset, NULL, 0);
967  //Invalid name?
968  if(!n)
969  break;
970 
971  //Point to the associated resource record
972  record = DNS_GET_RESOURCE_RECORD(message->dnsHeader, n);
973  //Point to the resource data
974  n += sizeof(DnsResourceRecord);
975 
976  //Make sure the resource record is valid
977  if(n > message->length)
978  break;
979  if((n + ntohs(record->rdlength)) > message->length)
980  break;
981 
982  //Convert the record class to host byte order
983  rclass = ntohs(record->rclass);
984  //Discard cache-flush bit
986 
987  //Check the class and the type of the resource record
988  if(rclass == DNS_RR_CLASS_IN && ntohs(record->rtype) == rtype)
989  {
990  //Compare resource record name
991  if(!mdnsCompareName(message->dnsHeader, message->length,
992  offset, instance, service, domain, 0))
993  {
994  //Valid resource record data?
995  if(rdata != NULL)
996  {
997  //Compare resource record data
998  if(ntohs(record->rdlength) == rdlength &&
999  !osMemcmp(record->rdata, rdata, rdlength))
1000  {
1001  //The resource record is already present in the Answer Section
1002  duplicate = TRUE;
1003  //We are done
1004  break;
1005  }
1006  }
1007  else
1008  {
1009  //The resource record is already present in the Answer Section
1010  duplicate = TRUE;
1011  //We are done
1012  break;
1013  }
1014  }
1015  }
1016 
1017  //Point to the next resource record
1018  offset = n + ntohs(record->rdlength);
1019  }
1020  }
1021 
1022  //The function returns TRUE is the specified resource record is a duplicate
1023  return duplicate;
1024 }
1025 
1026 #endif
void mdnsResponderProcessQuery(NetInterface *interface, MdnsMessage *query)
Process mDNS query message.
uint8_t length
Definition: coap_common.h:193
void mdnsClientParseAnRecord(NetInterface *interface, const MdnsMessage *message, size_t offset, const DnsResourceRecord *record)
Parse a resource record from the Answer Section.
Definition: mdns_client.c:257
#define htons(value)
Definition: cpu_endian.h:413
Ipv6PseudoHeader ipv6Data
Definition: ip.h:106
int bool_t
Definition: compiler_port.h:53
__start_packed struct @2 DnsResourceRecord
Resource record format.
const Ipv6Addr MDNS_IPV6_MULTICAST_ADDR
Definition: mdns_common.c:59
uint8_t pointer
Definition: icmp.h:166
const NetTxAncillary NET_DEFAULT_TX_ANCILLARY
Definition: net_misc.c:71
void mdnsProcessMessage(NetInterface *interface, const IpPseudoHeader *pseudoHeader, const UdpHeader *udpHeader, const NetBuffer *buffer, size_t offset, const NetRxAncillary *ancillary, void *param)
Process incoming mDNS message.
Definition: mdns_common.c:112
signed int int_t
Definition: compiler_port.h:49
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:648
IP network address.
Definition: ip.h:79
#define IPV6_ADDR(a, b, c, d, e, f, g, h)
Definition: ipv6.h:112
__start_packed struct @0 UdpHeader
UDP header.
@ DNS_OPCODE_QUERY
Definition: dns_common.h:81
@ DNS_RR_TYPE_SOA
Start of a zone of authority.
Definition: dns_common.h:126
uint8_t p
Definition: ndp.h:298
Structure describing a buffer that spans multiple chunks.
Definition: net_mem.h:89
#define TRUE
Definition: os_port.h:52
@ ERROR_OUT_OF_RESOURCES
Definition: error.h:64
bool_t mdnsCheckDuplicateRecord(const MdnsMessage *message, const char_t *instance, const char_t *service, const char_t *domain, uint16_t rtype, const uint8_t *rdata, size_t rdlength)
Check for duplicate resource records.
Definition: mdns_common.c:921
__start_packed struct @0 DnsHeader
DNS message header.
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:133
#define osMemcmp(p1, p2, length)
Definition: os_port.h:152
DnsHeader * dnsHeader
Definition: mdns_common.h:84
uint16_t rclass
Definition: dns_common.h:204
#define ipv6CompAddr(ipAddr1, ipAddr2)
Definition: ipv6.h:121
void dnsSdParseAnRecord(NetInterface *interface, const MdnsMessage *response, size_t offset, const DnsResourceRecord *record)
Parse a resource record from the Answer Section.
Definition: dns_sd_misc.c:702
@ DNS_RR_CLASS_IN
Internet.
Definition: dns_common.h:110
#define ipv4IsLinkLocalAddr(ipAddr)
Definition: ipv4.h:165
uint16_t destPort
Definition: tcp.h:338
uint16_t rtype
Definition: dns_common.h:203
size_t length
Definition: ip.h:99
const uint8_t res[]
uint32_t Ipv4Addr
IPv4 network address.
Definition: ipv4.h:268
uint16_t rdlength
Definition: dns_common.h:206
#define MDNS_DEFAULT_IP_TTL
Definition: mdns_common.h:55
uint8_t level
Definition: tls.h:1800
#define DNS_NAME_MAX_RECURSION
Definition: dns_common.h:39
IP pseudo header.
Definition: ip.h:98
#define MDNS_PORT
Definition: mdns_common.h:53
Helper functions for IPv4.
#define FALSE
Definition: os_port.h:48
#define osStrncasecmp(s1, s2, length)
Definition: os_port.h:188
error_t
Error codes.
Definition: error.h:43
#define DNS_GET_RESOURCE_RECORD(message, offset)
Definition: dns_common.h:64
#define Ipv6PseudoHeader
Definition: ipv6.h:42
void * netBufferAt(const NetBuffer *buffer, size_t offset)
Returns a pointer to the data at the specified position.
Definition: net_mem.c:413
error_t mdnsInit(NetInterface *interface)
mDNS related initialization
Definition: mdns_common.c:69
const IpPseudoHeader * pseudoHeader
Definition: mdns_common.h:82
@ ERROR_FAILURE
Generic error code.
Definition: error.h:45
size_t length
Definition: mdns_common.h:81
void mdnsResponderParseAnRecord(NetInterface *interface, const MdnsMessage *response, size_t offset, const DnsResourceRecord *record)
Parse a resource record from the Answer Section.
#define NetRxAncillary
Definition: net_misc.h:40
#define NetInterface
Definition: net.h:36
#define DNS_COMPRESSION_TAG
Definition: dns_common.h:60
Helper functions for DNS-SD.
error_t udpAttachRxCallback(NetInterface *interface, uint16_t port, UdpRxCallback callback, void *param)
Register user callback.
Definition: udp.c:877
void netBufferFree(NetBuffer *buffer)
Dispose a multi-part buffer.
Definition: net_mem.c:282
uint8_t rdata[]
Definition: dns_common.h:207
Helper functions for IPv6.
error_t udpSendBuffer(NetInterface *interface, const IpAddr *srcIpAddr, uint16_t srcPort, const IpAddr *destIpAddr, uint16_t destPort, NetBuffer *buffer, size_t offset, NetTxAncillary *ancillary)
Send a UDP datagram.
Definition: udp.c:548
@ DNS_RR_TYPE_PTR
Domain name pointer.
Definition: dns_common.h:128
#define NetTxAncillary
Definition: net_misc.h:36
error_t mdnsCreateMessage(MdnsMessage *message, bool_t queryResponse)
Create an empty mDNS message.
Definition: mdns_common.c:358
mDNS client (Multicast DNS)
error_t ipv6JoinMulticastGroup(NetInterface *interface, const Ipv6Addr *groupAddr)
Join an IPv6 multicast group.
Definition: ipv6.c:2002
#define Ipv4PseudoHeader
Definition: ipv4.h:39
#define TRACE_INFO(...)
Definition: debug.h:95
size_t netBufferGetLength(const NetBuffer *buffer)
Get the actual length of a multi-part buffer.
Definition: net_mem.c:297
void dnsDumpMessage(const DnsHeader *message, size_t length)
Dump DNS message for debugging purpose.
Definition: dns_debug.c:52
bool_t ipv4IsOnLink(NetInterface *interface, Ipv4Addr ipAddr)
Check whether an IPv4 address is on-link.
Definition: ipv4_misc.c:434
NetBuffer * udpAllocBuffer(size_t length, size_t *offset)
Allocate a buffer to hold a UDP packet.
Definition: udp.c:808
@ DNS_RR_TYPE_NS
Authoritative name server.
Definition: dns_common.h:124
int_t dnsCompareEncodedName(const DnsHeader *message1, size_t length1, size_t pos1, const DnsHeader *message2, size_t length2, size_t pos2, uint_t level)
Compare domain names encoded with DNS notation.
Definition: dns_common.c:342
size_t dnsEncodeName(const char_t *src, uint8_t *dest)
Encode a domain name using the DNS name notation.
Definition: dns_common.c:59
int_t mdnsCompareRecord(const MdnsMessage *message1, const DnsResourceRecord *record1, const MdnsMessage *message2, const DnsResourceRecord *record2)
Compare resource records.
Definition: mdns_common.c:799
#define ntohs(value)
Definition: cpu_endian.h:421
mDNS message
Definition: mdns_common.h:78
char char_t
Definition: compiler_port.h:48
void mdnsDeleteMessage(MdnsMessage *message)
release a mDNS message
Definition: mdns_common.c:434
uint8_t n
const UdpHeader * udpHeader
Definition: mdns_common.h:83
Helper functions for mDNS responder.
__start_packed struct @1 DnsQuestion
Question format.
error_t netBufferSetLength(NetBuffer *buffer, size_t length)
Adjust the length of a multi-part buffer.
Definition: net_mem.c:320
#define MDNS_MESSAGE_MAX_SIZE
Definition: mdns_common.h:40
uint8_t message[]
Definition: chap.h:152
@ DNS_RR_TYPE_CNAME
Canonical name for an alias.
Definition: dns_common.h:125
bool_t mdnsCheckSourceAddr(NetInterface *interface, const IpPseudoHeader *pseudoHeader)
Source address check.
Definition: mdns_common.c:276
error_t ipv4JoinMulticastGroup(NetInterface *interface, Ipv4Addr groupAddr)
Join the specified host group.
Definition: ipv4.c:1320
#define MDNS_IPV4_MULTICAST_ADDR
Definition: mdns_common.h:65
#define PRIuSIZE
unsigned int uint_t
Definition: compiler_port.h:50
TCP/IP stack core.
@ DNS_RCODE_NO_ERROR
Definition: dns_common.h:95
bool_t ipv6IsOnLink(NetInterface *interface, const Ipv6Addr *ipAddr)
Check whether an IPv6 address is on-link.
Definition: ipv6_misc.c:1029
Data logging functions for debugging purpose (DNS)
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:544
__start_packed struct @0 Ipv6Addr
IPv6 network address.
uint8_t ipAddr[4]
Definition: mib_common.h:187
Ipv4PseudoHeader ipv4Data
Definition: ip.h:103
@ NO_ERROR
Success.
Definition: error.h:44
Debugging facilities.
void mdnsProcessResponse(NetInterface *interface, MdnsMessage *response)
Process mDNS response message.
Definition: mdns_common.c:182
error_t mdnsSendMessage(NetInterface *interface, const MdnsMessage *message, const IpAddr *destIpAddr, uint_t destPort)
Send mDNS message.
Definition: mdns_common.c:458
mDNS responder (Multicast DNS)
#define MDNS_RCLASS_CACHE_FLUSH
Definition: mdns_common.h:62
Ipv4Addr destIpAddr
Definition: ipcp.h:78