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