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