source: MondoRescue/branches/3.3/mindi-busybox/networking/udhcp/domain_codec.c@ 3803

Last change on this file since 3803 was 3621, checked in by Bruno Cornec, 10 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

  • Property svn:eol-style set to native
File size: 6.9 KB
RevLine 
[1765]1/* vi: set sw=4 ts=4: */
2
3/* RFC1035 domain compression routines (C) 2007 Gabriel Somlo <somlo at cmu.edu>
4 *
5 * Loosely based on the isc-dhcpd implementation by dhankins@isc.org
6 *
[2725]7 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
[1765]8 */
[2725]9#ifdef DNS_COMPR_TESTING
[3621]10# define _GNU_SOURCE
[2725]11# define FAST_FUNC /* nothing */
12# define xmalloc malloc
13# include <stdlib.h>
14# include <stdint.h>
15# include <string.h>
16# include <stdio.h>
17#else
18# include "common.h"
19#endif
[1765]20
21#define NS_MAXDNAME 1025 /* max domain name length */
22#define NS_MAXCDNAME 255 /* max compressed domain name length */
23#define NS_MAXLABEL 63 /* max label length */
24#define NS_MAXDNSRCH 6 /* max domains in search path */
25#define NS_CMPRSFLGS 0xc0 /* name compression pointer flag */
26
27
[2725]28/* Expand a RFC1035-compressed list of domain names "cstr", of length "clen";
[1765]29 * returns a newly allocated string containing the space-separated domains,
30 * prefixed with the contents of string pre, or NULL if an error occurs.
31 */
[2725]32char* FAST_FUNC dname_dec(const uint8_t *cstr, int clen, const char *pre)
[1765]33{
[2725]34 char *ret = ret; /* for compiler */
[1765]35 char *dst = NULL;
36
37 /* We make two passes over the cstr string. First, we compute
38 * how long the resulting string would be. Then we allocate a
39 * new buffer of the required length, and fill it in with the
40 * expanded content. The advantage of this approach is not
41 * having to deal with requiring callers to supply their own
42 * buffer, then having to check if it's sufficiently large, etc.
43 */
[2725]44 while (1) {
45 /* note: "return NULL" below are leak-safe since
[3621]46 * dst isn't allocated yet */
[2725]47 const uint8_t *c;
48 unsigned crtpos, retpos, depth, len;
[1765]49
50 crtpos = retpos = depth = len = 0;
51 while (crtpos < clen) {
52 c = cstr + crtpos;
53
[2725]54 if ((*c & NS_CMPRSFLGS) == NS_CMPRSFLGS) {
55 /* pointer */
56 if (crtpos + 2 > clen) /* no offset to jump to? abort */
[1765]57 return NULL;
[2725]58 if (retpos == 0) /* toplevel? save return spot */
[1765]59 retpos = crtpos + 2;
60 depth++;
[2725]61 crtpos = ((c[0] & 0x3f) << 8) | c[1]; /* jump */
62 } else if (*c) {
63 /* label */
64 if (crtpos + *c + 1 > clen) /* label too long? abort */
[1765]65 return NULL;
66 if (dst)
[3621]67 /* \3com ---> "com." */
68 ((char*)mempcpy(dst + len, c + 1, *c))[0] = '.';
[1765]69 len += *c + 1;
70 crtpos += *c + 1;
[2725]71 } else {
72 /* NUL: end of current domain name */
73 if (retpos == 0) {
74 /* toplevel? keep going */
[1765]75 crtpos++;
[2725]76 } else {
77 /* return to toplevel saved spot */
[1765]78 crtpos = retpos;
79 retpos = depth = 0;
80 }
[3621]81 if (dst && len != 0)
82 /* \4host\3com\0\4host and we are at \0:
83 * \3com was converted to "com.", change dot to space.
84 */
[2725]85 dst[len - 1] = ' ';
[1765]86 }
87
[2725]88 if (depth > NS_MAXDNSRCH /* too many jumps? abort, it's a loop */
89 || len > NS_MAXDNAME * NS_MAXDNSRCH /* result too long? abort */
90 ) {
[1765]91 return NULL;
[2725]92 }
[1765]93 }
94
[2725]95 if (!len) /* expanded string has 0 length? abort */
[1765]96 return NULL;
97
[2725]98 if (!dst) { /* first pass? */
99 /* allocate dst buffer and copy pre */
100 unsigned plen = strlen(pre);
[3621]101 ret = xmalloc(plen + len);
102 dst = stpcpy(ret, pre);
[2725]103 } else {
104 dst[len - 1] = '\0';
105 break;
106 }
[1765]107 }
108
[2725]109 return ret;
[1765]110}
111
112/* Convert a domain name (src) from human-readable "foo.blah.com" format into
113 * RFC1035 encoding "\003foo\004blah\003com\000". Return allocated string, or
114 * NULL if an error occurs.
115 */
116static uint8_t *convert_dname(const char *src)
117{
[2725]118 uint8_t c, *res, *lenptr, *dst;
[1765]119 int len;
120
121 res = xmalloc(strlen(src) + 2);
[2725]122 dst = lenptr = res;
123 dst++;
[1765]124
125 for (;;) {
126 c = (uint8_t)*src++;
[2725]127 if (c == '.' || c == '\0') { /* end of label */
128 len = dst - lenptr - 1;
[1765]129 /* label too long, too short, or two '.'s in a row? abort */
130 if (len > NS_MAXLABEL || len == 0 || (c == '.' && *src == '.')) {
131 free(res);
132 return NULL;
133 }
[2725]134 *lenptr = len;
135 if (c == '\0' || *src == '\0') /* "" or ".": end of src */
[1765]136 break;
[2725]137 lenptr = dst++;
138 continue;
[1765]139 }
[2725]140 if (c >= 'A' && c <= 'Z') /* uppercase? convert to lower */
141 c += ('a' - 'A');
142 *dst++ = c;
[1765]143 }
144
[2725]145 if (dst - res >= NS_MAXCDNAME) { /* dname too long? abort */
[1765]146 free(res);
147 return NULL;
148 }
[2725]149
150 *dst = 0;
[1765]151 return res;
152}
153
[2725]154/* Returns the offset within cstr at which dname can be found, or -1 */
[1765]155static int find_offset(const uint8_t *cstr, int clen, const uint8_t *dname)
156{
157 const uint8_t *c, *d;
[2725]158 int off;
[1765]159
160 /* find all labels in cstr */
161 off = 0;
162 while (off < clen) {
163 c = cstr + off;
164
[2725]165 if ((*c & NS_CMPRSFLGS) == NS_CMPRSFLGS) { /* pointer, skip */
[1765]166 off += 2;
[2725]167 continue;
168 }
169 if (*c) { /* label, try matching dname */
[1765]170 d = dname;
[2725]171 while (1) {
172 unsigned len1 = *c + 1;
173 if (memcmp(c, d, len1) != 0)
174 break;
175 if (len1 == 1) /* at terminating NUL - match, return offset */
[1765]176 return off;
[2725]177 d += len1;
178 c += len1;
179 if ((*c & NS_CMPRSFLGS) == NS_CMPRSFLGS) /* pointer, jump */
180 c = cstr + (((c[0] & 0x3f) << 8) | c[1]);
[1765]181 }
[2725]182 off += cstr[off] + 1;
183 continue;
[1765]184 }
[2725]185 /* NUL, skip */
186 off++;
[1765]187 }
188
189 return -1;
190}
191
[2725]192/* Computes string to be appended to cstr so that src would be added to
[1765]193 * the compression (best case, it's a 2-byte pointer to some offset within
[2725]194 * cstr; worst case, it's all of src, converted to <4>host<3>com<0> format).
[1765]195 * The computed string is returned directly; its length is returned via retlen;
196 * NULL and 0, respectively, are returned if an error occurs.
197 */
[2725]198uint8_t* FAST_FUNC dname_enc(const uint8_t *cstr, int clen, const char *src, int *retlen)
[1765]199{
200 uint8_t *d, *dname;
201 int off;
202
203 dname = convert_dname(src);
204 if (dname == NULL) {
205 *retlen = 0;
206 return NULL;
207 }
208
[2725]209 d = dname;
210 while (*d) {
211 if (cstr) {
212 off = find_offset(cstr, clen, d);
213 if (off >= 0) { /* found a match, add pointer and return */
214 *d++ = NS_CMPRSFLGS | (off >> 8);
215 *d = off;
216 break;
217 }
[1765]218 }
[2725]219 d += *d + 1;
[1765]220 }
221
222 *retlen = d - dname + 1;
223 return dname;
224}
225
[2725]226#ifdef DNS_COMPR_TESTING
227/* gcc -Wall -DDNS_COMPR_TESTING domain_codec.c -o domain_codec && ./domain_codec */
228int main(int argc, char **argv)
229{
230 int len;
231 uint8_t *encoded;
232
[3621]233 uint8_t str[6] = { 0x00, 0x00, 0x02, 0x65, 0x65, 0x00 };
234 printf("NUL:'%s'\n", dname_dec(str, 6, ""));
235
[2725]236#define DNAME_DEC(encoded,pre) dname_dec((uint8_t*)(encoded), sizeof(encoded), (pre))
237 printf("'%s'\n", DNAME_DEC("\4host\3com\0", "test1:"));
238 printf("test2:'%s'\n", DNAME_DEC("\4host\3com\0\4host\3com\0", ""));
239 printf("test3:'%s'\n", DNAME_DEC("\4host\3com\0\xC0\0", ""));
240 printf("test4:'%s'\n", DNAME_DEC("\4host\3com\0\xC0\5", ""));
241 printf("test5:'%s'\n", DNAME_DEC("\4host\3com\0\xC0\5\1z\xC0\xA", ""));
242
243#define DNAME_ENC(cache,source,lenp) dname_enc((uint8_t*)(cache), sizeof(cache), (source), (lenp))
244 encoded = dname_enc(NULL, 0, "test.net", &len);
245 printf("test6:'%s' len:%d\n", dname_dec(encoded, len, ""), len);
246 encoded = DNAME_ENC("\3net\0", "test.net", &len);
247 printf("test7:'%s' len:%d\n", dname_dec(encoded, len, ""), len);
248 encoded = DNAME_ENC("\4test\3net\0", "test.net", &len);
249 printf("test8:'%s' len:%d\n", dname_dec(encoded, len, ""), len);
250 return 0;
251}
252#endif
Note: See TracBrowser for help on using the repository browser.