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-2019 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 1.9.6
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 "debug.h"
43 
44 //Check TCP/IP stack configuration
45 #if (DNS_CLIENT_SUPPORT == ENABLED || MDNS_CLIENT_SUPPORT == ENABLED || \
46  MDNS_RESPONDER_SUPPORT == ENABLED)
47 
48 
49 /**
50  * @brief Encode a domain name using the DNS name notation
51  * @param[in] src Pointer to the domain name to encode
52  * @param[out] dest Pointer to the encoded domain name (optional parameter)
53  * @return Length of the encoded domain name
54  **/
55 
56 size_t dnsEncodeName(const char_t *src, uint8_t *dest)
57 {
58  uint_t i = 0;
59  size_t length = 0;
60 
61  //Parse input name
62  while(1)
63  {
64  //End of string detected?
65  if(src[i] == '\0')
66  {
67  //Check label length
68  if(i < 1 || i > DNS_LABEL_MAX_SIZE)
69  return 0;
70 
71  //Save label length
72  if(dest != NULL)
73  {
74  dest[0] = i;
75  dest[i + 1] = 0;
76  }
77 
78  //Adjust the length of the resulting string
79  length += i + 2;
80 
81  //Stop parsing the input string
82  return length;
83  }
84  //Separator detected?
85  else if(src[i] == '.')
86  {
87  //Check label length
88  if(i < 1 || i > DNS_LABEL_MAX_SIZE)
89  return 0;
90 
91  //Save label length
92  if(dest != NULL)
93  dest[0] = i;
94 
95  //Adjust the length of the resulting string
96  length += i + 1;
97 
98  //Advance write pointer
99  if(dest != NULL)
100  dest += i + 1;
101 
102  //Prepare to decode the next label
103  src += i + 1;
104  i = 0;
105  }
106  //Any other character?
107  else
108  {
109  //Copy current character
110  if(dest != NULL)
111  dest[i + 1] = src[i];
112 
113  //Point to the next character
114  i++;
115  }
116  }
117 }
118 
119 
120 /**
121  * @brief Decode a domain name that uses the DNS name encoding
122  * @param[in] message Pointer to the DNS message
123  * @param[in] length Length of the DNS message
124  * @param[in] pos Offset of the name to decode
125  * @param[out] dest Pointer to the decoded name (optional)
126  * @param[in] level Current level of recursion
127  * @return The position of the resource record that immediately follows the domain name
128  **/
129 
131  size_t length, size_t pos, char_t *dest, uint_t level)
132 {
133  size_t n;
134  size_t pointer;
135  uint8_t *src;
136 
137  //Recursion limit exceeded?
139  return 0;
140 
141  //Cast the input DNS message to byte array
142  src = (uint8_t *) message;
143 
144  //Parse encoded domain name
145  while(pos < length)
146  {
147  //End marker found?
148  if(src[pos] == 0)
149  {
150  //Properly terminate the string
151  if(dest != NULL)
152  *dest = '\0';
153 
154  //Return the position of the resource record that
155  //is immediately following the domain name
156  return (pos + 1);
157  }
158  //Compression tag found?
159  else if(src[pos] >= DNS_COMPRESSION_TAG)
160  {
161  //Malformed DNS message?
162  if((pos + 1) >= length)
163  return 0;
164 
165  //Read the most significant byte of the pointer
166  pointer = (src[pos] & ~DNS_COMPRESSION_TAG) << 8;
167  //Read the least significant byte of the pointer
168  pointer |= src[pos + 1];
169 
170  //Decode the remaining part of the domain name
171  if(!dnsParseName(message, length, pointer, dest, level + 1))
172  {
173  //Domain name decoding failed
174  return 0;
175  }
176 
177  //Return the position of the resource record that
178  //is immediately following the domain name
179  return (pos + 2);
180  }
181  //Valid label length?
182  else if(src[pos] < DNS_LABEL_MAX_SIZE)
183  {
184  //Get the length of the current label
185  n = src[pos++];
186 
187  //Malformed DNS message?
188  if((pos + n) > length)
189  return 0;
190 
191  //The last parameter is optional
192  if(dest != NULL)
193  {
194  //Copy current label
195  memcpy(dest, src + pos, n);
196 
197  //Advance read pointer
198  pos += n;
199  //Advance write pointer
200  dest += n;
201 
202  //Append a separator if necessary
203  if(pos < length && src[pos] != '\0')
204  *(dest++) = '.';
205  }
206  else
207  {
208  //Advance read pointer
209  pos += n;
210  }
211  }
212  //Invalid label length?
213  else
214  {
215  //Properly terminate the string
216  if(dest != NULL)
217  *dest = '\0';
218  //Domain name decoding failed
219  return 0;
220  }
221  }
222 
223  //Domain name decoding failed
224  return 0;
225 }
226 
227 
228 /**
229  * @brief Compare domain names
230  * @param[in] message Pointer to the DNS message
231  * @param[in] length Length of the DNS message
232  * @param[in] pos Offset of the encoded domain name
233  * @param[in] name NULL-terminated string that holds a domain name
234  * @param[in] level Current level of recursion
235  * @return The function returns 0 if the domain names match, -1 if the first
236  * domain name lexicographically precedes the second name, or 1 if the
237  * second domain name lexicographically precedes the first name
238  **/
239 
241  size_t pos, const char_t *name, uint_t level)
242 {
243  int_t res;
244  size_t n;
245  size_t pointer;
246  uint8_t *p;
247 
248  //Recursion limit exceeded?
250  return -2;
251 
252  //Cast the DNS message to byte array
253  p = (uint8_t *) message;
254 
255  //Parse encoded domain name
256  while(pos < length)
257  {
258  //Retrieve the length of the current label
259  n = p[pos];
260 
261  //End marker found?
262  if(n == 0)
263  {
264  //The domain name which still has remaining data is deemed
265  //lexicographically later
266  if(*name != '\0')
267  return -1;
268 
269  //The domain names match each other
270  return 0;
271  }
272  //Compression tag found?
273  else if(n >= DNS_COMPRESSION_TAG)
274  {
275  //Malformed DNS message?
276  if((pos + 1) >= length)
277  return FALSE;
278 
279  //Read the most significant byte of the pointer
280  pointer = (p[pos] & ~DNS_COMPRESSION_TAG) << 8;
281  //Read the least significant byte of the pointer
282  pointer |= p[pos + 1];
283 
284  //Compare the remaining part
286 
287  //Return comparison result
288  return res;
289  }
290  else
291  {
292  //Advance data pointer
293  pos++;
294 
295  //Malformed DNS message?
296  if((pos + n) > length)
297  return -2;
298 
299  //Compare current label
300  res = strncasecmp((char_t *) p + pos, name, n);
301  //Any mismatch?
302  if(res)
303  return res;
304 
305  //Advance data pointer
306  pos += n;
307  name += n;
308 
309  //The domain name which still has remaining data is deemed
310  //lexicographically later
311  if(*name != '\0' && *name != '.')
312  return -1;
313 
314  //Skip the separator character, if any
315  if(*name == '.')
316  name++;
317  }
318  }
319 
320  //Malformed DNS message
321  return -2;
322 }
323 
324 
325 /**
326  * @brief Compare domain names encoded with DNS notation
327  * @param[in] message1 Pointer to the first DNS message
328  * @param[in] length1 Length of the first DNS message
329  * @param[in] pos1 Offset of the encoded domain name within the first message
330  * @param[in] message2 Pointer to the second DNS message
331  * @param[in] length2 Length of the second DNS message
332  * @param[in] pos2 Offset of the encoded domain name within the second message
333  * @param[in] level Current level of recursion
334  * @return The function returns 0 if the domain names match, -1 if the first
335  * domain name lexicographically precedes the second name, or 1 if the
336  * second domain name lexicographically precedes the first name
337  **/
338 
339 int_t dnsCompareEncodedName(const DnsHeader *message1, size_t length1, size_t pos1,
340  const DnsHeader *message2, size_t length2, size_t pos2, uint_t level)
341 {
342  int_t res;
343  size_t n;
344  size_t n1;
345  size_t n2;
346  size_t pointer1;
347  size_t pointer2;
348  uint8_t *p1;
349  uint8_t *p2;
350 
351  //Recursion limit exceeded?
353  return -2;
354 
355  //Cast DNS messages to byte array
356  p1 = (uint8_t *) message1;
357  p2 = (uint8_t *) message2;
358 
359  //Compare encoded domain names
360  while(pos1 < length1 && pos2 < length2)
361  {
362  //Retrieve the length of each label
363  n1 = p1[pos1];
364  n2 = p2[pos2];
365 
366  //End marker found?
367  if(n1 == 0 || n2 == 0)
368  {
369  //The domain name which still has remaining data is deemed
370  //lexicographically later
371  if(n1 < n2)
372  return -1;
373  else if(n1 > n2)
374  return 1;
375 
376  //The domain names match each other
377  return 0;
378  }
379  //Compression tag found?
380  else if(n1 >= DNS_COMPRESSION_TAG || n2 >= DNS_COMPRESSION_TAG)
381  {
382  //First domain name compressed?
383  if(n1 >= DNS_COMPRESSION_TAG)
384  {
385  //Malformed DNS message?
386  if((pos1 + 1) >= length1)
387  return -2;
388 
389  //Read the most significant byte of the pointer
390  pointer1 = (p1[pos1] & ~DNS_COMPRESSION_TAG) << 8;
391  //Read the least significant byte of the pointer
392  pointer1 |= p1[pos1 + 1];
393  }
394  else
395  {
396  //The first domain name is not compressed
397  pointer1 = pos1;
398  }
399 
400  //Second domain name compressed?
401  if(n2 >= DNS_COMPRESSION_TAG)
402  {
403  //Malformed DNS message?
404  if((pos2 + 1) >= length2)
405  return -2;
406 
407  //Read the most significant byte of the pointer
408  pointer2 = (p2[pos2] & ~DNS_COMPRESSION_TAG) << 8;
409  //Read the least significant byte of the pointer
410  pointer2 |= p2[pos2 + 1];
411  }
412  else
413  {
414  //The second domain name is not compressed
415  pointer2 = pos2;
416  }
417 
418  //Compare the remaining part
419  res = dnsCompareEncodedName(message1, length1, pointer1,
420  message2, length2, pointer2, level + 1);
421 
422  //Return comparison result
423  return res;
424  }
425  else
426  {
427  //Advance data pointer
428  pos1++;
429  pos2++;
430 
431  //Malformed DNS message?
432  if((pos1 + n1) > length1 || (pos2 + n2) > length2)
433  return -2;
434 
435  //Compare as much data as possible
436  n = MIN(n1, n2);
437 
438  //Compare labels
439  res = strncasecmp((char_t *) p1 + pos1, (char_t *) p2 + pos2, n);
440  //Any mismatch?
441  if(res)
442  return res;
443 
444  //The domain name which still has remaining data is deemed
445  //lexicographically later
446  if(n1 < n2)
447  return -1;
448  else if(n1 > n2)
449  return 1;
450 
451  //Advance data pointer
452  pos1 += n1;
453  pos2 += n2;
454  }
455  }
456 
457  //Malformed DNS message
458  return -2;
459 }
460 
461 #endif
uint8_t length
Definition: dtls_misc.h:149
signed int int_t
Definition: compiler_port.h:44
uint8_t p
Definition: ndp.h:298
#define strncasecmp
__start_packed struct @146 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:130
char_t name[]
const uint8_t res[]
Definitions common to mDNS client and mDNS responder.
uint8_t level
Definition: tls.h:1702
#define DNS_NAME_MAX_RECURSION
Definition: dns_common.h:39
#define FALSE
Definition: os_port.h:46
#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:62
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:339
size_t dnsEncodeName(const char_t *src, uint8_t *dest)
Encode a domain name using the DNS name notation.
Definition: dns_common.c:56
DNS client (Domain Name System)
char char_t
Definition: compiler_port.h:43
uint8_t n
uint32_t pointer
Definition: icmp.h:166
uint8_t message[]
Definition: chap.h:152
unsigned int uint_t
Definition: compiler_port.h:45
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:240
Debugging facilities.
mDNS responder (Multicast DNS)