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