ares_expand_name.c
Go to the documentation of this file.
1 
2 /* Copyright 1998, 2011 by the Massachusetts Institute of Technology.
3  *
4  * Permission to use, copy, modify, and distribute this
5  * software and its documentation for any purpose and without
6  * fee is hereby granted, provided that the above copyright
7  * notice appear in all copies and that both that copyright
8  * notice and this permission notice appear in supporting
9  * documentation, and that the name of M.I.T. not be used in
10  * advertising or publicity pertaining to distribution of the
11  * software without specific, written prior permission.
12  * M.I.T. makes no representations about the suitability of
13  * this software for any purpose. It is provided "as is"
14  * without express or implied warranty.
15  */
16 
17 #include "ares_setup.h"
18 
19 #ifdef HAVE_NETINET_IN_H
20 # include <netinet/in.h>
21 #endif
22 
23 #include "ares_nameser.h"
24 
25 #include "ares.h"
26 #include "ares_nowarn.h"
27 #include "ares_private.h" /* for the memdebug */
28 
29 /* Maximum number of indirections allowed for a name */
30 #define MAX_INDIRS 50
31 
32 static int name_length(const unsigned char *encoded, const unsigned char *abuf,
33  int alen, int is_hostname);
34 
35 /* Reserved characters for names that need to be escaped */
36 static int is_reservedch(int ch)
37 {
38  switch (ch) {
39  case '"':
40  case '.':
41  case ';':
42  case '\\':
43  case '(':
44  case ')':
45  case '@':
46  case '$':
47  return 1;
48  default:
49  break;
50  }
51 
52  return 0;
53 }
54 
55 static int ares__isprint(int ch)
56 {
57  if (ch >= 0x20 && ch <= 0x7E)
58  return 1;
59  return 0;
60 }
61 
62 /* Character set allowed by hostnames */
63 static int is_hostnamech(int ch)
64 {
65  /* [A-Za-z0-9-.]
66  * Don't use isalnum() as it is locale-specific
67  */
68  if (ch >= 'A' && ch <= 'Z')
69  return 1;
70  if (ch >= 'a' && ch <= 'z')
71  return 1;
72  if (ch >= '0' && ch <= '9')
73  return 1;
74  if (ch == '-' || ch == '.')
75  return 1;
76 
77  return 0;
78 }
79 
80 /* Expand an RFC1035-encoded domain name given by encoded. The
81  * containing message is given by abuf and alen. The result given by
82  * *s, which is set to a NUL-terminated allocated buffer. *enclen is
83  * set to the length of the encoded name (not the length of the
84  * expanded name; the goal is to tell the caller how many bytes to
85  * move forward to get past the encoded name).
86  *
87  * In the simple case, an encoded name is a series of labels, each
88  * composed of a one-byte length (limited to values between 0 and 63
89  * inclusive) followed by the label contents. The name is terminated
90  * by a zero-length label.
91  *
92  * In the more complicated case, a label may be terminated by an
93  * indirection pointer, specified by two bytes with the high bits of
94  * the first byte (corresponding to INDIR_MASK) set to 11. With the
95  * two high bits of the first byte stripped off, the indirection
96  * pointer gives an offset from the beginning of the containing
97  * message with more labels to decode. Indirection can happen an
98  * arbitrary number of times, so we have to detect loops.
99  *
100  * Since the expanded name uses '.' as a label separator, we use
101  * backslashes to escape periods or backslashes in the expanded name.
102  *
103  * If the result is expected to be a hostname, then no escaped data is allowed
104  * and will return error.
105  */
106 
107 int ares__expand_name_validated(const unsigned char *encoded,
108  const unsigned char *abuf,
109  int alen, char **s, long *enclen,
110  int is_hostname)
111 {
112  int len, indir = 0;
113  char *q;
114  const unsigned char *p;
115  union {
116  ares_ssize_t sig;
117  size_t uns;
118  } nlen;
119 
120  nlen.sig = name_length(encoded, abuf, alen, is_hostname);
121  if (nlen.sig < 0)
122  return ARES_EBADNAME;
123 
124  *s = ares_malloc(nlen.uns + 1);
125  if (!*s)
126  return ARES_ENOMEM;
127  q = *s;
128 
129  if (nlen.uns == 0) {
130  /* RFC2181 says this should be ".": the root of the DNS tree.
131  * Since this function strips trailing dots though, it becomes ""
132  */
133  q[0] = '\0';
134 
135  /* indirect root label (like 0xc0 0x0c) is 2 bytes long (stupid, but
136  valid) */
137  if ((*encoded & INDIR_MASK) == INDIR_MASK)
138  *enclen = 2L;
139  else
140  *enclen = 1L; /* the caller should move one byte to get past this */
141 
142  return ARES_SUCCESS;
143  }
144 
145  /* No error-checking necessary; it was all done by name_length(). */
146  p = encoded;
147  while (*p)
148  {
149  if ((*p & INDIR_MASK) == INDIR_MASK)
150  {
151  if (!indir)
152  {
153  *enclen = aresx_uztosl(p + 2U - encoded);
154  indir = 1;
155  }
156  p = abuf + ((*p & ~INDIR_MASK) << 8 | *(p + 1));
157  }
158  else
159  {
160  int name_len = *p;
161  len = name_len;
162  p++;
163 
164  while (len--)
165  {
166  /* Output as \DDD for consistency with RFC1035 5.1, except
167  * for the special case of a root name response */
168  if (!ares__isprint(*p) && !(name_len == 1 && *p == 0))
169  {
170  *q++ = '\\';
171  *q++ = '0' + *p / 100;
172  *q++ = '0' + (*p % 100) / 10;
173  *q++ = '0' + (*p % 10);
174  }
175  else if (is_reservedch(*p))
176  {
177  *q++ = '\\';
178  *q++ = *p;
179  }
180  else
181  {
182  *q++ = *p;
183  }
184  p++;
185  }
186  *q++ = '.';
187  }
188  }
189 
190  if (!indir)
191  *enclen = aresx_uztosl(p + 1U - encoded);
192 
193  /* Nuke the trailing period if we wrote one. */
194  if (q > *s)
195  *(q - 1) = 0;
196  else
197  *q = 0; /* zero terminate; LCOV_EXCL_LINE: empty names exit above */
198 
199  return ARES_SUCCESS;
200 }
201 
202 
203 int ares_expand_name(const unsigned char *encoded, const unsigned char *abuf,
204  int alen, char **s, long *enclen)
205 {
206  return ares__expand_name_validated(encoded, abuf, alen, s, enclen, 0);
207 }
208 
209 /* Return the length of the expansion of an encoded domain name, or
210  * -1 if the encoding is invalid.
211  */
212 static int name_length(const unsigned char *encoded, const unsigned char *abuf,
213  int alen, int is_hostname)
214 {
215  int n = 0, offset, indir = 0, top;
216 
217  /* Allow the caller to pass us abuf + alen and have us check for it. */
218  if (encoded >= abuf + alen)
219  return -1;
220 
221  while (*encoded)
222  {
223  top = (*encoded & INDIR_MASK);
224  if (top == INDIR_MASK)
225  {
226  /* Check the offset and go there. */
227  if (encoded + 1 >= abuf + alen)
228  return -1;
229  offset = (*encoded & ~INDIR_MASK) << 8 | *(encoded + 1);
230  if (offset >= alen)
231  return -1;
232  encoded = abuf + offset;
233 
234  /* If we've seen more indirects than the message length,
235  * then there's a loop.
236  */
237  ++indir;
238  if (indir > alen || indir > MAX_INDIRS)
239  return -1;
240  }
241  else if (top == 0x00)
242  {
243  int name_len = *encoded;
244  offset = name_len;
245  if (encoded + offset + 1 >= abuf + alen)
246  return -1;
247  encoded++;
248 
249  while (offset--)
250  {
251  if (!ares__isprint(*encoded) && !(name_len == 1 && *encoded == 0))
252  {
253  if (is_hostname)
254  return -1;
255  n += 4;
256  }
257  else if (is_reservedch(*encoded))
258  {
259  if (is_hostname)
260  return -1;
261  n += 2;
262  }
263  else
264  {
265  if (is_hostname && !is_hostnamech(*encoded))
266  return -1;
267  n += 1;
268  }
269  encoded++;
270  }
271 
272  n++;
273  }
274  else
275  {
276  /* RFC 1035 4.1.4 says other options (01, 10) for top 2
277  * bits are reserved.
278  */
279  return -1;
280  }
281  }
282 
283  /* If there were any labels at all, then the number of dots is one
284  * less than the number of labels, so subtract one.
285  */
286  return (n) ? n - 1 : n;
287 }
288 
289 /* Like ares_expand_name_validated but returns EBADRESP in case of invalid
290  * input. */
291 int ares__expand_name_for_response(const unsigned char *encoded,
292  const unsigned char *abuf, int alen,
293  char **s, long *enclen, int is_hostname)
294 {
295  int status = ares__expand_name_validated(encoded, abuf, alen, s, enclen,
296  is_hostname);
297  if (status == ARES_EBADNAME)
299  return status;
300 }
ARES_ENOMEM
#define ARES_ENOMEM
Definition: ares.h:117
ares__expand_name_validated
int ares__expand_name_validated(const unsigned char *encoded, const unsigned char *abuf, int alen, char **s, long *enclen, int is_hostname)
Definition: ares_expand_name.c:107
is_reservedch
static int is_reservedch(int ch)
Definition: ares_expand_name.c:36
ares.h
INDIR_MASK
#define INDIR_MASK
Definition: ares_nameser.h:215
ares__expand_name_for_response
int ares__expand_name_for_response(const unsigned char *encoded, const unsigned char *abuf, int alen, char **s, long *enclen, int is_hostname)
Definition: ares_expand_name.c:291
status
absl::Status status
Definition: rls.cc:251
xds_manager.p
p
Definition: xds_manager.py:60
ARES_EBADRESP
#define ARES_EBADRESP
Definition: ares.h:112
ares_malloc
void *(* ares_malloc)(size_t size)=default_malloc
Definition: ares_library_init.c:58
ares_expand_name
int ares_expand_name(const unsigned char *encoded, const unsigned char *abuf, int alen, char **s, long *enclen)
Definition: ares_expand_name.c:203
ARES_EBADNAME
#define ARES_EBADNAME
Definition: ares.h:110
ARES_SUCCESS
#define ARES_SUCCESS
Definition: ares.h:98
n
int n
Definition: abseil-cpp/absl/container/btree_test.cc:1080
aresx_uztosl
long aresx_uztosl(size_t uznum)
Definition: ares_nowarn.c:65
ares_setup.h
L
lua_State * L
Definition: upb/upb/bindings/lua/main.c:35
MAX_INDIRS
#define MAX_INDIRS
Definition: ares_expand_name.c:30
ares_private.h
ares__isprint
static int ares__isprint(int ch)
Definition: ares_expand_name.c:55
is_hostnamech
static int is_hostnamech(int ch)
Definition: ares_expand_name.c:63
ch
char ch
Definition: bloaty/third_party/googletest/googlemock/test/gmock-matchers_test.cc:3621
len
int len
Definition: abseil-cpp/absl/base/internal/low_level_alloc_test.cc:46
ares_nameser.h
name_length
static int name_length(const unsigned char *encoded, const unsigned char *abuf, int alen, int is_hostname)
Definition: ares_expand_name.c:212
ares_ssize_t
CARES_TYPEOF_ARES_SSIZE_T ares_ssize_t
Definition: ares_build.h:210
ares_nowarn.h
top
static upb_pb_encoder_segment * top(upb_pb_encoder *e)
Definition: bloaty/third_party/protobuf/php/ext/google/protobuf/upb.c:7624
offset
voidpf uLong offset
Definition: bloaty/third_party/zlib/contrib/minizip/ioapi.h:142


grpc
Author(s):
autogenerated on Fri May 16 2025 02:57:43