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-2026 Oryx Embedded SARL. All rights reserved.
10  *
11  * This file is part of CycloneTCP Open.
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26  *
27  * @author Oryx Embedded SARL (www.oryx-embedded.com)
28  * @version 2.6.6
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[in] level Current level of recursion
128  * @return The position of the resource record that immediately follows the domain name
129  **/
130 
131 size_t dnsParseName(const DnsHeader *message, size_t length, size_t pos,
132  uint_t level)
133 {
134  size_t n;
135  size_t pointer;
136  uint8_t *src;
137 
138  //Recursion limit exceeded?
139  if(level >= DNS_NAME_MAX_RECURSION)
140  return 0;
141 
142  //Cast the input DNS message to byte array
143  src = (uint8_t *) message;
144 
145  //Parse encoded domain name
146  while(pos >= sizeof(DnsHeader) && pos < length)
147  {
148  //Check label length
149  if(src[pos] == DNS_END_TAG)
150  {
151  //Return the position of the resource record that is immediately
152  //following the domain name
153  return (pos + 1);
154  }
155  else if(src[pos] >= DNS_COMPRESSION_TAG)
156  {
157  //Malformed DNS message?
158  if((pos + 1) >= length)
159  return 0;
160 
161  //Read the most significant byte of the pointer
162  pointer = (src[pos] & ~DNS_COMPRESSION_TAG) << 8;
163  //Read the least significant byte of the pointer
164  pointer |= src[pos + 1];
165 
166  //Parse the remaining part of the domain name
167  if(!dnsParseName(message, length, pointer, level + 1))
168  {
169  //Domain name decoding failed
170  return 0;
171  }
172 
173  //Return the position of the resource record that is immediately
174  //following the domain name
175  return (pos + 2);
176  }
177  else if(src[pos] <= DNS_LABEL_MAX_SIZE)
178  {
179  //Get the length of the current label
180  n = src[pos++];
181 
182  //Malformed DNS message?
183  if((pos + n) > length)
184  return 0;
185 
186  //Advance read pointer
187  pos += n;
188  }
189  else
190  {
191  //Domain name decoding failed
192  return 0;
193  }
194  }
195 
196  //Domain name decoding failed
197  return 0;
198 }
199 
200 
201 /**
202  * @brief Compare domain names
203  * @param[in] message Pointer to the DNS message
204  * @param[in] length Length of the DNS message
205  * @param[in] pos Offset of the encoded domain name
206  * @param[in] name NULL-terminated string that holds a domain name
207  * @param[in] level Current level of recursion
208  * @return The function returns 0 if the domain names match, -1 if the first
209  * domain name lexicographically precedes the second name, or 1 if the
210  * second domain name lexicographically precedes the first name
211  **/
212 
213 int_t dnsCompareName(const DnsHeader *message, size_t length, size_t pos,
214  const char_t *name, uint_t level)
215 {
216  int_t res;
217  size_t n;
218  size_t pointer;
219  uint8_t *p;
220 
221  //Recursion limit exceeded?
222  if(level >= DNS_NAME_MAX_RECURSION)
223  return -2;
224 
225  //Cast the DNS message to byte array
226  p = (uint8_t *) message;
227 
228  //Parse encoded domain name
229  while(pos >= sizeof(DnsHeader) && pos < length)
230  {
231  //Each label is represented as a one octet length field followed by that
232  //number of octets (refer to RFC 1035, section 3.1)
233  n = p[pos];
234 
235  //Check label length
236  if(n == DNS_END_TAG)
237  {
238  //The domain name which still has remaining data is deemed
239  //lexicographically later
240  if(*name != '\0')
241  return -1;
242 
243  //The domain names match each other
244  return 0;
245  }
246  else if(n >= DNS_COMPRESSION_TAG)
247  {
248  //Malformed DNS message?
249  if((pos + 1) >= length)
250  return FALSE;
251 
252  //Read the most significant byte of the pointer
253  pointer = (p[pos] & ~DNS_COMPRESSION_TAG) << 8;
254  //Read the least significant byte of the pointer
255  pointer |= p[pos + 1];
256 
257  //Compare the remaining part
258  res = dnsCompareName(message, length, pointer, name, level + 1);
259 
260  //Return comparison result
261  return res;
262  }
263  else if(n <= DNS_LABEL_MAX_SIZE)
264  {
265  //Advance data pointer
266  pos++;
267 
268  //Malformed DNS message?
269  if((pos + n) > length)
270  return -2;
271 
272  //Compare current label
273  res = osStrncasecmp((char_t *) p + pos, name, n);
274  //Any mismatch?
275  if(res)
276  return res;
277 
278  //Advance data pointer
279  pos += n;
280  name += n;
281 
282  //The domain name which still has remaining data is deemed
283  //lexicographically later
284  if(*name != '\0' && *name != '.')
285  return -1;
286 
287  //Skip the separator character, if any
288  if(*name == '.')
289  {
290  name++;
291  }
292  }
293  else
294  {
295  //Malformed DNS message
296  return -2;
297  }
298  }
299 
300  //Malformed DNS message
301  return -2;
302 }
303 
304 
305 /**
306  * @brief Compare domain names encoded with DNS notation
307  * @param[in] message1 Pointer to the first DNS message
308  * @param[in] length1 Length of the first DNS message
309  * @param[in] pos1 Offset of the encoded domain name within the first message
310  * @param[in] message2 Pointer to the second DNS message
311  * @param[in] length2 Length of the second DNS message
312  * @param[in] pos2 Offset of the encoded domain name within the second message
313  * @param[in] level Current level of recursion
314  * @return The function returns 0 if the domain names match, -1 if the first
315  * domain name lexicographically precedes the second name, or 1 if the
316  * second domain name lexicographically precedes the first name
317  **/
318 
319 int_t dnsCompareEncodedName(const DnsHeader *message1, size_t length1,
320  size_t pos1, const DnsHeader *message2, size_t length2, size_t pos2,
321  uint_t level)
322 {
323  int_t res;
324  size_t n;
325  size_t n1;
326  size_t n2;
327  size_t pointer1;
328  size_t pointer2;
329  uint8_t *p1;
330  uint8_t *p2;
331 
332  //Recursion limit exceeded?
333  if(level >= DNS_NAME_MAX_RECURSION)
334  return -2;
335 
336  //Cast DNS messages to byte array
337  p1 = (uint8_t *) message1;
338  p2 = (uint8_t *) message2;
339 
340  //Compare encoded domain names
341  while(pos1 >= sizeof(DnsHeader) && pos1 < length1 &&
342  pos2 >= sizeof(DnsHeader) && pos2 < length2)
343  {
344  //Each label is represented as a one octet length field followed by that
345  //number of octets (refer to RFC 1035, section 3.1)
346  n1 = p1[pos1];
347  n2 = p2[pos2];
348 
349  //Check label length
350  if(n1 == DNS_END_TAG || n2 == DNS_END_TAG)
351  {
352  //The domain name which still has remaining data is deemed
353  //lexicographically later
354  if(n1 < n2)
355  {
356  return -1;
357  }
358  else if(n1 > n2)
359  {
360  return 1;
361  }
362 
363  //The domain names match each other
364  return 0;
365  }
366  else if(n1 >= DNS_COMPRESSION_TAG || n2 >= DNS_COMPRESSION_TAG)
367  {
368  //First domain name compressed?
369  if(n1 >= DNS_COMPRESSION_TAG)
370  {
371  //Malformed DNS message?
372  if((pos1 + 1) >= length1)
373  return -2;
374 
375  //Read the most significant byte of the pointer
376  pointer1 = (p1[pos1] & ~DNS_COMPRESSION_TAG) << 8;
377  //Read the least significant byte of the pointer
378  pointer1 |= p1[pos1 + 1];
379  }
380  else
381  {
382  //The first domain name is not compressed
383  pointer1 = pos1;
384  }
385 
386  //Second domain name compressed?
387  if(n2 >= DNS_COMPRESSION_TAG)
388  {
389  //Malformed DNS message?
390  if((pos2 + 1) >= length2)
391  return -2;
392 
393  //Read the most significant byte of the pointer
394  pointer2 = (p2[pos2] & ~DNS_COMPRESSION_TAG) << 8;
395  //Read the least significant byte of the pointer
396  pointer2 |= p2[pos2 + 1];
397  }
398  else
399  {
400  //The second domain name is not compressed
401  pointer2 = pos2;
402  }
403 
404  //Compare the remaining part
405  res = dnsCompareEncodedName(message1, length1, pointer1,
406  message2, length2, pointer2, level + 1);
407 
408  //Return comparison result
409  return res;
410  }
411  else if(n1 <= DNS_LABEL_MAX_SIZE && n2 <= DNS_LABEL_MAX_SIZE)
412  {
413  //Advance data pointer
414  pos1++;
415  pos2++;
416 
417  //Malformed DNS message?
418  if((pos1 + n1) > length1 || (pos2 + n2) > length2)
419  return -2;
420 
421  //Compare as much data as possible
422  n = MIN(n1, n2);
423 
424  //Compare labels
425  res = osStrncasecmp((char_t *) p1 + pos1, (char_t *) p2 + pos2, n);
426  //Any mismatch?
427  if(res)
428  return res;
429 
430  //The domain name which still has remaining data is deemed
431  //lexicographically later
432  if(n1 < n2)
433  {
434  return -1;
435  }
436  else if(n1 > n2)
437  {
438  return 1;
439  }
440 
441  //Advance data pointer
442  pos1 += n1;
443  pos2 += n2;
444  }
445  else
446  {
447  //Malformed DNS message
448  return -2;
449  }
450  }
451 
452  //Malformed DNS message
453  return -2;
454 }
455 
456 
457 /**
458  * @brief Generate domain name for reverse DNS lookup (IPv4)
459  * @param[in] ipv4Addr IPv4 address
460  * @param[out] buffer Output buffer where to store the resulting domain name
461  **/
462 
464 {
465  uint8_t *p;
466 
467  //Cast the IPv4 address as byte array
468  p = (uint8_t *) &ipv4Addr;
469 
470  //The decimal numbers are concatenated in the reverse order
471  osSprintf(buffer, "%" PRIu8 ".%" PRIu8 ".%" PRIu8 ".%" PRIu8,
472  p[3], p[2], p[1], p[0]);
473 }
474 
475 
476 /**
477  * @brief Generate domain name for reverse DNS lookup (IPv6)
478  * @param[in] ipv6Addr IPv6 address
479  * @param[out] buffer Output buffer where to store the resulting domain name
480  **/
481 
482 void dnsGenerateIpv6ReverseName(const Ipv6Addr *ipv6Addr, char_t *buffer)
483 {
484  uint_t i;
485  uint_t m;
486  uint_t n;
487  uint8_t digit;
488 
489  //Generate the domain name for reverse DNS lookup
490  for(i = 0; i < 32; i++)
491  {
492  //Calculate the shift count
493  n = (31 - i) / 2;
494  m = (i % 2) * 4;
495 
496  //Extract current nibble
497  digit = (ipv6Addr->b[n] >> m) & 0x0F;
498  //The nibbles are concatenated in the reverse order
499  buffer += osSprintf(buffer, "%" PRIx8, digit);
500 
501  //Add a delimiter character
502  if(i != 31)
503  {
504  buffer += osSprintf(buffer, ".");
505  }
506  }
507 }
508 
509 #endif
size_t dnsParseName(const DnsHeader *message, size_t length, size_t pos, uint_t level)
Decode a domain name that uses the DNS name encoding.
Definition: dns_common.c:131
uint8_t pointer
Definition: icmp.h:234
signed int int_t
Definition: compiler_port.h:56
uint8_t p
Definition: ndp.h:300
#define DNS_END_TAG
Definition: dns_common.h:60
uint8_t message[]
Definition: chap.h:154
Ipv6Addr
Definition: ipv6.h:282
char_t name[]
DnsHeader
Definition: dns_common.h:204
const uint8_t res[]
uint32_t Ipv4Addr
IPv4 network address.
Definition: ipv4.h:324
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:195
#define osSprintf(dest,...)
Definition: os_port.h:237
#define DNS_LABEL_MAX_SIZE
Definition: dns_common.h:49
#define DNS_COMPRESSION_TAG
Definition: dns_common.h:62
mDNS client (Multicast DNS)
uint8_t length
Definition: tcp.h:375
#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:463
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:319
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:55
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:482
unsigned int uint_t
Definition: compiler_port.h:57
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:213
Debugging facilities.
mDNS responder (Multicast DNS)