source: trunk/athena/lib/ares/ares_expand_name.c @ 17855

Revision 17855, 4.3 KB checked in by ghudson, 22 years ago (diff)
Fix up length checking to avoid the possibility of pointer overflow; also a couple of cosmetic changes.
Line 
1/* Copyright 1998, 2002 by the Massachusetts Institute of Technology.
2 *
3 * Permission to use, copy, modify, and distribute this
4 * software and its documentation for any purpose and without
5 * fee is hereby granted, provided that the above copyright
6 * notice appear in all copies and that both that copyright
7 * notice and this permission notice appear in supporting
8 * documentation, and that the name of M.I.T. not be used in
9 * advertising or publicity pertaining to distribution of the
10 * software without specific, written prior permission.
11 * M.I.T. makes no representations about the suitability of
12 * this software for any purpose.  It is provided "as is"
13 * without express or implied warranty.
14 */
15
16static const char rcsid[] = "$Id: ares_expand_name.c,v 1.4 2002-08-26 06:48:53 ghudson Exp $";
17
18#include <sys/types.h>
19#include <netinet/in.h>
20#include <arpa/nameser.h>
21#include <stdlib.h>
22#include "ares.h"
23
24static int name_length(const unsigned char *encoded, const unsigned char *abuf,
25                       int alen);
26
27/* Expand an RFC1035-encoded domain name given by encoded.  The
28 * containing message is given by abuf and alen.  The result given by
29 * *s, which is set to a NUL-terminated allocated buffer.  *enclen is
30 * set to the length of the encoded name (not the length of the
31 * expanded name; the goal is to tell the caller how many bytes to
32 * move forward to get past the encoded name).
33 *
34 * In the simple case, an encoded name is a series of labels, each
35 * composed of a one-byte length (limited to values between 0 and 63
36 * inclusive) followed by the label contents.  The name is terminated
37 * by a zero-length label.
38 *
39 * In the more complicated case, a label may be terminated by an
40 * indirection pointer, specified by two bytes with the high bits of
41 * the first byte (corresponding to INDIR_MASK) set to 11.  With the
42 * two high bits of the first byte stripped off, the indirection
43 * pointer gives an offset from the beginning of the containing
44 * message with more labels to decode.  Indirection can happen an
45 * arbitrary number of times, so we have to detect loops.
46 *
47 * Since the expanded name uses '.' as a label separator, we use
48 * backslashes to escape periods or backslashes in the expanded name.
49 */
50
51int ares_expand_name(const unsigned char *encoded, const unsigned char *abuf,
52                     int alen, char **s, int *enclen)
53{
54  int len, indir = 0;
55  char *q;
56  const unsigned char *p;
57
58  len = name_length(encoded, abuf, alen);
59  if (len == -1)
60    return ARES_EBADNAME;
61
62  *s = malloc(len + 1);
63  if (!*s)
64    return ARES_ENOMEM;
65  q = *s;
66
67  /* No error-checking necessary; it was all done by name_length(). */
68  p = encoded;
69  while (*p)
70    {
71      if ((*p & INDIR_MASK) == INDIR_MASK)
72        {
73          if (!indir)
74            *enclen = p + 2 - encoded;
75          indir = 1;
76          p = abuf + ((*p & ~INDIR_MASK) << 8 | *(p + 1));
77        }
78      else
79        {
80          len = *p;
81          p++;
82          while (len--)
83            {
84              if (*p == '.' || *p == '\\')
85                *q++ = '\\';
86              *q++ = *p;
87              p++;
88            }
89          *q++ = '.';
90        }
91    }
92  if (!indir)
93    *enclen = p + 1 - encoded;
94
95  /* Nuke the trailing period if we wrote one. */
96  if (q > *s)
97    *(q - 1) = 0;
98
99  return ARES_SUCCESS;
100}
101
102/* Return the length of the expansion of an encoded domain name, or
103 * -1 if the encoding is invalid.
104 */
105static int name_length(const unsigned char *encoded, const unsigned char *abuf,
106                       int alen)
107{
108  int n = 0, offset, indir = 0;
109
110  /* An encoded domain name must contain at least one byte. */
111  if (encoded == abuf + alen)
112    return -1;
113
114  while (*encoded)
115    {
116      if ((*encoded & INDIR_MASK) == INDIR_MASK)
117        {
118          /* Check the offset and go there. */
119          if (abuf + alen - encoded < 2)
120            return -1;
121          offset = (*encoded & ~INDIR_MASK) << 8 | *(encoded + 1);
122          if (offset >= alen)
123            return -1;
124          encoded = abuf + offset;
125
126          /* If we've seen more indirects than the message length,
127           * then there's a loop.
128           */
129          if (++indir > alen)
130            return -1;
131        }
132      else
133        {
134          offset = *encoded;
135          /* There must be at least one byte for the next label. */
136          if (abuf + alen - encoded < offset + 2)
137            return -1;
138          encoded++;
139          while (offset--)
140            {
141              n += (*encoded == '.' || *encoded == '\\') ? 2 : 1;
142              encoded++;
143            }
144          n++;
145        }
146    }
147
148  /* If there were any labels at all, then the number of dots is one
149   * less than the number of labels, so subtract one.
150   */
151  return (n) ? n - 1 : n;
152}
Note: See TracBrowser for help on using the repository browser.