dns_common.c
Go to the documentation of this file.
1 /**
2  * @file dns_common.c
3  * @brief Common DNS routines
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  * @author Oryx Embedded SARL (www.oryx-embedded.com)
28  * @version 2.2.4
29  **/
30 
31 //Switch to the appropriate trace level
32 #define TRACE_LEVEL DNS_TRACE_LEVEL
33 
34 //Dependencies
35 #include <string.h>
36 #include "core/net.h"
37 #include "dns/dns_client.h"
38 #include "dns/dns_common.h"
39 #include "mdns/mdns_client.h"
40 #include "mdns/mdns_responder.h"
41 #include "mdns/mdns_common.h"
42 #include "llmnr/llmnr_client.h"
43 #include "llmnr/llmnr_responder.h"
44 #include "debug.h"
45 
46 //Check TCP/IP stack configuration
47 #if (DNS_CLIENT_SUPPORT == ENABLED || MDNS_CLIENT_SUPPORT == ENABLED || \
48  MDNS_RESPONDER_SUPPORT == ENABLED || LLMNR_CLIENT_SUPPORT == ENABLED || \
49  LLMNR_RESPONDER_SUPPORT == ENABLED)
50 
51 
52 /**
53  * @brief Encode a domain name using the DNS name notation
54  * @param[in] src Pointer to the domain name to encode
55  * @param[out] dest Pointer to the encoded domain name (optional parameter)
56  * @return Length of the encoded domain name
57  **/
58 
59 size_t dnsEncodeName(const char_t *src, uint8_t *dest)
60 {
61  uint_t i = 0;
62  size_t length = 0;
63 
64  //Parse input name
65  while(1)
66  {
67  //End of string detected?
68  if(src[i] == '\0')
69  {
70  //Check label length
71  if(i < 1 || i > DNS_LABEL_MAX_SIZE)
72  return 0;
73 
74  //Save label length
75  if(dest != NULL)
76  {
77  dest[0] = i;
78  dest[i + 1] = 0;
79  }
80 
81  //Adjust the length of the resulting string
82  length += i + 2;
83 
84  //Stop parsing the input string
85  return length;
86  }
87  //Separator detected?
88  else if(src[i] == '.')
89  {
90  //Check label length
91  if(i < 1 || i > DNS_LABEL_MAX_SIZE)
92  return 0;
93 
94  //Save label length
95  if(dest != NULL)
96  dest[0] = i;
97 
98  //Adjust the length of the resulting string
99  length += i + 1;
100 
101  //Advance write pointer
102  if(dest != NULL)
103  dest += i + 1;
104 
105  //Prepare to decode the next label
106  src += i + 1;
107  i = 0;
108  }
109  //Any other character?
110  else
111  {
112  //Copy current character
113  if(dest != NULL)
114  dest[i + 1] = src[i];
115 
116  //Point to the next character
117  i++;
118  }
119  }
120 }
121 
122 
123 /**
124  * @brief Decode a domain name that uses the DNS name encoding
125  * @param[in] message Pointer to the DNS message
126  * @param[in] length Length of the DNS message
127  * @param[in] pos Offset of the name to decode
128  * @param[out] dest Pointer to the decoded name (optional)
129  * @param[in] level Current level of recursion
130  * @return The position of the resource record that immediately follows the domain name
131  **/
132 
134  size_t length, size_t pos, char_t *dest, uint_t level)
135 {
136  size_t n;
137  size_t pointer;
138  uint8_t *src;
139 
140  //Recursion limit exceeded?
142  return 0;
143 
144  //Cast the input DNS message to byte array
145  src = (uint8_t *) message;
146 
147  //Parse encoded domain name
148  while(pos < length)
149  {
150  //End marker found?
151  if(src[pos] == 0)
152  {
153  //Properly terminate the string
154  if(dest != NULL)
155  *dest = '\0';
156 
157  //Return the position of the resource record that
158  //is immediately following the domain name
159  return (pos + 1);
160  }
161  //Compression tag found?
162  else if(src[pos] >= DNS_COMPRESSION_TAG)
163  {
164  //Malformed DNS message?
165  if((pos + 1) >= length)
166  return 0;
167 
168  //Read the most significant byte of the pointer
169  pointer = (src[pos] & ~DNS_COMPRESSION_TAG) << 8;
170  //Read the least significant byte of the pointer
171  pointer |= src[pos + 1];
172 
173  //Decode the remaining part of the domain name
174  if(!dnsParseName(message, length, pointer, dest, level + 1))
175  {
176  //Domain name decoding failed
177  return 0;
178  }
179 
180  //Return the position of the resource record that
181  //is immediately following the domain name
182  return (pos + 2);
183  }
184  //Valid label length?
185  else if(src[pos] < DNS_LABEL_MAX_SIZE)
186  {
187  //Get the length of the current label
188  n = src[pos++];
189 
190  //Malformed DNS message?
191  if((pos + n) > length)
192  return 0;
193 
194  //The last parameter is optional
195  if(dest != NULL)
196  {
197  //Copy current label
198  osMemcpy(dest, src + pos, n);
199 
200  //Advance read pointer
201  pos += n;
202  //Advance write pointer
203  dest += n;
204 
205  //Append a separator if necessary
206  if(pos < length && src[pos] != '\0')
207  *(dest++) = '.';
208  }
209  else
210  {
211  //Advance read pointer
212  pos += n;
213  }
214  }
215  //Invalid label length?
216  else
217  {
218  //Properly terminate the string
219  if(dest != NULL)
220  *dest = '\0';
221  //Domain name decoding failed
222  return 0;
223  }
224  }
225 
226  //Domain name decoding failed
227  return 0;
228 }
229 
230 
231 /**
232  * @brief Compare domain names
233  * @param[in] message Pointer to the DNS message
234  * @param[in] length Length of the DNS message
235  * @param[in] pos Offset of the encoded domain name
236  * @param[in] name NULL-terminated string that holds a domain name
237  * @param[in] level Current level of recursion
238  * @return The function returns 0 if the domain names match, -1 if the first
239  * domain name lexicographically precedes the second name, or 1 if the
240  * second domain name lexicographically precedes the first name
241  **/
242 
244  size_t pos, const char_t *name, uint_t level)
245 {
246  int_t res;
247  size_t n;
248  size_t pointer;
249  uint8_t *p;
250 
251  //Recursion limit exceeded?
253  return -2;
254 
255  //Cast the DNS message to byte array
256  p = (uint8_t *) message;
257 
258  //Parse encoded domain name
259  while(pos < length)
260  {
261  //Retrieve the length of the current label
262  n = p[pos];
263 
264  //End marker found?
265  if(n == 0)
266  {
267  //The domain name which still has remaining data is deemed
268  //lexicographically later
269  if(*name != '\0')
270  return -1;
271 
272  //The domain names match each other
273  return 0;
274  }
275  //Compression tag found?
276  else if(n >= DNS_COMPRESSION_TAG)
277  {
278  //Malformed DNS message?
279  if((pos + 1) >= length)
280  return FALSE;
281 
282  //Read the most significant byte of the pointer
283  pointer = (p[pos] & ~DNS_COMPRESSION_TAG) << 8;
284  //Read the least significant byte of the pointer
285  pointer |= p[pos + 1];
286 
287  //Compare the remaining part
289 
290  //Return comparison result
291  return res;
292  }
293  else
294  {
295  //Advance data pointer
296  pos++;
297 
298  //Malformed DNS message?
299  if((pos + n) > length)
300  return -2;
301 
302  //Compare current label
303  res = osStrncasecmp((char_t *) p + pos, name, n);
304  //Any mismatch?
305  if(res)
306  return res;
307 
308  //Advance data pointer
309  pos += n;
310  name += n;
311 
312  //The domain name which still has remaining data is deemed
313  //lexicographically later
314  if(*name != '\0' && *name != '.')
315  return -1;
316 
317  //Skip the separator character, if any
318  if(*name == '.')
319  name++;
320  }
321  }
322 
323  //Malformed DNS message
324  return -2;
325 }
326 
327 
328 /**
329  * @brief Compare domain names encoded with DNS notation
330  * @param[in] message1 Pointer to the first DNS message
331  * @param[in] length1 Length of the first DNS message
332  * @param[in] pos1 Offset of the encoded domain name within the first message
333  * @param[in] message2 Pointer to the second DNS message
334  * @param[in] length2 Length of the second DNS message
335  * @param[in] pos2 Offset of the encoded domain name within the second message
336  * @param[in] level Current level of recursion
337  * @return The function returns 0 if the domain names match, -1 if the first
338  * domain name lexicographically precedes the second name, or 1 if the
339  * second domain name lexicographically precedes the first name
340  **/
341 
342 int_t dnsCompareEncodedName(const DnsHeader *message1, size_t length1, size_t pos1,
343  const DnsHeader *message2, size_t length2, size_t pos2, uint_t level)
344 {
345  int_t res;
346  size_t n;
347  size_t n1;
348  size_t n2;
349  size_t pointer1;
350  size_t pointer2;
351  uint8_t *p1;
352  uint8_t *p2;
353 
354  //Recursion limit exceeded?
356  return -2;
357 
358  //Cast DNS messages to byte array
359  p1 = (uint8_t *) message1;
360  p2 = (uint8_t *) message2;
361 
362  //Compare encoded domain names
363  while(pos1 < length1 && pos2 < length2)
364  {
365  //Retrieve the length of each label
366  n1 = p1[pos1];
367  n2 = p2[pos2];
368 
369  //End marker found?
370  if(n1 == 0 || n2 == 0)
371  {
372  //The domain name which still has remaining data is deemed
373  //lexicographically later
374  if(n1 < n2)
375  return -1;
376  else if(n1 > n2)
377  return 1;
378 
379  //The domain names match each other
380  return 0;
381  }
382  //Compression tag found?
383  else if(n1 >= DNS_COMPRESSION_TAG || n2 >= DNS_COMPRESSION_TAG)
384  {
385  //First domain name compressed?
386  if(n1 >= DNS_COMPRESSION_TAG)
387  {
388  //Malformed DNS message?
389  if((pos1 + 1) >= length1)
390  return -2;
391 
392  //Read the most significant byte of the pointer
393  pointer1 = (p1[pos1] & ~DNS_COMPRESSION_TAG) << 8;
394  //Read the least significant byte of the pointer
395  pointer1 |= p1[pos1 + 1];
396  }
397  else
398  {
399  //The first domain name is not compressed
400  pointer1 = pos1;
401  }
402 
403  //Second domain name compressed?
404  if(n2 >= DNS_COMPRESSION_TAG)
405  {
406  //Malformed DNS message?
407  if((pos2 + 1) >= length2)
408  return -2;
409 
410  //Read the most significant byte of the pointer
411  pointer2 = (p2[pos2] & ~DNS_COMPRESSION_TAG) << 8;
412  //Read the least significant byte of the pointer
413  pointer2 |= p2[pos2 + 1];
414  }
415  else
416  {
417  //The second domain name is not compressed
418  pointer2 = pos2;
419  }
420 
421  //Compare the remaining part
422  res = dnsCompareEncodedName(message1, length1, pointer1,
423  message2, length2, pointer2, level + 1);
424 
425  //Return comparison result
426  return res;
427  }
428  else
429  {
430  //Advance data pointer
431  pos1++;
432  pos2++;
433 
434  //Malformed DNS message?
435  if((pos1 + n1) > length1 || (pos2 + n2) > length2)
436  return -2;
437 
438  //Compare as much data as possible
439  n = MIN(n1, n2);
440 
441  //Compare labels
442  res = osStrncasecmp((char_t *) p1 + pos1, (char_t *) p2 + pos2, n);
443  //Any mismatch?
444  if(res)
445  return res;
446 
447  //The domain name which still has remaining data is deemed
448  //lexicographically later
449  if(n1 < n2)
450  return -1;
451  else if(n1 > n2)
452  return 1;
453 
454  //Advance data pointer
455  pos1 += n1;
456  pos2 += n2;
457  }
458  }
459 
460  //Malformed DNS message
461  return -2;
462 }
463 
464 
465 /**
466  * @brief Generate domain name for reverse DNS lookup (IPv4)
467  * @param[in] ipv4Addr IPv4 address
468  * @param[out] buffer Output buffer where to store the resulting domain name
469  **/
470 
472 {
473  uint8_t *p;
474 
475  //Cast the IPv4 address as byte array
476  p = (uint8_t *) &ipv4Addr;
477 
478  //The decimal numbers are concatenated in the reverse order
479  osSprintf(buffer, "%" PRIu8 ".%" PRIu8 ".%" PRIu8 ".%" PRIu8,
480  p[3], p[2], p[1], p[0]);
481 }
482 
483 
484 /**
485  * @brief Generate domain name for reverse DNS lookup (IPv6)
486  * @param[in] ipv6Addr IPv6 address
487  * @param[out] buffer Output buffer where to store the resulting domain name
488  **/
489 
490 void dnsGenerateIpv6ReverseName(const Ipv6Addr *ipv6Addr, char_t *buffer)
491 {
492  uint_t i;
493  uint_t m;
494  uint_t n;
495  uint8_t digit;
496 
497  //Generate the domain name for reverse DNS lookup
498  for(i = 0; i < 32; i++)
499  {
500  //Calculate the shift count
501  n = (31 - i) / 2;
502  m = (i % 2) * 4;
503 
504  //Extract current nibble
505  digit = (ipv6Addr->b[n] >> m) & 0x0F;
506  //The nibbles are concatenated in the reverse order
507  buffer += osSprintf(buffer, "%" PRIx8, digit);
508 
509  //Add a delimiter character
510  if(i != 31)
511  {
512  buffer += osSprintf(buffer, ".");
513  }
514  }
515 }
516 
517 #endif
uint8_t length
Definition: coap_common.h:193
uint8_t pointer
Definition: icmp.h:166
signed int int_t
Definition: compiler_port.h:49
uint8_t p
Definition: ndp.h:298
__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
char_t name[]
const uint8_t res[]
uint32_t Ipv4Addr
IPv4 network address.
Definition: ipv4.h:268
Definitions common to mDNS client and mDNS responder.
uint8_t level
Definition: tls.h:1800
#define DNS_NAME_MAX_RECURSION
Definition: dns_common.h:39
#define FALSE
Definition: os_port.h:48
#define osStrncasecmp(s1, s2, length)
Definition: os_port.h:188
#define osMemcpy(dest, src, length)
Definition: os_port.h:140
#define osSprintf(dest,...)
Definition: os_port.h:230
#define DNS_LABEL_MAX_SIZE
Definition: dns_common.h:49
#define DNS_COMPRESSION_TAG
Definition: dns_common.h:60
mDNS client (Multicast DNS)
#define MIN(a, b)
Definition: os_port.h:65
void dnsGenerateIpv4ReverseName(Ipv4Addr ipv4Addr, char_t *buffer)
Generate domain name for reverse DNS lookup (IPv4)
Definition: dns_common.c:471
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
DNS client (Domain Name System)
char char_t
Definition: compiler_port.h:48
LLMNR client (Link-Local Multicast Name Resolution)
uint8_t m
Definition: ndp.h:302
uint8_t n
uint8_t message[]
Definition: chap.h:152
LLMNR responder (Link-Local Multicast Name Resolution)
void dnsGenerateIpv6ReverseName(const Ipv6Addr *ipv6Addr, char_t *buffer)
Generate domain name for reverse DNS lookup (IPv6)
Definition: dns_common.c:490
unsigned int uint_t
Definition: compiler_port.h:50
TCP/IP stack core.
Common DNS routines.
int_t dnsCompareName(const DnsHeader *message, size_t length, size_t pos, const char_t *name, uint_t level)
Compare domain names.
Definition: dns_common.c:243
__start_packed struct @0 Ipv6Addr
IPv6 network address.
Debugging facilities.
mDNS responder (Multicast DNS)