xref: /openbsd-src/usr.sbin/dhcpd/tree.c (revision c525a1850093eeda7e2f4793bb28f06faaca922a)
1*c525a185Skrw /*	$OpenBSD: tree.c,v 1.18 2017/02/13 19:13:14 krw Exp $ */
2e853bc5dShenning 
3f84e989bShshoexer /* Routines for manipulating parse trees... */
4e853bc5dShenning 
5e853bc5dShenning /*
6e853bc5dShenning  * Copyright (c) 1995, 1996, 1997 The Internet Software Consortium.
7e853bc5dShenning  * All rights reserved.
8e853bc5dShenning  *
9e853bc5dShenning  * Redistribution and use in source and binary forms, with or without
10e853bc5dShenning  * modification, are permitted provided that the following conditions
11e853bc5dShenning  * are met:
12e853bc5dShenning  *
13e853bc5dShenning  * 1. Redistributions of source code must retain the above copyright
14e853bc5dShenning  *    notice, this list of conditions and the following disclaimer.
15e853bc5dShenning  * 2. Redistributions in binary form must reproduce the above copyright
16e853bc5dShenning  *    notice, this list of conditions and the following disclaimer in the
17e853bc5dShenning  *    documentation and/or other materials provided with the distribution.
18e853bc5dShenning  * 3. Neither the name of The Internet Software Consortium nor the names
19e853bc5dShenning  *    of its contributors may be used to endorse or promote products derived
20e853bc5dShenning  *    from this software without specific prior written permission.
21e853bc5dShenning  *
22e853bc5dShenning  * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
23e853bc5dShenning  * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
24e853bc5dShenning  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25e853bc5dShenning  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26e853bc5dShenning  * DISCLAIMED.  IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
27e853bc5dShenning  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28e853bc5dShenning  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
29e853bc5dShenning  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
30e853bc5dShenning  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
31e853bc5dShenning  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32e853bc5dShenning  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
33e853bc5dShenning  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34e853bc5dShenning  * SUCH DAMAGE.
35e853bc5dShenning  *
36e853bc5dShenning  * This software has been written for the Internet Software Consortium
37e853bc5dShenning  * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
38e853bc5dShenning  * Enterprises.  To learn more about the Internet Software Consortium,
39e853bc5dShenning  * see ``http://www.vix.com/isc''.  To learn more about Vixie
40e853bc5dShenning  * Enterprises, see ``http://www.vix.com''.
41e853bc5dShenning  */
42e853bc5dShenning 
43837cddffSkrw #include <sys/types.h>
44837cddffSkrw #include <sys/socket.h>
45837cddffSkrw 
46837cddffSkrw #include <net/if.h>
47837cddffSkrw 
48837cddffSkrw #include <netinet/in.h>
49837cddffSkrw 
50837cddffSkrw #include <stdio.h>
51837cddffSkrw #include <stdlib.h>
52837cddffSkrw #include <string.h>
53837cddffSkrw 
54837cddffSkrw #include "dhcp.h"
55837cddffSkrw #include "tree.h"
56e853bc5dShenning #include "dhcpd.h"
57*c525a185Skrw #include "log.h"
58e853bc5dShenning 
593c41e82cShenning static time_t tree_evaluate_recurse(int *, unsigned char **, int *,
603c41e82cShenning     struct tree *);
613c41e82cShenning static void do_data_copy(int *, unsigned char **, int *, unsigned char *, int);
62e853bc5dShenning 
63f84e989bShshoexer pair
cons(caddr_t car,pair cdr)64f84e989bShshoexer cons(caddr_t car, pair cdr)
65e853bc5dShenning {
66d60fc4a4Skrw 	pair foo;
67d60fc4a4Skrw 
68d60fc4a4Skrw 	foo = calloc(1, sizeof *foo);
69e853bc5dShenning 	if (!foo)
70*c525a185Skrw 		fatalx("no memory for cons.");
71e853bc5dShenning 	foo->car = car;
72e853bc5dShenning 	foo->cdr = cdr;
73e853bc5dShenning 	return foo;
74e853bc5dShenning }
75e853bc5dShenning 
76f84e989bShshoexer struct tree_cache *
tree_cache(struct tree * tree)77f84e989bShshoexer tree_cache(struct tree *tree)
78e853bc5dShenning {
79e853bc5dShenning 	struct tree_cache	*tc;
80e853bc5dShenning 
81e853bc5dShenning 	tc = new_tree_cache("tree_cache");
82e853bc5dShenning 	if (!tc)
83e853bc5dShenning 		return 0;
848e1c2028Sderaadt 	tc->value = NULL;
85e853bc5dShenning 	tc->len = tc->buf_size = 0;
86e853bc5dShenning 	tc->timeout = 0;
87e853bc5dShenning 	tc->tree = tree;
88e853bc5dShenning 	return tc;
89e853bc5dShenning }
90e853bc5dShenning 
91f84e989bShshoexer struct tree *
tree_const(unsigned char * data,int len)92f84e989bShshoexer tree_const(unsigned char *data, int len)
93e853bc5dShenning {
941c629c6dSkrw 	unsigned char *d;
95e853bc5dShenning 	struct tree *nt;
96f84e989bShshoexer 
971c629c6dSkrw 	d = calloc(1, len);
981c629c6dSkrw 	nt = calloc(1, sizeof(struct tree));
991c629c6dSkrw 	if (!nt || !d)
100*c525a185Skrw 		fatalx("No memory for constant data tree node.");
1011c629c6dSkrw 
1021c629c6dSkrw 	memcpy(d, data, len);
1031c629c6dSkrw 
104e853bc5dShenning 	nt->op = TREE_CONST;
1051c629c6dSkrw 	nt->data.const_val.data = d;
106e853bc5dShenning 	nt->data.const_val.len = len;
1071c629c6dSkrw 
108e853bc5dShenning 	return nt;
109e853bc5dShenning }
110e853bc5dShenning 
111f84e989bShshoexer struct tree *
tree_concat(struct tree * left,struct tree * right)112f84e989bShshoexer tree_concat(struct tree *left, struct tree *right)
113e853bc5dShenning {
114e853bc5dShenning 	struct tree	*nt;
115e853bc5dShenning 
116f84e989bShshoexer 	/*
117f84e989bShshoexer 	 * If we're concatenating a null tree to a non-null tree, just
118f84e989bShshoexer 	 * return the non-null tree; if both trees are null, return
119f84e989bShshoexer 	 * a null tree.
120f84e989bShshoexer 	 */
121e853bc5dShenning 	if (!left)
122e853bc5dShenning 		return right;
123e853bc5dShenning 	if (!right)
124e853bc5dShenning 		return left;
125e853bc5dShenning 
126e853bc5dShenning 	/* If both trees are constant, combine them. */
127e853bc5dShenning 	if (left->op == TREE_CONST && right->op == TREE_CONST) {
128d60fc4a4Skrw 		unsigned char *buf;
129d60fc4a4Skrw 
130d60fc4a4Skrw 		buf = calloc(1, left->data.const_val.len
131d60fc4a4Skrw 		    + right->data.const_val.len);
132ea507cabSderaadt 
133e853bc5dShenning 		if (!buf)
134*c525a185Skrw 			fatalx("No memory to concatenate constants.");
135e853bc5dShenning 		memcpy(buf, left->data.const_val.data,
136e853bc5dShenning 		    left->data.const_val.len);
137e853bc5dShenning 		memcpy(buf + left->data.const_val.len,
138f84e989bShshoexer 		    right->data.const_val.data, right->data.const_val.len);
13992b98df2Skrw 		free(left->data.const_val.data);
14092b98df2Skrw 		free(right->data.const_val.data);
141e853bc5dShenning 		left->data.const_val.data = buf;
142e853bc5dShenning 		left->data.const_val.len += right->data.const_val.len;
14304b89754Skrw 		free(right);
144e853bc5dShenning 		return left;
145e853bc5dShenning 	}
146e853bc5dShenning 
147e853bc5dShenning 	/* Otherwise, allocate a new node to concatenate the two. */
1481c629c6dSkrw 	nt = calloc(1, sizeof(struct tree));
1491c629c6dSkrw 	if (!nt)
150*c525a185Skrw 		fatalx("No memory for data tree concatenation node.");
151e853bc5dShenning 	nt->op = TREE_CONCAT;
152e853bc5dShenning 	nt->data.concat.left = left;
153e853bc5dShenning 	nt->data.concat.right = right;
154e853bc5dShenning 	return nt;
155e853bc5dShenning }
156e853bc5dShenning 
157f84e989bShshoexer struct tree *
tree_limit(struct tree * tree,int limit)158f84e989bShshoexer tree_limit(struct tree *tree, int limit)
159e853bc5dShenning {
160e853bc5dShenning 	struct tree	*rv;
161e853bc5dShenning 
162e853bc5dShenning 	/* If the tree we're limiting is constant, limit it now. */
163e853bc5dShenning 	if (tree->op == TREE_CONST) {
164e853bc5dShenning 		if (tree->data.const_val.len > limit)
165e853bc5dShenning 			tree->data.const_val.len = limit;
166e853bc5dShenning 		return tree;
167e853bc5dShenning 	}
168e853bc5dShenning 
169e853bc5dShenning 	/* Otherwise, put in a node which enforces the limit on evaluation. */
1701c629c6dSkrw 	rv = calloc(1, sizeof(struct tree));
1711c629c6dSkrw 	if (!rv) {
172*c525a185Skrw 		log_warnx("No memory for data tree limit node.");
1738e1c2028Sderaadt 		return NULL;
1741c629c6dSkrw 	}
175e853bc5dShenning 	rv->op = TREE_LIMIT;
176e853bc5dShenning 	rv->data.limit.tree = tree;
177e853bc5dShenning 	rv->data.limit.limit = limit;
178e853bc5dShenning 	return rv;
179e853bc5dShenning }
180e853bc5dShenning 
181f84e989bShshoexer int
tree_evaluate(struct tree_cache * tree_cache)182f84e989bShshoexer tree_evaluate(struct tree_cache *tree_cache)
183e853bc5dShenning {
184e853bc5dShenning 	unsigned char	*bp = tree_cache->value;
185e853bc5dShenning 	int		 bc = tree_cache->buf_size;
186e853bc5dShenning 	int		 bufix = 0;
187e853bc5dShenning 
188f84e989bShshoexer 	/*
189f84e989bShshoexer 	 * If there's no tree associated with this cache, it evaluates to a
190f84e989bShshoexer 	 * constant and that was detected at startup.
191f84e989bShshoexer 	 */
192e853bc5dShenning 	if (!tree_cache->tree)
193e853bc5dShenning 		return 1;
194e853bc5dShenning 
195e853bc5dShenning 	/* Try to evaluate the tree without allocating more memory... */
196e853bc5dShenning 	tree_cache->timeout = tree_evaluate_recurse(&bufix, &bp, &bc,
197e853bc5dShenning 	    tree_cache->tree);
198e853bc5dShenning 
199e853bc5dShenning 	/* No additional allocation needed? */
200e853bc5dShenning 	if (bufix <= bc) {
201e853bc5dShenning 		tree_cache->len = bufix;
202e853bc5dShenning 		return 1;
203e853bc5dShenning 	}
204e853bc5dShenning 
205f84e989bShshoexer 	/*
206f84e989bShshoexer 	 * If we can't allocate more memory, return with what we
207f84e989bShshoexer 	 * have (maybe nothing).
208f84e989bShshoexer 	 */
209d60fc4a4Skrw 	bp = calloc(1, bufix);
210d60fc4a4Skrw 	if (!bp) {
211*c525a185Skrw 		log_warnx("no more memory for option data");
212e853bc5dShenning 		return 0;
213d60fc4a4Skrw 	}
214e853bc5dShenning 
215e853bc5dShenning 	/* Record the change in conditions... */
216e853bc5dShenning 	bc = bufix;
217e853bc5dShenning 	bufix = 0;
218e853bc5dShenning 
219f84e989bShshoexer 	/*
220f84e989bShshoexer 	 * Note that the size of the result shouldn't change on the
221f84e989bShshoexer 	 * second call to tree_evaluate_recurse, since we haven't
222f84e989bShshoexer 	 * changed the ``current'' time.
223f84e989bShshoexer 	 */
224e853bc5dShenning 	tree_evaluate_recurse(&bufix, &bp, &bc, tree_cache->tree);
225e853bc5dShenning 
226f84e989bShshoexer 	/*
227f84e989bShshoexer 	 * Free the old buffer if needed, then store the new buffer
228f84e989bShshoexer 	 * location and size and return.
229f84e989bShshoexer 	 */
23092b98df2Skrw 	free(tree_cache->value);
231e853bc5dShenning 	tree_cache->value = bp;
232e853bc5dShenning 	tree_cache->len = bufix;
233e853bc5dShenning 	tree_cache->buf_size = bc;
234e853bc5dShenning 	return 1;
235e853bc5dShenning }
236e853bc5dShenning 
237f84e989bShshoexer static time_t
tree_evaluate_recurse(int * bufix,unsigned char ** bufp,int * bufcount,struct tree * tree)238f84e989bShshoexer tree_evaluate_recurse(int *bufix, unsigned char **bufp,
239f84e989bShshoexer     int *bufcount, struct tree *tree)
240e853bc5dShenning {
241e853bc5dShenning 	int	limit;
242fbc5e94dShenning 	time_t	t1, t2;
243e853bc5dShenning 
244e853bc5dShenning 	switch (tree->op) {
245e853bc5dShenning 	case TREE_CONCAT:
246e853bc5dShenning 		t1 = tree_evaluate_recurse(bufix, bufp, bufcount,
247e853bc5dShenning 		    tree->data.concat.left);
248e853bc5dShenning 		t2 = tree_evaluate_recurse(bufix, bufp, bufcount,
249e853bc5dShenning 		    tree->data.concat.right);
250e853bc5dShenning 		if (t1 > t2)
251e853bc5dShenning 			return t2;
252e853bc5dShenning 		return t1;
253e853bc5dShenning 
254e853bc5dShenning 	case TREE_CONST:
255f84e989bShshoexer 		do_data_copy(bufix, bufp, bufcount, tree->data.const_val.data,
256e853bc5dShenning 		    tree->data.const_val.len);
257e853bc5dShenning 		t1 = MAX_TIME;
258e853bc5dShenning 		return t1;
259e853bc5dShenning 
260e853bc5dShenning 	case TREE_LIMIT:
261e853bc5dShenning 		limit = *bufix + tree->data.limit.limit;
262e853bc5dShenning 		t1 = tree_evaluate_recurse(bufix, bufp, bufcount,
263e853bc5dShenning 		    tree->data.limit.tree);
264e853bc5dShenning 		*bufix = limit;
265e853bc5dShenning 		return t1;
266e853bc5dShenning 
267e853bc5dShenning 	default:
268*c525a185Skrw 		log_warnx("Bad node id in tree: %d.", tree->op);
269e853bc5dShenning 		t1 = MAX_TIME;
270e853bc5dShenning 		return t1;
271e853bc5dShenning 	}
272e853bc5dShenning }
273e853bc5dShenning 
274f84e989bShshoexer static void
do_data_copy(int * bufix,unsigned char ** bufp,int * bufcount,unsigned char * data,int len)275f84e989bShshoexer do_data_copy(int *bufix, unsigned char **bufp, int *bufcount,
276f84e989bShshoexer     unsigned char *data, int len)
277e853bc5dShenning {
278e853bc5dShenning 	int	space = *bufcount - *bufix;
279e853bc5dShenning 
280e853bc5dShenning 	/* If there's more space than we need, use only what we need. */
281e853bc5dShenning 	if (space > len)
282e853bc5dShenning 		space = len;
283e853bc5dShenning 
284f84e989bShshoexer 	/*
285f84e989bShshoexer 	 * Copy as much data as will fit, then increment the buffer index
286f84e989bShshoexer 	 * by the amount we actually had to copy, which could be more.
287f84e989bShshoexer 	 */
288e853bc5dShenning 	if (space > 0)
289e853bc5dShenning 		memcpy(*bufp + *bufix, data, space);
290e853bc5dShenning 	*bufix += len;
291e853bc5dShenning }
292