xref: /csrg-svn/lib/libc/net/res_comp.c (revision 34817)
1 /*
2  * Copyright (c) 1985 Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that the above copyright notice and this paragraph are
7  * duplicated in all such forms and that any documentation,
8  * advertising materials, and other materials related to such
9  * distribution and use acknowledge that the software was developed
10  * by the University of California, Berkeley.  The name of the
11  * University may not be used to endorse or promote products derived
12  * from this software without specific prior written permission.
13  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16  */
17 
18 #if defined(LIBC_SCCS) && !defined(lint)
19 static char sccsid[] = "@(#)res_comp.c	6.14 (Berkeley) 06/27/88";
20 #endif /* LIBC_SCCS and not lint */
21 
22 #include <sys/types.h>
23 #include <stdio.h>
24 #include <arpa/nameser.h>
25 
26 /*
27  * Expand compressed domain name 'comp_dn' to full domain name.
28  * 'msg' is a pointer to the begining of the message,
29  * 'eomorig' points to the first location after the message,
30  * 'exp_dn' is a pointer to a buffer of size 'length' for the result.
31  * Return size of compressed name or -1 if there was an error.
32  */
33 dn_expand(msg, eomorig, comp_dn, exp_dn, length)
34 	u_char *msg, *eomorig, *comp_dn, *exp_dn;
35 	int length;
36 {
37 	register u_char *cp, *dn;
38 	register int n, c;
39 	u_char *eom;
40 	int len = -1, checked = 0;
41 
42 	dn = exp_dn;
43 	cp = comp_dn;
44 	eom = exp_dn + length - 1;
45 	/*
46 	 * fetch next label in domain name
47 	 */
48 	while (n = *cp++) {
49 		/*
50 		 * Check for indirection
51 		 */
52 		switch (n & INDIR_MASK) {
53 		case 0:
54 			if (dn != exp_dn) {
55 				if (dn >= eom)
56 					return (-1);
57 				*dn++ = '.';
58 			}
59 			if (dn+n >= eom)
60 				return (-1);
61 			checked += n + 1;
62 			while (--n >= 0) {
63 				if ((c = *cp++) == '.') {
64 					if (dn+n+1 >= eom)
65 						return (-1);
66 					*dn++ = '\\';
67 				}
68 				*dn++ = c;
69 				if (cp >= eomorig)	/* out of range */
70 					return(-1);
71 			}
72 			break;
73 
74 		case INDIR_MASK:
75 			if (len < 0)
76 				len = cp - comp_dn + 1;
77 			cp = msg + (((n & 0x3f) << 8) | (*cp & 0xff));
78 			if (cp < msg || cp >= eomorig)	/* out of range */
79 				return(-1);
80 			checked += 2;
81 			/*
82 			 * Check for loops in the compressed name;
83 			 * if we've looked at the whole message,
84 			 * there must be a loop.
85 			 */
86 			if (checked >= eomorig - msg)
87 				return (-1);
88 			break;
89 
90 		default:
91 			return (-1);			/* flag error */
92 		}
93 	}
94 	*dn = '\0';
95 	if (len < 0)
96 		len = cp - comp_dn;
97 	return (len);
98 }
99 
100 /*
101  * Compress domain name 'exp_dn' into 'comp_dn'.
102  * Return the size of the compressed name or -1.
103  * 'length' is the size of the array pointed to by 'comp_dn'.
104  * 'dnptrs' is a list of pointers to previous compressed names. dnptrs[0]
105  * is a pointer to the beginning of the message. The list ends with NULL.
106  * 'lastdnptr' is a pointer to the end of the arrary pointed to
107  * by 'dnptrs'. Side effect is to update the list of pointers for
108  * labels inserted into the message as we compress the name.
109  * If 'dnptr' is NULL, we don't try to compress names. If 'lastdnptr'
110  * is NULL, we don't update the list.
111  */
112 dn_comp(exp_dn, comp_dn, length, dnptrs, lastdnptr)
113 	u_char *exp_dn, *comp_dn;
114 	int length;
115 	u_char **dnptrs, **lastdnptr;
116 {
117 	register u_char *cp, *dn;
118 	register int c, l;
119 	u_char **cpp, **lpp, *sp, *eob;
120 	u_char *msg;
121 
122 	dn = exp_dn;
123 	cp = comp_dn;
124 	eob = cp + length;
125 	if (dnptrs != NULL) {
126 		if ((msg = *dnptrs++) != NULL) {
127 			for (cpp = dnptrs; *cpp != NULL; cpp++)
128 				;
129 			lpp = cpp;	/* end of list to search */
130 		}
131 	} else
132 		msg = NULL;
133 	for (c = *dn++; c != '\0'; ) {
134 		/* look to see if we can use pointers */
135 		if (msg != NULL) {
136 			if ((l = dn_find(dn-1, msg, dnptrs, lpp)) >= 0) {
137 				if (cp+1 >= eob)
138 					return (-1);
139 				*cp++ = (l >> 8) | INDIR_MASK;
140 				*cp++ = l % 256;
141 				return (cp - comp_dn);
142 			}
143 			/* not found, save it */
144 			if (lastdnptr != NULL && cpp < lastdnptr-1) {
145 				*cpp++ = cp;
146 				*cpp = NULL;
147 			}
148 		}
149 		sp = cp++;	/* save ptr to length byte */
150 		do {
151 			if (c == '.') {
152 				c = *dn++;
153 				break;
154 			}
155 			if (c == '\\') {
156 				if ((c = *dn++) == '\0')
157 					break;
158 			}
159 			if (cp >= eob)
160 				return (-1);
161 			*cp++ = c;
162 		} while ((c = *dn++) != '\0');
163 		/* catch trailing '.'s but not '..' */
164 		if ((l = cp - sp - 1) == 0 && c == '\0') {
165 			cp--;
166 			break;
167 		}
168 		if (l <= 0 || l > MAXLABEL)
169 			return (-1);
170 		*sp = l;
171 	}
172 	if (cp >= eob)
173 		return (-1);
174 	*cp++ = '\0';
175 	return (cp - comp_dn);
176 }
177 
178 /*
179  * Skip over a compressed domain name. Return the size or -1.
180  */
181 dn_skipname(comp_dn, eom)
182 	u_char *comp_dn, *eom;
183 {
184 	register u_char *cp;
185 	register int n;
186 
187 	cp = comp_dn;
188 	while (cp < eom && (n = *cp++)) {
189 		/*
190 		 * check for indirection
191 		 */
192 		switch (n & INDIR_MASK) {
193 		case 0:		/* normal case, n == len */
194 			cp += n;
195 			continue;
196 		default:	/* illegal type */
197 			return (-1);
198 		case INDIR_MASK:	/* indirection */
199 			cp++;
200 		}
201 		break;
202 	}
203 	return (cp - comp_dn);
204 }
205 
206 /*
207  * Search for expanded name from a list of previously compressed names.
208  * Return the offset from msg if found or -1.
209  * dnptrs is the pointer to the first name on the list,
210  * not the pointer to the start of the message.
211  */
212 static
213 dn_find(exp_dn, msg, dnptrs, lastdnptr)
214 	u_char *exp_dn, *msg;
215 	u_char **dnptrs, **lastdnptr;
216 {
217 	register u_char *dn, *cp, **cpp;
218 	register int n;
219 	u_char *sp;
220 
221 	for (cpp = dnptrs; cpp < lastdnptr; cpp++) {
222 		dn = exp_dn;
223 		sp = cp = *cpp;
224 		while (n = *cp++) {
225 			/*
226 			 * check for indirection
227 			 */
228 			switch (n & INDIR_MASK) {
229 			case 0:		/* normal case, n == len */
230 				while (--n >= 0) {
231 					if (*dn == '\\')
232 						dn++;
233 					if (*dn++ != *cp++)
234 						goto next;
235 				}
236 				if ((n = *dn++) == '\0' && *cp == '\0')
237 					return (sp - msg);
238 				if (n == '.')
239 					continue;
240 				goto next;
241 
242 			default:	/* illegal type */
243 				return (-1);
244 
245 			case INDIR_MASK:	/* indirection */
246 				cp = msg + (((n & 0x3f) << 8) | *cp);
247 			}
248 		}
249 		if (*dn == '\0')
250 			return (sp - msg);
251 	next:	;
252 	}
253 	return (-1);
254 }
255 
256 /*
257  * Routines to insert/extract short/long's. Must account for byte
258  * order and non-alignment problems. This code at least has the
259  * advantage of being portable.
260  *
261  * used by sendmail.
262  */
263 
264 u_short
265 _getshort(msgp)
266 	u_char *msgp;
267 {
268 	register u_char *p = (u_char *) msgp;
269 #ifdef vax
270 	/*
271 	 * vax compiler doesn't put shorts in registers
272 	 */
273 	register u_long u;
274 #else
275 	register u_short u;
276 #endif
277 
278 	u = *p++ << 8;
279 	return ((u_short)(u | *p));
280 }
281 
282 u_long
283 _getlong(msgp)
284 	u_char *msgp;
285 {
286 	register u_char *p = (u_char *) msgp;
287 	register u_long u;
288 
289 	u = *p++; u <<= 8;
290 	u |= *p++; u <<= 8;
291 	u |= *p++; u <<= 8;
292 	return (u | *p);
293 }
294 
295 
296 putshort(s, msgp)
297 	register u_short s;
298 	register u_char *msgp;
299 {
300 
301 	msgp[1] = s;
302 	msgp[0] = s >> 8;
303 }
304 
305 putlong(l, msgp)
306 	register u_long l;
307 	register u_char *msgp;
308 {
309 
310 	msgp[3] = l;
311 	msgp[2] = (l >>= 8);
312 	msgp[1] = (l >>= 8);
313 	msgp[0] = l >> 8;
314 }
315