1 |
|
|
/* $OpenBSD: res_comp.c,v 1.20 2016/05/01 15:17:29 millert Exp $ */ |
2 |
|
|
|
3 |
|
|
/* |
4 |
|
|
* ++Copyright++ 1985, 1993 |
5 |
|
|
* - |
6 |
|
|
* Copyright (c) 1985, 1993 |
7 |
|
|
* The Regents of the University of California. All rights reserved. |
8 |
|
|
* |
9 |
|
|
* Redistribution and use in source and binary forms, with or without |
10 |
|
|
* modification, are permitted provided that the following conditions |
11 |
|
|
* are met: |
12 |
|
|
* 1. Redistributions of source code must retain the above copyright |
13 |
|
|
* notice, this list of conditions and the following disclaimer. |
14 |
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
15 |
|
|
* notice, this list of conditions and the following disclaimer in the |
16 |
|
|
* documentation and/or other materials provided with the distribution. |
17 |
|
|
* 3. Neither the name of the University nor the names of its contributors |
18 |
|
|
* may be used to endorse or promote products derived from this software |
19 |
|
|
* without specific prior written permission. |
20 |
|
|
* |
21 |
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
22 |
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
23 |
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
24 |
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
25 |
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
26 |
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
27 |
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
28 |
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
29 |
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
30 |
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
31 |
|
|
* SUCH DAMAGE. |
32 |
|
|
* - |
33 |
|
|
* Portions Copyright (c) 1993 by Digital Equipment Corporation. |
34 |
|
|
* |
35 |
|
|
* Permission to use, copy, modify, and distribute this software for any |
36 |
|
|
* purpose with or without fee is hereby granted, provided that the above |
37 |
|
|
* copyright notice and this permission notice appear in all copies, and that |
38 |
|
|
* the name of Digital Equipment Corporation not be used in advertising or |
39 |
|
|
* publicity pertaining to distribution of the document or software without |
40 |
|
|
* specific, written prior permission. |
41 |
|
|
* |
42 |
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL |
43 |
|
|
* WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES |
44 |
|
|
* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT |
45 |
|
|
* CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL |
46 |
|
|
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR |
47 |
|
|
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS |
48 |
|
|
* ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS |
49 |
|
|
* SOFTWARE. |
50 |
|
|
* - |
51 |
|
|
* --Copyright-- |
52 |
|
|
*/ |
53 |
|
|
|
54 |
|
|
#include <sys/types.h> |
55 |
|
|
#include <netinet/in.h> |
56 |
|
|
#include <arpa/nameser.h> |
57 |
|
|
|
58 |
|
|
#include <stdio.h> |
59 |
|
|
#include <resolv.h> |
60 |
|
|
#include <ctype.h> |
61 |
|
|
|
62 |
|
|
#include <unistd.h> |
63 |
|
|
#include <limits.h> |
64 |
|
|
#include <string.h> |
65 |
|
|
|
66 |
|
|
static int dn_find(u_char *, u_char *, u_char **, u_char **); |
67 |
|
|
|
68 |
|
|
/* |
69 |
|
|
* Expand compressed domain name 'comp_dn' to full domain name. |
70 |
|
|
* 'msg' is a pointer to the beginning of the message, |
71 |
|
|
* 'eomorig' points to the first location after the message, |
72 |
|
|
* 'exp_dn' is a pointer to a buffer of size 'length' for the result. |
73 |
|
|
* Return size of compressed name or -1 if there was an error. |
74 |
|
|
*/ |
75 |
|
|
int |
76 |
|
|
dn_expand(const u_char *msg, const u_char *eomorig, const u_char *comp_dn, |
77 |
|
|
char *exp_dn, int length) |
78 |
|
|
{ |
79 |
|
|
const u_char *cp; |
80 |
|
|
char *dn; |
81 |
|
|
int n, c; |
82 |
|
|
char *eom; |
83 |
|
|
int len = -1, checked = 0; |
84 |
|
|
|
85 |
|
|
dn = exp_dn; |
86 |
|
|
cp = comp_dn; |
87 |
|
|
if (length > HOST_NAME_MAX) |
88 |
|
|
length = HOST_NAME_MAX; |
89 |
|
|
eom = exp_dn + length; |
90 |
|
|
/* |
91 |
|
|
* fetch next label in domain name |
92 |
|
|
*/ |
93 |
|
|
while ((n = *cp++)) { |
94 |
|
|
/* |
95 |
|
|
* Check for indirection |
96 |
|
|
*/ |
97 |
|
|
switch (n & INDIR_MASK) { |
98 |
|
|
case 0: |
99 |
|
|
if (dn != exp_dn) { |
100 |
|
|
if (dn >= eom) |
101 |
|
|
return (-1); |
102 |
|
|
*dn++ = '.'; |
103 |
|
|
} |
104 |
|
|
if (dn+n >= eom) |
105 |
|
|
return (-1); |
106 |
|
|
checked += n + 1; |
107 |
|
|
while (--n >= 0) { |
108 |
|
|
if (((c = *cp++) == '.') || (c == '\\')) { |
109 |
|
|
if (dn + n + 2 >= eom) |
110 |
|
|
return (-1); |
111 |
|
|
*dn++ = '\\'; |
112 |
|
|
} |
113 |
|
|
*dn++ = c; |
114 |
|
|
if (cp >= eomorig) /* out of range */ |
115 |
|
|
return (-1); |
116 |
|
|
} |
117 |
|
|
break; |
118 |
|
|
|
119 |
|
|
case INDIR_MASK: |
120 |
|
|
if (len < 0) |
121 |
|
|
len = cp - comp_dn + 1; |
122 |
|
|
cp = msg + (((n & 0x3f) << 8) | (*cp & 0xff)); |
123 |
|
|
if (cp < msg || cp >= eomorig) /* out of range */ |
124 |
|
|
return (-1); |
125 |
|
|
checked += 2; |
126 |
|
|
/* |
127 |
|
|
* Check for loops in the compressed name; |
128 |
|
|
* if we've looked at the whole message, |
129 |
|
|
* there must be a loop. |
130 |
|
|
*/ |
131 |
|
|
if (checked >= eomorig - msg) |
132 |
|
|
return (-1); |
133 |
|
|
break; |
134 |
|
|
|
135 |
|
|
default: |
136 |
|
|
return (-1); /* flag error */ |
137 |
|
|
} |
138 |
|
|
} |
139 |
|
|
*dn = '\0'; |
140 |
|
|
if (len < 0) |
141 |
|
|
len = cp - comp_dn; |
142 |
|
|
return (len); |
143 |
|
|
} |
144 |
|
|
DEF_WEAK(dn_expand); |
145 |
|
|
|
146 |
|
|
/* |
147 |
|
|
* Compress domain name 'exp_dn' into 'comp_dn'. |
148 |
|
|
* Return the size of the compressed name or -1. |
149 |
|
|
* 'length' is the size of the array pointed to by 'comp_dn'. |
150 |
|
|
* 'dnptrs' is a list of pointers to previous compressed names. dnptrs[0] |
151 |
|
|
* is a pointer to the beginning of the message. The list ends with NULL. |
152 |
|
|
* 'lastdnptr' is a pointer to the end of the arrary pointed to |
153 |
|
|
* by 'dnptrs'. Side effect is to update the list of pointers for |
154 |
|
|
* labels inserted into the message as we compress the name. |
155 |
|
|
* If 'dnptr' is NULL, we don't try to compress names. If 'lastdnptr' |
156 |
|
|
* is NULL, we don't update the list. |
157 |
|
|
*/ |
158 |
|
|
int |
159 |
|
|
dn_comp(const char *exp_dn, u_char *comp_dn, int length, u_char **dnptrs, |
160 |
|
|
u_char **lastdnptr) |
161 |
|
|
{ |
162 |
|
|
u_char *cp, *dn; |
163 |
|
|
int c, l; |
164 |
|
|
u_char **cpp, **lpp, *sp, *eob; |
165 |
|
|
u_char *msg; |
166 |
|
|
|
167 |
|
|
dn = (u_char *)exp_dn; |
168 |
|
|
cp = comp_dn; |
169 |
|
|
eob = cp + length; |
170 |
|
|
lpp = cpp = NULL; |
171 |
|
|
if (dnptrs != NULL) { |
172 |
|
|
if ((msg = *dnptrs++) != NULL) { |
173 |
|
|
for (cpp = dnptrs; *cpp != NULL; cpp++) |
174 |
|
|
; |
175 |
|
|
lpp = cpp; /* end of list to search */ |
176 |
|
|
} |
177 |
|
|
} else |
178 |
|
|
msg = NULL; |
179 |
|
|
for (c = *dn++; c != '\0'; ) { |
180 |
|
|
/* look to see if we can use pointers */ |
181 |
|
|
if (msg != NULL) { |
182 |
|
|
if ((l = dn_find(dn-1, msg, dnptrs, lpp)) >= 0) { |
183 |
|
|
if (cp+1 >= eob) |
184 |
|
|
return (-1); |
185 |
|
|
*cp++ = (l >> 8) | INDIR_MASK; |
186 |
|
|
*cp++ = l % 256; |
187 |
|
|
return (cp - comp_dn); |
188 |
|
|
} |
189 |
|
|
/* not found, save it */ |
190 |
|
|
if (lastdnptr != NULL && cpp < lastdnptr-1) { |
191 |
|
|
*cpp++ = cp; |
192 |
|
|
*cpp = NULL; |
193 |
|
|
} |
194 |
|
|
} |
195 |
|
|
sp = cp++; /* save ptr to length byte */ |
196 |
|
|
do { |
197 |
|
|
if (c == '.') { |
198 |
|
|
c = *dn++; |
199 |
|
|
break; |
200 |
|
|
} |
201 |
|
|
if (c == '\\') { |
202 |
|
|
if ((c = *dn++) == '\0') |
203 |
|
|
break; |
204 |
|
|
} |
205 |
|
|
if (cp >= eob) { |
206 |
|
|
if (msg != NULL) |
207 |
|
|
*lpp = NULL; |
208 |
|
|
return (-1); |
209 |
|
|
} |
210 |
|
|
*cp++ = c; |
211 |
|
|
} while ((c = *dn++) != '\0'); |
212 |
|
|
/* catch trailing '.'s but not '..' */ |
213 |
|
|
if ((l = cp - sp - 1) == 0 && c == '\0') { |
214 |
|
|
cp--; |
215 |
|
|
break; |
216 |
|
|
} |
217 |
|
|
if (l <= 0 || l > MAXLABEL) { |
218 |
|
|
if (msg != NULL) |
219 |
|
|
*lpp = NULL; |
220 |
|
|
return (-1); |
221 |
|
|
} |
222 |
|
|
*sp = l; |
223 |
|
|
} |
224 |
|
|
if (cp >= eob) { |
225 |
|
|
if (msg != NULL) |
226 |
|
|
*lpp = NULL; |
227 |
|
|
return (-1); |
228 |
|
|
} |
229 |
|
|
*cp++ = '\0'; |
230 |
|
|
return (cp - comp_dn); |
231 |
|
|
} |
232 |
|
|
|
233 |
|
|
/* |
234 |
|
|
* Skip over a compressed domain name. Return the size or -1. |
235 |
|
|
*/ |
236 |
|
|
int |
237 |
|
|
__dn_skipname(const u_char *comp_dn, const u_char *eom) |
238 |
|
|
{ |
239 |
|
|
const u_char *cp; |
240 |
|
|
int n; |
241 |
|
|
|
242 |
|
|
cp = comp_dn; |
243 |
|
|
while (cp < eom && (n = *cp++)) { |
244 |
|
|
/* |
245 |
|
|
* check for indirection |
246 |
|
|
*/ |
247 |
|
|
switch (n & INDIR_MASK) { |
248 |
|
|
case 0: /* normal case, n == len */ |
249 |
|
|
cp += n; |
250 |
|
|
continue; |
251 |
|
|
case INDIR_MASK: /* indirection */ |
252 |
|
|
cp++; |
253 |
|
|
break; |
254 |
|
|
default: /* illegal type */ |
255 |
|
|
return (-1); |
256 |
|
|
} |
257 |
|
|
break; |
258 |
|
|
} |
259 |
|
|
if (cp > eom) |
260 |
|
|
return (-1); |
261 |
|
|
return (cp - comp_dn); |
262 |
|
|
} |
263 |
|
|
|
264 |
|
|
static int |
265 |
|
|
mklower(int ch) |
266 |
|
|
{ |
267 |
|
|
if (isascii(ch) && isupper(ch)) |
268 |
|
|
return (tolower(ch)); |
269 |
|
|
return (ch); |
270 |
|
|
} |
271 |
|
|
|
272 |
|
|
/* |
273 |
|
|
* Search for expanded name from a list of previously compressed names. |
274 |
|
|
* Return the offset from msg if found or -1. |
275 |
|
|
* dnptrs is the pointer to the first name on the list, |
276 |
|
|
* not the pointer to the start of the message. |
277 |
|
|
*/ |
278 |
|
|
static int |
279 |
|
|
dn_find(u_char *exp_dn, u_char *msg, u_char **dnptrs, u_char **lastdnptr) |
280 |
|
|
{ |
281 |
|
|
u_char *dn, *cp, **cpp; |
282 |
|
|
int n; |
283 |
|
|
u_char *sp; |
284 |
|
|
|
285 |
|
|
for (cpp = dnptrs; cpp < lastdnptr; cpp++) { |
286 |
|
|
dn = exp_dn; |
287 |
|
|
sp = cp = *cpp; |
288 |
|
|
while ((n = *cp++)) { |
289 |
|
|
/* |
290 |
|
|
* check for indirection |
291 |
|
|
*/ |
292 |
|
|
switch (n & INDIR_MASK) { |
293 |
|
|
case 0: /* normal case, n == len */ |
294 |
|
|
while (--n >= 0) { |
295 |
|
|
if (*dn == '.') |
296 |
|
|
goto next; |
297 |
|
|
if (*dn == '\\') |
298 |
|
|
dn++; |
299 |
|
|
if (mklower(*dn++) != mklower(*cp++)) |
300 |
|
|
goto next; |
301 |
|
|
} |
302 |
|
|
if ((n = *dn++) == '\0' && *cp == '\0') |
303 |
|
|
return (sp - msg); |
304 |
|
|
if (n == '.') |
305 |
|
|
continue; |
306 |
|
|
goto next; |
307 |
|
|
|
308 |
|
|
case INDIR_MASK: /* indirection */ |
309 |
|
|
cp = msg + (((n & 0x3f) << 8) | *cp); |
310 |
|
|
break; |
311 |
|
|
|
312 |
|
|
default: /* illegal type */ |
313 |
|
|
return (-1); |
314 |
|
|
} |
315 |
|
|
} |
316 |
|
|
if (*dn == '\0') |
317 |
|
|
return (sp - msg); |
318 |
|
|
next: ; |
319 |
|
|
} |
320 |
|
|
return (-1); |
321 |
|
|
} |
322 |
|
|
|
323 |
|
|
/* |
324 |
|
|
* Verify that a domain name uses an acceptable character set. |
325 |
|
|
*/ |
326 |
|
|
|
327 |
|
|
/* |
328 |
|
|
* Note the conspicuous absence of ctype macros in these definitions. On |
329 |
|
|
* non-ASCII hosts, we can't depend on string literals or ctype macros to |
330 |
|
|
* tell us anything about network-format data. The rest of the BIND system |
331 |
|
|
* is not careful about this, but for some reason, we're doing it right here. |
332 |
|
|
*/ |
333 |
|
|
#define PERIOD 0x2e |
334 |
|
|
#define hyphenchar(c) ((c) == 0x2d) |
335 |
|
|
#define bslashchar(c) ((c) == 0x5c) |
336 |
|
|
#define underscorechar(c) ((c) == 0x5f) |
337 |
|
|
#define periodchar(c) ((c) == PERIOD) |
338 |
|
|
#define asterchar(c) ((c) == 0x2a) |
339 |
|
|
#define alphachar(c) (((c) >= 0x41 && (c) <= 0x5a) \ |
340 |
|
|
|| ((c) >= 0x61 && (c) <= 0x7a)) |
341 |
|
|
#define digitchar(c) ((c) >= 0x30 && (c) <= 0x39) |
342 |
|
|
|
343 |
|
|
#define borderchar(c) (alphachar(c) || digitchar(c)) |
344 |
|
|
#define middlechar(c) (borderchar(c) || hyphenchar(c) || underscorechar(c)) |
345 |
|
|
#define domainchar(c) ((c) > 0x20 && (c) < 0x7f) |
346 |
|
|
|
347 |
|
|
int |
348 |
|
|
__res_hnok(const char *dn) |
349 |
|
|
{ |
350 |
|
|
int pch = PERIOD, ch = *dn++; |
351 |
|
|
|
352 |
|
|
while (ch != '\0') { |
353 |
|
|
int nch = *dn++; |
354 |
|
|
|
355 |
|
|
if (periodchar(ch)) { |
356 |
|
|
; |
357 |
|
|
} else if (periodchar(pch)) { |
358 |
|
|
if (!borderchar(ch)) |
359 |
|
|
return (0); |
360 |
|
|
} else if (periodchar(nch) || nch == '\0') { |
361 |
|
|
if (!borderchar(ch)) |
362 |
|
|
return (0); |
363 |
|
|
} else { |
364 |
|
|
if (!middlechar(ch)) |
365 |
|
|
return (0); |
366 |
|
|
} |
367 |
|
|
pch = ch, ch = nch; |
368 |
|
|
} |
369 |
|
|
return (1); |
370 |
|
|
} |
371 |
|
|
DEF_STRONG(__res_hnok); |
372 |
|
|
|
373 |
|
|
/* |
374 |
|
|
* hostname-like (A, MX, WKS) owners can have "*" as their first label |
375 |
|
|
* but must otherwise be as a host name. |
376 |
|
|
*/ |
377 |
|
|
int |
378 |
|
|
res_ownok(const char *dn) |
379 |
|
|
{ |
380 |
|
|
if (asterchar(dn[0])) { |
381 |
|
|
if (periodchar(dn[1])) |
382 |
|
|
return (res_hnok(dn+2)); |
383 |
|
|
if (dn[1] == '\0') |
384 |
|
|
return (1); |
385 |
|
|
} |
386 |
|
|
return (res_hnok(dn)); |
387 |
|
|
} |
388 |
|
|
|
389 |
|
|
/* |
390 |
|
|
* SOA RNAMEs and RP RNAMEs can have any printable character in their first |
391 |
|
|
* label, but the rest of the name has to look like a host name. |
392 |
|
|
*/ |
393 |
|
|
int |
394 |
|
|
res_mailok(const char *dn) |
395 |
|
|
{ |
396 |
|
|
int ch, escaped = 0; |
397 |
|
|
|
398 |
|
|
/* "." is a valid missing representation */ |
399 |
|
|
if (*dn == '\0') |
400 |
|
|
return(1); |
401 |
|
|
|
402 |
|
|
/* otherwise <label>.<hostname> */ |
403 |
|
|
while ((ch = *dn++) != '\0') { |
404 |
|
|
if (!domainchar(ch)) |
405 |
|
|
return (0); |
406 |
|
|
if (!escaped && periodchar(ch)) |
407 |
|
|
break; |
408 |
|
|
if (escaped) |
409 |
|
|
escaped = 0; |
410 |
|
|
else if (bslashchar(ch)) |
411 |
|
|
escaped = 1; |
412 |
|
|
} |
413 |
|
|
if (periodchar(ch)) |
414 |
|
|
return (res_hnok(dn)); |
415 |
|
|
return(0); |
416 |
|
|
} |
417 |
|
|
|
418 |
|
|
/* |
419 |
|
|
* This function is quite liberal, since RFC 1034's character sets are only |
420 |
|
|
* recommendations. |
421 |
|
|
*/ |
422 |
|
|
int |
423 |
|
|
res_dnok(const char *dn) |
424 |
|
|
{ |
425 |
|
|
int ch; |
426 |
|
|
|
427 |
|
|
while ((ch = *dn++) != '\0') |
428 |
|
|
if (!domainchar(ch)) |
429 |
|
|
return (0); |
430 |
|
|
return (1); |
431 |
|
|
} |
432 |
|
|
|
433 |
|
|
/* |
434 |
|
|
* Routines to insert/extract short/long's. |
435 |
|
|
*/ |
436 |
|
|
|
437 |
|
|
u_int16_t |
438 |
|
|
_getshort(const u_char *msgp) |
439 |
|
|
{ |
440 |
|
|
u_int16_t u; |
441 |
|
|
|
442 |
|
|
GETSHORT(u, msgp); |
443 |
|
|
return (u); |
444 |
|
|
} |
445 |
|
|
DEF_STRONG(_getshort); |
446 |
|
|
|
447 |
|
|
u_int32_t |
448 |
|
|
_getlong(const u_char *msgp) |
449 |
|
|
{ |
450 |
|
|
u_int32_t u; |
451 |
|
|
|
452 |
|
|
GETLONG(u, msgp); |
453 |
|
|
return (u); |
454 |
|
|
} |
455 |
|
|
DEF_STRONG(_getlong); |
456 |
|
|
|
457 |
|
|
void |
458 |
|
|
__putshort(u_int16_t s, u_char *msgp) |
459 |
|
|
{ |
460 |
|
|
PUTSHORT(s, msgp); |
461 |
|
|
} |
462 |
|
|
|
463 |
|
|
void |
464 |
|
|
__putlong(u_int32_t l, u_char *msgp) |
465 |
|
|
{ |
466 |
|
|
PUTLONG(l, msgp); |
467 |
|
|
} |