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