diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2007-02-27 22:39:19 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2007-02-27 22:39:19 +0000 |
commit | 4c7e5b6c764427fc94d4197b4d40760500705914 (patch) | |
tree | 71f498ec8a5d5c500e88446cc82c1a2b60f27ab6 | |
parent | 5066473d411d6a474af3393d1b62a58ee3313861 (diff) | |
download | busybox-w32-4c7e5b6c764427fc94d4197b4d40760500705914.tar.gz busybox-w32-4c7e5b6c764427fc94d4197b4d40760500705914.tar.bz2 busybox-w32-4c7e5b6c764427fc94d4197b4d40760500705914.zip |
I *always* forget svn add :(
-rw-r--r-- | networking/udhcp/domain_codec.c | 205 |
1 files changed, 205 insertions, 0 deletions
diff --git a/networking/udhcp/domain_codec.c b/networking/udhcp/domain_codec.c new file mode 100644 index 000000000..6e16ddedc --- /dev/null +++ b/networking/udhcp/domain_codec.c | |||
@@ -0,0 +1,205 @@ | |||
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 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
8 | */ | ||
9 | |||
10 | #if ENABLE_FEATURE_RFC3397 | ||
11 | |||
12 | #include "common.h" | ||
13 | #include "options.h" | ||
14 | |||
15 | #define NS_MAXDNAME 1025 /* max domain name length */ | ||
16 | #define NS_MAXCDNAME 255 /* max compressed domain name length */ | ||
17 | #define NS_MAXLABEL 63 /* max label length */ | ||
18 | #define NS_MAXDNSRCH 6 /* max domains in search path */ | ||
19 | #define NS_CMPRSFLGS 0xc0 /* name compression pointer flag */ | ||
20 | |||
21 | |||
22 | /* expand a RFC1035-compressed list of domain names "cstr", of length "clen"; | ||
23 | * returns a newly allocated string containing the space-separated domains, | ||
24 | * prefixed with the contents of string pre, or NULL if an error occurs. | ||
25 | */ | ||
26 | char *dname_dec(const uint8_t *cstr, int clen, const char *pre) | ||
27 | { | ||
28 | const uint8_t *c; | ||
29 | int crtpos, retpos, depth, plen = 0, len = 0; | ||
30 | char *dst = NULL; | ||
31 | |||
32 | if (!cstr) | ||
33 | return NULL; | ||
34 | |||
35 | if (pre) | ||
36 | plen = strlen(pre); | ||
37 | |||
38 | /* We make two passes over the cstr string. First, we compute | ||
39 | * how long the resulting string would be. Then we allocate a | ||
40 | * new buffer of the required length, and fill it in with the | ||
41 | * expanded content. The advantage of this approach is not | ||
42 | * having to deal with requiring callers to supply their own | ||
43 | * buffer, then having to check if it's sufficiently large, etc. | ||
44 | */ | ||
45 | |||
46 | while (!dst) { | ||
47 | |||
48 | if (len > 0) { /* second pass? allocate dst buffer and copy pre */ | ||
49 | dst = xmalloc(len + plen); | ||
50 | memcpy(dst, pre, plen); | ||
51 | } | ||
52 | |||
53 | crtpos = retpos = depth = len = 0; | ||
54 | |||
55 | while (crtpos < clen) { | ||
56 | c = cstr + crtpos; | ||
57 | |||
58 | if ((*c & NS_CMPRSFLGS) != 0) { /* pointer */ | ||
59 | if (crtpos + 2 > clen) /* no offset to jump to? abort */ | ||
60 | return NULL; | ||
61 | if (retpos == 0) /* toplevel? save return spot */ | ||
62 | retpos = crtpos + 2; | ||
63 | depth++; | ||
64 | crtpos = ((*c & 0x3f) << 8) | (*(c + 1) & 0xff); /* jump */ | ||
65 | } else if (*c) { /* label */ | ||
66 | if (crtpos + *c + 1 > clen) /* label too long? abort */ | ||
67 | return NULL; | ||
68 | if (dst) | ||
69 | memcpy(dst + plen + len, c + 1, *c); | ||
70 | len += *c + 1; | ||
71 | crtpos += *c + 1; | ||
72 | if (dst) | ||
73 | *(dst + plen + len - 1) = '.'; | ||
74 | } else { /* null: end of current domain name */ | ||
75 | if (retpos == 0) { /* toplevel? keep going */ | ||
76 | crtpos++; | ||
77 | } else { /* return to toplevel saved spot */ | ||
78 | crtpos = retpos; | ||
79 | retpos = depth = 0; | ||
80 | } | ||
81 | if (dst) | ||
82 | *(dst + plen + len - 1) = ' '; | ||
83 | } | ||
84 | |||
85 | if (depth > NS_MAXDNSRCH || /* too many jumps? abort, it's a loop */ | ||
86 | len > NS_MAXDNAME * NS_MAXDNSRCH) /* result too long? abort */ | ||
87 | return NULL; | ||
88 | } | ||
89 | |||
90 | if (!len) /* expanded string has 0 length? abort */ | ||
91 | return NULL; | ||
92 | |||
93 | if (dst) | ||
94 | *(dst + plen + len - 1) = '\0'; | ||
95 | } | ||
96 | |||
97 | return dst; | ||
98 | } | ||
99 | |||
100 | /* Convert a domain name (src) from human-readable "foo.blah.com" format into | ||
101 | * RFC1035 encoding "\003foo\004blah\003com\000". Return allocated string, or | ||
102 | * NULL if an error occurs. | ||
103 | */ | ||
104 | static uint8_t *convert_dname(const char *src) | ||
105 | { | ||
106 | uint8_t c, *res, *lp, *rp; | ||
107 | int len; | ||
108 | |||
109 | res = xmalloc(strlen(src) + 2); | ||
110 | rp = lp = res; | ||
111 | rp++; | ||
112 | |||
113 | for (;;) { | ||
114 | c = (uint8_t)*src++; | ||
115 | if (c == '.' || c == '\0') { /* end of label */ | ||
116 | len = rp - lp - 1; | ||
117 | /* label too long, too short, or two '.'s in a row? abort */ | ||
118 | if (len > NS_MAXLABEL || len == 0 || (c == '.' && *src == '.')) { | ||
119 | free(res); | ||
120 | return NULL; | ||
121 | } | ||
122 | *lp = len; | ||
123 | lp = rp++; | ||
124 | if (c == '\0' || *src == '\0') /* end of dname */ | ||
125 | break; | ||
126 | } else { | ||
127 | if (c >= 0x41 && c <= 0x5A) /* uppercase? convert to lower */ | ||
128 | c += 0x20; | ||
129 | *rp++ = c; | ||
130 | } | ||
131 | } | ||
132 | |||
133 | *lp = 0; | ||
134 | if (rp - res > NS_MAXCDNAME) { /* dname too long? abort */ | ||
135 | free(res); | ||
136 | return NULL; | ||
137 | } | ||
138 | return res; | ||
139 | } | ||
140 | |||
141 | /* returns the offset within cstr at which dname can be found, or -1 | ||
142 | */ | ||
143 | static int find_offset(const uint8_t *cstr, int clen, const uint8_t *dname) | ||
144 | { | ||
145 | const uint8_t *c, *d; | ||
146 | int off, inc; | ||
147 | |||
148 | /* find all labels in cstr */ | ||
149 | off = 0; | ||
150 | while (off < clen) { | ||
151 | c = cstr + off; | ||
152 | |||
153 | if ((*c & NS_CMPRSFLGS) != 0) { /* pointer, skip */ | ||
154 | off += 2; | ||
155 | } else if (*c) { /* label, try matching dname */ | ||
156 | inc = *c + 1; | ||
157 | d = dname; | ||
158 | while (*c == *d && memcmp(c + 1, d + 1, *c) == 0) { | ||
159 | if (*c == 0) /* match, return offset */ | ||
160 | return off; | ||
161 | d += *c + 1; | ||
162 | c += *c + 1; | ||
163 | if ((*c & NS_CMPRSFLGS) != 0) /* pointer, jump */ | ||
164 | c = cstr + (((*c & 0x3f) << 8) | (*(c + 1) & 0xff)); | ||
165 | } | ||
166 | off += inc; | ||
167 | } else { /* null, skip */ | ||
168 | off++; | ||
169 | } | ||
170 | } | ||
171 | |||
172 | return -1; | ||
173 | } | ||
174 | |||
175 | /* computes string to be appended to cstr so that src would be added to | ||
176 | * the compression (best case, it's a 2-byte pointer to some offset within | ||
177 | * cstr; worst case, it's all of src, converted to rfc3011 format). | ||
178 | * The computed string is returned directly; its length is returned via retlen; | ||
179 | * NULL and 0, respectively, are returned if an error occurs. | ||
180 | */ | ||
181 | uint8_t *dname_enc(const uint8_t *cstr, int clen, const char *src, int *retlen) | ||
182 | { | ||
183 | uint8_t *d, *dname; | ||
184 | int off; | ||
185 | |||
186 | dname = convert_dname(src); | ||
187 | if (dname == NULL) { | ||
188 | *retlen = 0; | ||
189 | return NULL; | ||
190 | } | ||
191 | |||
192 | for (d = dname; *d != 0; d += *d + 1) { | ||
193 | off = find_offset(cstr, clen, d); | ||
194 | if (off >= 0) { /* found a match, add pointer and terminate string */ | ||
195 | *d++ = NS_CMPRSFLGS; | ||
196 | *d = off; | ||
197 | break; | ||
198 | } | ||
199 | } | ||
200 | |||
201 | *retlen = d - dname + 1; | ||
202 | return dname; | ||
203 | } | ||
204 | |||
205 | #endif /* ENABLE_FEATURE_RFC3397 */ | ||