xref: /illumos-gate/usr/src/lib/libresolv/res_comp.c (revision d8e10381a0083d7717710b0db7e64707bc0f3ff8)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28 /*	  All Rights Reserved  	*/
29 
30 /*
31  * University Copyright- Copyright (c) 1982, 1986, 1988
32  * The Regents of the University of California
33  * All Rights Reserved
34  *
35  * University Acknowledgment- Portions of this document are derived from
36  * software developed by the University of California, Berkeley, and its
37  * contributors.
38  */
39 
40 #pragma ident	"%Z%%M%	%I%	%E% SMI"
41 
42 #include <sys/types.h>
43 #include <stdio.h>
44 #include <arpa/nameser.h>
45 
46 static int dn_find(u_char *exp_dn, u_char *msg, u_char **dnptrs,
47 	u_char **lastdnptr);
48 
49 
50 /*
51  * Expand compressed domain name 'comp_dn' to full domain name.
52  * 'msg' is a pointer to the begining of the message,
53  * 'eomorig' points to the first location after the message,
54  * 'exp_dn' is a pointer to a buffer of size 'length' for the result.
55  * Return size of compressed name or -1 if there was an error.
56  */
57 int
58 dn_expand(msg, eomorig, comp_dn, exp_dn, length)
59 	u_char *msg, *eomorig, *comp_dn, *exp_dn;
60 	int length;
61 {
62 	register u_char *cp, *dn;
63 	register int n, c;
64 	u_char *eom;
65 	int len = -1, checked = 0;
66 
67 	dn = exp_dn;
68 	cp = comp_dn;
69 	eom = exp_dn + length;
70 	/*
71 	 * fetch next label in domain name
72 	 */
73 	while (n = *cp++) {
74 		/*
75 		 * Check for indirection
76 		 */
77 		switch (n & INDIR_MASK) {
78 		case 0:
79 			if (dn != exp_dn) {
80 				if (dn >= eom)
81 					return (-1);
82 				*dn++ = '.';
83 			}
84 			if (dn+n >= eom)
85 				return (-1);
86 			checked += n + 1;
87 			while (--n >= 0) {
88 				if ((c = *cp++) == '.') {
89 					if (dn + n + 2 >= eom)
90 						return (-1);
91 					*dn++ = '\\';
92 				}
93 				*dn++ = c;
94 				if (cp >= eomorig)	/* out of range */
95 					return (-1);
96 			}
97 			break;
98 
99 		case INDIR_MASK:
100 			if (len < 0)
101 				len = cp - comp_dn + 1;
102 			cp = msg + (((n & 0x3f) << 8) | (*cp & 0xff));
103 			if (cp < msg || cp >= eomorig)	/* out of range */
104 				return (-1);
105 			checked += 2;
106 			/*
107 			 * Check for loops in the compressed name;
108 			 * if we've looked at the whole message,
109 			 * there must be a loop.
110 			 */
111 			if (checked >= eomorig - msg)
112 				return (-1);
113 			break;
114 
115 		default:
116 			return (-1);			/* flag error */
117 		}
118 	}
119 	*dn = '\0';
120 	if (len < 0)
121 		len = cp - comp_dn;
122 	return (len);
123 }
124 
125 /*
126  * Compress domain name 'exp_dn' into 'comp_dn'.
127  * Return the size of the compressed name or -1.
128  * 'length' is the size of the array pointed to by 'comp_dn'.
129  * 'dnptrs' is a list of pointers to previous compressed names. dnptrs[0]
130  * is a pointer to the beginning of the message. The list ends with NULL.
131  * 'lastdnptr' is a pointer to the end of the arrary pointed to
132  * by 'dnptrs'. Side effect is to update the list of pointers for
133  * labels inserted into the message as we compress the name.
134  * If 'dnptr' is NULL, we don't try to compress names. If 'lastdnptr'
135  * is NULL, we don't update the list.
136  */
137 int
138 dn_comp(exp_dn, comp_dn, length, dnptrs, lastdnptr)
139 	u_char *exp_dn, *comp_dn;
140 	int length;
141 	u_char **dnptrs, **lastdnptr;
142 {
143 	register u_char *cp, *dn;
144 	register int c, l;
145 	u_char **cpp, **lpp, *sp, *eob;
146 	u_char *msg;
147 
148 	dn = exp_dn;
149 	cp = comp_dn;
150 	eob = cp + length;
151 	if (dnptrs != NULL) {
152 		if ((msg = *dnptrs++) != NULL) {
153 			for (cpp = dnptrs; *cpp != NULL; cpp++)
154 				;
155 			lpp = cpp;	/* end of list to search */
156 		}
157 	} else
158 		msg = NULL;
159 	for (c = *dn++; c != '\0'; /*EMPTY*/) {
160 		/* look to see if we can use pointers */
161 		if (msg != NULL) {
162 			if ((l = dn_find(dn-1, msg, dnptrs, lpp)) >= 0) {
163 				if (cp+1 >= eob)
164 					return (-1);
165 				*cp++ = (l >> 8) | INDIR_MASK;
166 				*cp++ = l % 256;
167 				return (cp - comp_dn);
168 			}
169 			/* not found, save it */
170 			if (lastdnptr != NULL && cpp < lastdnptr-1) {
171 				*cpp++ = cp;
172 				*cpp = NULL;
173 			}
174 		}
175 		sp = cp++;	/* save ptr to length byte */
176 		do {
177 			if (c == '.') {
178 				c = *dn++;
179 				break;
180 			}
181 			if (c == '\\') {
182 				if ((c = *dn++) == '\0')
183 					break;
184 			}
185 			if (cp >= eob) {
186 				if (msg != NULL)
187 					*lpp = NULL;
188 				return (-1);
189 			}
190 			*cp++ = c;
191 		} while ((c = *dn++) != '\0');
192 		/* catch trailing '.'s but not '..' */
193 		if ((l = cp - sp - 1) == 0 && c == '\0') {
194 			cp--;
195 			break;
196 		}
197 		if (l <= 0 || l > MAXLABEL) {
198 			if (msg != NULL)
199 				*lpp = NULL;
200 			return (-1);
201 		}
202 		*sp = l;
203 	}
204 	if (cp >= eob) {
205 		if (msg != NULL)
206 			*lpp = NULL;
207 		return (-1);
208 	}
209 	*cp++ = '\0';
210 	return (cp - comp_dn);
211 }
212 
213 /*
214  * Skip over a compressed domain name. Return the size or -1.
215  */
216 int
217 dn_skipname(comp_dn, eom)
218 	u_char *comp_dn, *eom;
219 {
220 	register u_char *cp;
221 	register int n;
222 
223 	cp = comp_dn;
224 	while (cp < eom && (n = *cp++)) {
225 		/*
226 		 * check for indirection
227 		 */
228 		switch (n & INDIR_MASK) {
229 		case 0:		/* normal case, n == len */
230 			cp += n;
231 			continue;
232 		default:	/* illegal type */
233 			return (-1);
234 		case INDIR_MASK:	/* indirection */
235 			cp++;
236 		}
237 		break;
238 	}
239 	return (cp - comp_dn);
240 }
241 
242 /*
243  * Search for expanded name from a list of previously compressed names.
244  * Return the offset from msg if found or -1.
245  * dnptrs is the pointer to the first name on the list,
246  * not the pointer to the start of the message.
247  */
248 static int
249 dn_find(u_char *exp_dn, u_char *msg, u_char **dnptrs, u_char **lastdnptr)
250 {
251 	register u_char *dn, *cp, **cpp;
252 	register int n;
253 	u_char *sp;
254 
255 	for (cpp = dnptrs; cpp < lastdnptr; cpp++) {
256 		dn = exp_dn;
257 		sp = cp = *cpp;
258 		while (n = *cp++) {
259 			/*
260 			 * check for indirection
261 			 */
262 			switch (n & INDIR_MASK) {
263 			case 0:		/* normal case, n == len */
264 				while (--n >= 0) {
265 					if (*dn == '.')
266 						goto next;
267 					if (*dn == '\\')
268 						dn++;
269 					if (*dn++ != *cp++)
270 						goto next;
271 				}
272 				if ((n = *dn++) == '\0' && *cp == '\0')
273 					return (sp - msg);
274 				if (n == '.')
275 					continue;
276 				goto next;
277 
278 			default:	/* illegal type */
279 				return (-1);
280 
281 			case INDIR_MASK:	/* indirection */
282 				cp = msg + (((n & 0x3f) << 8) | *cp);
283 			}
284 		}
285 		if (*dn == '\0')
286 			return (sp - msg);
287 	next:	/*EMPTY*/;
288 	}
289 	return (-1);
290 }
291 
292 /*
293  * Routines to insert/extract short/long's. Must account for byte
294  * order and non-alignment problems. This code at least has the
295  * advantage of being portable.
296  *
297  * used by sendmail.
298  */
299 
300 u_short
301 _getshort(msgp)
302 	u_char *msgp;
303 {
304 	register u_char *p = (u_char *) msgp;
305 #ifdef vax
306 	/*
307 	 * vax compiler doesn't put shorts in registers
308 	 */
309 	register u_long u;
310 #else
311 	register u_short u;
312 #endif
313 
314 	u = *p++ << 8;
315 	return ((u_short)(u | *p));
316 }
317 
318 u_long
319 _getlong(msgp)
320 	u_char *msgp;
321 {
322 	register u_char *p = (u_char *) msgp;
323 	register u_long u;
324 
325 	u = *p++; u <<= 8;
326 	u |= *p++; u <<= 8;
327 	u |= *p++; u <<= 8;
328 	return (u | *p);
329 }
330 
331 void
332 putshort(s, msgp)
333 	register u_short s;
334 	register u_char *msgp;
335 {
336 
337 	msgp[1] = s;
338 	msgp[0] = s >> 8;
339 }
340 
341 void
342 putlong(l, msgp)
343 	register u_long l;
344 	register u_char *msgp;
345 {
346 
347 	msgp[3] = l;
348 	msgp[2] = (l >>= 8);
349 	msgp[1] = (l >>= 8);
350 	msgp[0] = l >> 8;
351 }
352