xref: /minix3/external/bsd/dhcp/dist/common/options.c (revision 83ee113ee0d94f3844d44065af2311604e9a30ad)
1*83ee113eSDavid van Moolenbroek /*	$NetBSD: options.c,v 1.1.1.3 2014/07/12 11:57:46 spz Exp $	*/
2*83ee113eSDavid van Moolenbroek /* options.c
3*83ee113eSDavid van Moolenbroek 
4*83ee113eSDavid van Moolenbroek    DHCP options parsing and reassembly. */
5*83ee113eSDavid van Moolenbroek 
6*83ee113eSDavid van Moolenbroek /*
7*83ee113eSDavid van Moolenbroek  * Copyright (c) 2004-2012,2014 by Internet Systems Consortium, Inc. ("ISC")
8*83ee113eSDavid van Moolenbroek  * Copyright (c) 1995-2003 by Internet Software Consortium
9*83ee113eSDavid van Moolenbroek  *
10*83ee113eSDavid van Moolenbroek  * Permission to use, copy, modify, and distribute this software for any
11*83ee113eSDavid van Moolenbroek  * purpose with or without fee is hereby granted, provided that the above
12*83ee113eSDavid van Moolenbroek  * copyright notice and this permission notice appear in all copies.
13*83ee113eSDavid van Moolenbroek  *
14*83ee113eSDavid van Moolenbroek  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
15*83ee113eSDavid van Moolenbroek  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
16*83ee113eSDavid van Moolenbroek  * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
17*83ee113eSDavid van Moolenbroek  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
18*83ee113eSDavid van Moolenbroek  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
19*83ee113eSDavid van Moolenbroek  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
20*83ee113eSDavid van Moolenbroek  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21*83ee113eSDavid van Moolenbroek  *
22*83ee113eSDavid van Moolenbroek  *   Internet Systems Consortium, Inc.
23*83ee113eSDavid van Moolenbroek  *   950 Charter Street
24*83ee113eSDavid van Moolenbroek  *   Redwood City, CA 94063
25*83ee113eSDavid van Moolenbroek  *   <info@isc.org>
26*83ee113eSDavid van Moolenbroek  *   https://www.isc.org/
27*83ee113eSDavid van Moolenbroek  *
28*83ee113eSDavid van Moolenbroek  */
29*83ee113eSDavid van Moolenbroek 
30*83ee113eSDavid van Moolenbroek #include <sys/cdefs.h>
31*83ee113eSDavid van Moolenbroek __RCSID("$NetBSD: options.c,v 1.1.1.3 2014/07/12 11:57:46 spz Exp $");
32*83ee113eSDavid van Moolenbroek 
33*83ee113eSDavid van Moolenbroek #define DHCP_OPTION_DATA
34*83ee113eSDavid van Moolenbroek #include "dhcpd.h"
35*83ee113eSDavid van Moolenbroek #include <omapip/omapip_p.h>
36*83ee113eSDavid van Moolenbroek #include <limits.h>
37*83ee113eSDavid van Moolenbroek 
38*83ee113eSDavid van Moolenbroek struct option *vendor_cfg_option;
39*83ee113eSDavid van Moolenbroek 
40*83ee113eSDavid van Moolenbroek static int pretty_text(char **, char *, const unsigned char **,
41*83ee113eSDavid van Moolenbroek 			 const unsigned char *, int);
42*83ee113eSDavid van Moolenbroek static int pretty_domain(char **, char *, const unsigned char **,
43*83ee113eSDavid van Moolenbroek 			 const unsigned char *);
44*83ee113eSDavid van Moolenbroek static int prepare_option_buffer(struct universe *universe, struct buffer *bp,
45*83ee113eSDavid van Moolenbroek 				 unsigned char *buffer, unsigned length,
46*83ee113eSDavid van Moolenbroek 				 unsigned code, int terminatep,
47*83ee113eSDavid van Moolenbroek 				 struct option_cache **opp);
48*83ee113eSDavid van Moolenbroek 
49*83ee113eSDavid van Moolenbroek /* Parse all available options out of the specified packet. */
50*83ee113eSDavid van Moolenbroek 
parse_options(packet)51*83ee113eSDavid van Moolenbroek int parse_options (packet)
52*83ee113eSDavid van Moolenbroek 	struct packet *packet;
53*83ee113eSDavid van Moolenbroek {
54*83ee113eSDavid van Moolenbroek 	struct option_cache *op = (struct option_cache *)0;
55*83ee113eSDavid van Moolenbroek 
56*83ee113eSDavid van Moolenbroek 	/* Allocate a new option state. */
57*83ee113eSDavid van Moolenbroek 	if (!option_state_allocate (&packet -> options, MDL)) {
58*83ee113eSDavid van Moolenbroek 		packet -> options_valid = 0;
59*83ee113eSDavid van Moolenbroek 		return 0;
60*83ee113eSDavid van Moolenbroek 	}
61*83ee113eSDavid van Moolenbroek 
62*83ee113eSDavid van Moolenbroek 	/* If we don't see the magic cookie, there's nothing to parse. */
63*83ee113eSDavid van Moolenbroek 	if (memcmp (packet -> raw -> options, DHCP_OPTIONS_COOKIE, 4)) {
64*83ee113eSDavid van Moolenbroek 		packet -> options_valid = 0;
65*83ee113eSDavid van Moolenbroek 		return 1;
66*83ee113eSDavid van Moolenbroek 	}
67*83ee113eSDavid van Moolenbroek 
68*83ee113eSDavid van Moolenbroek 	/* Go through the options field, up to the end of the packet
69*83ee113eSDavid van Moolenbroek 	   or the End field. */
70*83ee113eSDavid van Moolenbroek 	if (!parse_option_buffer (packet -> options,
71*83ee113eSDavid van Moolenbroek 				  &packet -> raw -> options [4],
72*83ee113eSDavid van Moolenbroek 				  (packet -> packet_length -
73*83ee113eSDavid van Moolenbroek 				   DHCP_FIXED_NON_UDP - 4),
74*83ee113eSDavid van Moolenbroek 				  &dhcp_universe)) {
75*83ee113eSDavid van Moolenbroek 
76*83ee113eSDavid van Moolenbroek 		/* STSN servers have a bug where they send a mangled
77*83ee113eSDavid van Moolenbroek 		   domain-name option, and whatever is beyond that in
78*83ee113eSDavid van Moolenbroek 		   the packet is junk.   Microsoft clients accept this,
79*83ee113eSDavid van Moolenbroek 		   which is probably why whoever implemented the STSN
80*83ee113eSDavid van Moolenbroek 		   server isn't aware of the problem yet.   To work around
81*83ee113eSDavid van Moolenbroek 		   this, we will accept corrupt packets from the server if
82*83ee113eSDavid van Moolenbroek 		   they contain a valid DHCP_MESSAGE_TYPE option, but
83*83ee113eSDavid van Moolenbroek 		   will not accept any corrupt client packets (the ISC DHCP
84*83ee113eSDavid van Moolenbroek 		   server is sufficiently widely used that it is probably
85*83ee113eSDavid van Moolenbroek 		   beneficial for it to be picky) and will not accept
86*83ee113eSDavid van Moolenbroek 		   packets whose type can't be determined. */
87*83ee113eSDavid van Moolenbroek 
88*83ee113eSDavid van Moolenbroek 		if ((op = lookup_option (&dhcp_universe, packet -> options,
89*83ee113eSDavid van Moolenbroek 					 DHO_DHCP_MESSAGE_TYPE))) {
90*83ee113eSDavid van Moolenbroek 			if (!op -> data.data ||
91*83ee113eSDavid van Moolenbroek 			    (op -> data.data [0] != DHCPOFFER &&
92*83ee113eSDavid van Moolenbroek 			     op -> data.data [0] != DHCPACK &&
93*83ee113eSDavid van Moolenbroek 			     op -> data.data [0] != DHCPNAK))
94*83ee113eSDavid van Moolenbroek 				return 0;
95*83ee113eSDavid van Moolenbroek 		} else
96*83ee113eSDavid van Moolenbroek 			return 0;
97*83ee113eSDavid van Moolenbroek 	}
98*83ee113eSDavid van Moolenbroek 
99*83ee113eSDavid van Moolenbroek 	/* If we parsed a DHCP Option Overload option, parse more
100*83ee113eSDavid van Moolenbroek 	   options out of the buffer(s) containing them. */
101*83ee113eSDavid van Moolenbroek 	if ((op = lookup_option (&dhcp_universe, packet -> options,
102*83ee113eSDavid van Moolenbroek 				 DHO_DHCP_OPTION_OVERLOAD))) {
103*83ee113eSDavid van Moolenbroek 		if (op -> data.data [0] & 1) {
104*83ee113eSDavid van Moolenbroek 			if (!parse_option_buffer
105*83ee113eSDavid van Moolenbroek 			    (packet -> options,
106*83ee113eSDavid van Moolenbroek 			     (unsigned char *)packet -> raw -> file,
107*83ee113eSDavid van Moolenbroek 			     sizeof packet -> raw -> file,
108*83ee113eSDavid van Moolenbroek 			     &dhcp_universe))
109*83ee113eSDavid van Moolenbroek 				return 0;
110*83ee113eSDavid van Moolenbroek 		}
111*83ee113eSDavid van Moolenbroek 		if (op -> data.data [0] & 2) {
112*83ee113eSDavid van Moolenbroek 			if (!parse_option_buffer
113*83ee113eSDavid van Moolenbroek 			    (packet -> options,
114*83ee113eSDavid van Moolenbroek 			     (unsigned char *)packet -> raw -> sname,
115*83ee113eSDavid van Moolenbroek 			     sizeof packet -> raw -> sname,
116*83ee113eSDavid van Moolenbroek 			     &dhcp_universe))
117*83ee113eSDavid van Moolenbroek 				return 0;
118*83ee113eSDavid van Moolenbroek 		}
119*83ee113eSDavid van Moolenbroek 	}
120*83ee113eSDavid van Moolenbroek 	packet -> options_valid = 1;
121*83ee113eSDavid van Moolenbroek 	return 1;
122*83ee113eSDavid van Moolenbroek }
123*83ee113eSDavid van Moolenbroek 
124*83ee113eSDavid van Moolenbroek /* Parse options out of the specified buffer, storing addresses of option
125*83ee113eSDavid van Moolenbroek  * values in packet->options.
126*83ee113eSDavid van Moolenbroek  */
parse_option_buffer(options,buffer,length,universe)127*83ee113eSDavid van Moolenbroek int parse_option_buffer (options, buffer, length, universe)
128*83ee113eSDavid van Moolenbroek 	struct option_state *options;
129*83ee113eSDavid van Moolenbroek 	const unsigned char *buffer;
130*83ee113eSDavid van Moolenbroek 	unsigned length;
131*83ee113eSDavid van Moolenbroek 	struct universe *universe;
132*83ee113eSDavid van Moolenbroek {
133*83ee113eSDavid van Moolenbroek 	unsigned len, offset;
134*83ee113eSDavid van Moolenbroek 	unsigned code;
135*83ee113eSDavid van Moolenbroek 	struct option_cache *op = NULL, *nop = NULL;
136*83ee113eSDavid van Moolenbroek 	struct buffer *bp = (struct buffer *)0;
137*83ee113eSDavid van Moolenbroek 	struct option *option = NULL;
138*83ee113eSDavid van Moolenbroek 	char *reason = "general failure";
139*83ee113eSDavid van Moolenbroek 
140*83ee113eSDavid van Moolenbroek 	if (!buffer_allocate (&bp, length, MDL)) {
141*83ee113eSDavid van Moolenbroek 		log_error ("no memory for option buffer.");
142*83ee113eSDavid van Moolenbroek 		return 0;
143*83ee113eSDavid van Moolenbroek 	}
144*83ee113eSDavid van Moolenbroek 	memcpy (bp -> data, buffer, length);
145*83ee113eSDavid van Moolenbroek 
146*83ee113eSDavid van Moolenbroek 	for (offset = 0;
147*83ee113eSDavid van Moolenbroek 	     (offset + universe->tag_size) <= length &&
148*83ee113eSDavid van Moolenbroek 	     (code = universe->get_tag(buffer + offset)) != universe->end; ) {
149*83ee113eSDavid van Moolenbroek 		offset += universe->tag_size;
150*83ee113eSDavid van Moolenbroek 
151*83ee113eSDavid van Moolenbroek 		/* Pad options don't have a length - just skip them. */
152*83ee113eSDavid van Moolenbroek 		if (code == DHO_PAD)
153*83ee113eSDavid van Moolenbroek 			continue;
154*83ee113eSDavid van Moolenbroek 
155*83ee113eSDavid van Moolenbroek 		/* Don't look for length if the buffer isn't that big. */
156*83ee113eSDavid van Moolenbroek 		if ((offset + universe->length_size) > length) {
157*83ee113eSDavid van Moolenbroek 			reason = "code tag at end of buffer - missing "
158*83ee113eSDavid van Moolenbroek 				 "length field";
159*83ee113eSDavid van Moolenbroek 			goto bogus;
160*83ee113eSDavid van Moolenbroek 		}
161*83ee113eSDavid van Moolenbroek 
162*83ee113eSDavid van Moolenbroek 		/* All other fields (except PAD and END handled above)
163*83ee113eSDavid van Moolenbroek 		 * have a length field, unless it's a DHCPv6 zero-length
164*83ee113eSDavid van Moolenbroek 		 * options space (eg any of the enterprise-id'd options).
165*83ee113eSDavid van Moolenbroek 		 *
166*83ee113eSDavid van Moolenbroek 		 * Zero-length-size option spaces basically consume the
167*83ee113eSDavid van Moolenbroek 		 * entire options buffer, so have at it.
168*83ee113eSDavid van Moolenbroek 		 */
169*83ee113eSDavid van Moolenbroek 		if (universe->get_length != NULL)
170*83ee113eSDavid van Moolenbroek 			len = universe->get_length(buffer + offset);
171*83ee113eSDavid van Moolenbroek 		else if (universe->length_size == 0)
172*83ee113eSDavid van Moolenbroek 			len = length - universe->tag_size;
173*83ee113eSDavid van Moolenbroek 		else {
174*83ee113eSDavid van Moolenbroek 			log_fatal("Improperly configured option space(%s): "
175*83ee113eSDavid van Moolenbroek 				  "may not have a nonzero length size "
176*83ee113eSDavid van Moolenbroek 				  "AND a NULL get_length function.",
177*83ee113eSDavid van Moolenbroek 				  universe->name);
178*83ee113eSDavid van Moolenbroek 
179*83ee113eSDavid van Moolenbroek 			/* Silence compiler warnings. */
180*83ee113eSDavid van Moolenbroek 			return 0;
181*83ee113eSDavid van Moolenbroek 		}
182*83ee113eSDavid van Moolenbroek 
183*83ee113eSDavid van Moolenbroek 		offset += universe->length_size;
184*83ee113eSDavid van Moolenbroek 
185*83ee113eSDavid van Moolenbroek 		option_code_hash_lookup(&option, universe->code_hash, &code,
186*83ee113eSDavid van Moolenbroek 					0, MDL);
187*83ee113eSDavid van Moolenbroek 
188*83ee113eSDavid van Moolenbroek 		/* If the length is outrageous, the options are bad. */
189*83ee113eSDavid van Moolenbroek 		if (offset + len > length) {
190*83ee113eSDavid van Moolenbroek 			reason = "option length exceeds option buffer length";
191*83ee113eSDavid van Moolenbroek 		      bogus:
192*83ee113eSDavid van Moolenbroek 			log_error("parse_option_buffer: malformed option "
193*83ee113eSDavid van Moolenbroek 				  "%s.%s (code %u): %s.", universe->name,
194*83ee113eSDavid van Moolenbroek 				  option ? option->name : "<unknown>",
195*83ee113eSDavid van Moolenbroek 				  code, reason);
196*83ee113eSDavid van Moolenbroek 			buffer_dereference (&bp, MDL);
197*83ee113eSDavid van Moolenbroek 			return 0;
198*83ee113eSDavid van Moolenbroek 		}
199*83ee113eSDavid van Moolenbroek 
200*83ee113eSDavid van Moolenbroek 		/* If the option contains an encapsulation, parse it.   If
201*83ee113eSDavid van Moolenbroek 		   the parse fails, or the option isn't an encapsulation (by
202*83ee113eSDavid van Moolenbroek 		   far the most common case), or the option isn't entirely
203*83ee113eSDavid van Moolenbroek 		   an encapsulation, keep the raw data as well. */
204*83ee113eSDavid van Moolenbroek 		if (!(option &&
205*83ee113eSDavid van Moolenbroek 		      (option->format[0] == 'e' ||
206*83ee113eSDavid van Moolenbroek 		       option->format[0] == 'E') &&
207*83ee113eSDavid van Moolenbroek 		      (parse_encapsulated_suboptions(options, option,
208*83ee113eSDavid van Moolenbroek 						     bp->data + offset, len,
209*83ee113eSDavid van Moolenbroek 						     universe, NULL)))) {
210*83ee113eSDavid van Moolenbroek 			op = lookup_option(universe, options, code);
211*83ee113eSDavid van Moolenbroek 
212*83ee113eSDavid van Moolenbroek 			if (op != NULL && universe->concat_duplicates) {
213*83ee113eSDavid van Moolenbroek 				struct data_string new;
214*83ee113eSDavid van Moolenbroek 				memset(&new, 0, sizeof new);
215*83ee113eSDavid van Moolenbroek 				if (!buffer_allocate(&new.buffer,
216*83ee113eSDavid van Moolenbroek 						     op->data.len + len,
217*83ee113eSDavid van Moolenbroek 						     MDL)) {
218*83ee113eSDavid van Moolenbroek 					log_error("parse_option_buffer: "
219*83ee113eSDavid van Moolenbroek 						  "No memory.");
220*83ee113eSDavid van Moolenbroek 					buffer_dereference(&bp, MDL);
221*83ee113eSDavid van Moolenbroek 					return 0;
222*83ee113eSDavid van Moolenbroek 				}
223*83ee113eSDavid van Moolenbroek 				/* Copy old option to new data object. */
224*83ee113eSDavid van Moolenbroek 				memcpy(new.buffer->data, op->data.data,
225*83ee113eSDavid van Moolenbroek 					op->data.len);
226*83ee113eSDavid van Moolenbroek 				/* Concat new option behind old. */
227*83ee113eSDavid van Moolenbroek 				memcpy(new.buffer->data + op->data.len,
228*83ee113eSDavid van Moolenbroek 					bp->data + offset, len);
229*83ee113eSDavid van Moolenbroek 				new.len = op->data.len + len;
230*83ee113eSDavid van Moolenbroek 				new.data = new.buffer->data;
231*83ee113eSDavid van Moolenbroek 				/* Save new concat'd object. */
232*83ee113eSDavid van Moolenbroek 				data_string_forget(&op->data, MDL);
233*83ee113eSDavid van Moolenbroek 				data_string_copy(&op->data, &new, MDL);
234*83ee113eSDavid van Moolenbroek 				data_string_forget(&new, MDL);
235*83ee113eSDavid van Moolenbroek 			} else if (op != NULL) {
236*83ee113eSDavid van Moolenbroek 				/* We must append this statement onto the
237*83ee113eSDavid van Moolenbroek 				 * end of the list.
238*83ee113eSDavid van Moolenbroek 				 */
239*83ee113eSDavid van Moolenbroek 				while (op->next != NULL)
240*83ee113eSDavid van Moolenbroek 					op = op->next;
241*83ee113eSDavid van Moolenbroek 
242*83ee113eSDavid van Moolenbroek 				if (!option_cache_allocate(&nop, MDL)) {
243*83ee113eSDavid van Moolenbroek 					log_error("parse_option_buffer: "
244*83ee113eSDavid van Moolenbroek 						  "No memory.");
245*83ee113eSDavid van Moolenbroek 					buffer_dereference(&bp, MDL);
246*83ee113eSDavid van Moolenbroek 					return 0;
247*83ee113eSDavid van Moolenbroek 				}
248*83ee113eSDavid van Moolenbroek 
249*83ee113eSDavid van Moolenbroek 				option_reference(&nop->option, op->option, MDL);
250*83ee113eSDavid van Moolenbroek 
251*83ee113eSDavid van Moolenbroek 				nop->data.buffer = NULL;
252*83ee113eSDavid van Moolenbroek 				buffer_reference(&nop->data.buffer, bp, MDL);
253*83ee113eSDavid van Moolenbroek 				nop->data.data = bp->data + offset;
254*83ee113eSDavid van Moolenbroek 				nop->data.len = len;
255*83ee113eSDavid van Moolenbroek 
256*83ee113eSDavid van Moolenbroek 				option_cache_reference(&op->next, nop, MDL);
257*83ee113eSDavid van Moolenbroek 				option_cache_dereference(&nop, MDL);
258*83ee113eSDavid van Moolenbroek 			} else {
259*83ee113eSDavid van Moolenbroek 				if (save_option_buffer(universe, options, bp,
260*83ee113eSDavid van Moolenbroek 						       bp->data + offset, len,
261*83ee113eSDavid van Moolenbroek 						       code, 1) == 0) {
262*83ee113eSDavid van Moolenbroek 					log_error("parse_option_buffer: "
263*83ee113eSDavid van Moolenbroek 						  "save_option_buffer failed");
264*83ee113eSDavid van Moolenbroek 					buffer_dereference(&bp, MDL);
265*83ee113eSDavid van Moolenbroek 					return 0;
266*83ee113eSDavid van Moolenbroek 				}
267*83ee113eSDavid van Moolenbroek 			}
268*83ee113eSDavid van Moolenbroek 		}
269*83ee113eSDavid van Moolenbroek 		option_dereference(&option, MDL);
270*83ee113eSDavid van Moolenbroek 		offset += len;
271*83ee113eSDavid van Moolenbroek 	}
272*83ee113eSDavid van Moolenbroek 	buffer_dereference (&bp, MDL);
273*83ee113eSDavid van Moolenbroek 	return 1;
274*83ee113eSDavid van Moolenbroek }
275*83ee113eSDavid van Moolenbroek 
276*83ee113eSDavid van Moolenbroek /* If an option in an option buffer turns out to be an encapsulation,
277*83ee113eSDavid van Moolenbroek    figure out what to do.   If we don't know how to de-encapsulate it,
278*83ee113eSDavid van Moolenbroek    or it's not well-formed, return zero; otherwise, return 1, indicating
279*83ee113eSDavid van Moolenbroek    that we succeeded in de-encapsulating it. */
280*83ee113eSDavid van Moolenbroek 
find_option_universe(struct option * eopt,const char * uname)281*83ee113eSDavid van Moolenbroek struct universe *find_option_universe (struct option *eopt, const char *uname)
282*83ee113eSDavid van Moolenbroek {
283*83ee113eSDavid van Moolenbroek 	int i;
284*83ee113eSDavid van Moolenbroek 	char *s, *t;
285*83ee113eSDavid van Moolenbroek 	struct universe *universe = (struct universe *)0;
286*83ee113eSDavid van Moolenbroek 
287*83ee113eSDavid van Moolenbroek 	/* Look for the E option in the option format. */
288*83ee113eSDavid van Moolenbroek 	s = strchr (eopt -> format, 'E');
289*83ee113eSDavid van Moolenbroek 	if (!s) {
290*83ee113eSDavid van Moolenbroek 		log_error ("internal encapsulation format error 1.");
291*83ee113eSDavid van Moolenbroek 		return 0;
292*83ee113eSDavid van Moolenbroek 	}
293*83ee113eSDavid van Moolenbroek 	/* Look for the universe name in the option format. */
294*83ee113eSDavid van Moolenbroek 	t = strchr (++s, '.');
295*83ee113eSDavid van Moolenbroek 	/* If there was no trailing '.', or there's something after the
296*83ee113eSDavid van Moolenbroek 	   trailing '.', the option is bogus and we can't use it. */
297*83ee113eSDavid van Moolenbroek 	if (!t || t [1]) {
298*83ee113eSDavid van Moolenbroek 		log_error ("internal encapsulation format error 2.");
299*83ee113eSDavid van Moolenbroek 		return 0;
300*83ee113eSDavid van Moolenbroek 	}
301*83ee113eSDavid van Moolenbroek 	if (t == s && uname) {
302*83ee113eSDavid van Moolenbroek 		for (i = 0; i < universe_count; i++) {
303*83ee113eSDavid van Moolenbroek 			if (!strcmp (universes [i] -> name, uname)) {
304*83ee113eSDavid van Moolenbroek 				universe = universes [i];
305*83ee113eSDavid van Moolenbroek 				break;
306*83ee113eSDavid van Moolenbroek 			}
307*83ee113eSDavid van Moolenbroek 		}
308*83ee113eSDavid van Moolenbroek 	} else if (t != s) {
309*83ee113eSDavid van Moolenbroek 		for (i = 0; i < universe_count; i++) {
310*83ee113eSDavid van Moolenbroek 			if (strlen (universes [i] -> name) == t - s &&
311*83ee113eSDavid van Moolenbroek 			    !memcmp (universes [i] -> name,
312*83ee113eSDavid van Moolenbroek 				     s, (unsigned)(t - s))) {
313*83ee113eSDavid van Moolenbroek 				universe = universes [i];
314*83ee113eSDavid van Moolenbroek 				break;
315*83ee113eSDavid van Moolenbroek 			}
316*83ee113eSDavid van Moolenbroek 		}
317*83ee113eSDavid van Moolenbroek 	}
318*83ee113eSDavid van Moolenbroek 	return universe;
319*83ee113eSDavid van Moolenbroek }
320*83ee113eSDavid van Moolenbroek 
321*83ee113eSDavid van Moolenbroek /* If an option in an option buffer turns out to be an encapsulation,
322*83ee113eSDavid van Moolenbroek    figure out what to do.   If we don't know how to de-encapsulate it,
323*83ee113eSDavid van Moolenbroek    or it's not well-formed, return zero; otherwise, return 1, indicating
324*83ee113eSDavid van Moolenbroek    that we succeeded in de-encapsulating it. */
325*83ee113eSDavid van Moolenbroek 
parse_encapsulated_suboptions(struct option_state * options,struct option * eopt,const unsigned char * buffer,unsigned len,struct universe * eu,const char * uname)326*83ee113eSDavid van Moolenbroek int parse_encapsulated_suboptions (struct option_state *options,
327*83ee113eSDavid van Moolenbroek 				   struct option *eopt,
328*83ee113eSDavid van Moolenbroek 				   const unsigned char *buffer,
329*83ee113eSDavid van Moolenbroek 				   unsigned len, struct universe *eu,
330*83ee113eSDavid van Moolenbroek 				   const char *uname)
331*83ee113eSDavid van Moolenbroek {
332*83ee113eSDavid van Moolenbroek 	int i;
333*83ee113eSDavid van Moolenbroek 	struct universe *universe = find_option_universe (eopt, uname);
334*83ee113eSDavid van Moolenbroek 
335*83ee113eSDavid van Moolenbroek 	/* If we didn't find the universe, we can't do anything with it
336*83ee113eSDavid van Moolenbroek 	   right now (e.g., we can't decode vendor options until we've
337*83ee113eSDavid van Moolenbroek 	   decoded the packet and executed the scopes that it matches). */
338*83ee113eSDavid van Moolenbroek 	if (!universe)
339*83ee113eSDavid van Moolenbroek 		return 0;
340*83ee113eSDavid van Moolenbroek 
341*83ee113eSDavid van Moolenbroek 	/* If we don't have a decoding function for it, we can't decode
342*83ee113eSDavid van Moolenbroek 	   it. */
343*83ee113eSDavid van Moolenbroek 	if (!universe -> decode)
344*83ee113eSDavid van Moolenbroek 		return 0;
345*83ee113eSDavid van Moolenbroek 
346*83ee113eSDavid van Moolenbroek 	i = (*universe -> decode) (options, buffer, len, universe);
347*83ee113eSDavid van Moolenbroek 
348*83ee113eSDavid van Moolenbroek 	/* If there is stuff before the suboptions, we have to keep it. */
349*83ee113eSDavid van Moolenbroek 	if (eopt -> format [0] != 'E')
350*83ee113eSDavid van Moolenbroek 		return 0;
351*83ee113eSDavid van Moolenbroek 	/* Otherwise, return the status of the decode function. */
352*83ee113eSDavid van Moolenbroek 	return i;
353*83ee113eSDavid van Moolenbroek }
354*83ee113eSDavid van Moolenbroek 
fqdn_universe_decode(struct option_state * options,const unsigned char * buffer,unsigned length,struct universe * u)355*83ee113eSDavid van Moolenbroek int fqdn_universe_decode (struct option_state *options,
356*83ee113eSDavid van Moolenbroek 			  const unsigned char *buffer,
357*83ee113eSDavid van Moolenbroek 			  unsigned length, struct universe *u)
358*83ee113eSDavid van Moolenbroek {
359*83ee113eSDavid van Moolenbroek 	struct buffer *bp = (struct buffer *)0;
360*83ee113eSDavid van Moolenbroek 
361*83ee113eSDavid van Moolenbroek 	/* FQDN options have to be at least four bytes long. */
362*83ee113eSDavid van Moolenbroek 	if (length < 3)
363*83ee113eSDavid van Moolenbroek 		return 0;
364*83ee113eSDavid van Moolenbroek 
365*83ee113eSDavid van Moolenbroek 	/* Save the contents of the option in a buffer. */
366*83ee113eSDavid van Moolenbroek 	if (!buffer_allocate (&bp, length + 4, MDL)) {
367*83ee113eSDavid van Moolenbroek 		log_error ("no memory for option buffer.");
368*83ee113eSDavid van Moolenbroek 		return 0;
369*83ee113eSDavid van Moolenbroek 	}
370*83ee113eSDavid van Moolenbroek 	memcpy (&bp -> data [3], buffer + 1, length - 1);
371*83ee113eSDavid van Moolenbroek 
372*83ee113eSDavid van Moolenbroek 	if (buffer [0] & 4)	/* encoded */
373*83ee113eSDavid van Moolenbroek 		bp -> data [0] = 1;
374*83ee113eSDavid van Moolenbroek 	else
375*83ee113eSDavid van Moolenbroek 		bp -> data [0] = 0;
376*83ee113eSDavid van Moolenbroek 	if (!save_option_buffer(&fqdn_universe, options, bp,
377*83ee113eSDavid van Moolenbroek 				bp->data, 1, FQDN_ENCODED, 0)) {
378*83ee113eSDavid van Moolenbroek 	      bad:
379*83ee113eSDavid van Moolenbroek 		buffer_dereference (&bp, MDL);
380*83ee113eSDavid van Moolenbroek 		return 0;
381*83ee113eSDavid van Moolenbroek 	}
382*83ee113eSDavid van Moolenbroek 
383*83ee113eSDavid van Moolenbroek 	if (buffer [0] & 1)	/* server-update */
384*83ee113eSDavid van Moolenbroek 		bp -> data [2] = 1;
385*83ee113eSDavid van Moolenbroek 	else
386*83ee113eSDavid van Moolenbroek 		bp -> data [2] = 0;
387*83ee113eSDavid van Moolenbroek 	if (buffer [0] & 2)	/* no-client-update */
388*83ee113eSDavid van Moolenbroek 		bp -> data [1] = 1;
389*83ee113eSDavid van Moolenbroek 	else
390*83ee113eSDavid van Moolenbroek 		bp -> data [1] = 0;
391*83ee113eSDavid van Moolenbroek 
392*83ee113eSDavid van Moolenbroek 	/* XXX Ideally we should store the name in DNS format, so if the
393*83ee113eSDavid van Moolenbroek 	   XXX label isn't in DNS format, we convert it to DNS format,
394*83ee113eSDavid van Moolenbroek 	   XXX rather than converting labels specified in DNS format to
395*83ee113eSDavid van Moolenbroek 	   XXX the plain ASCII representation.   But that's hard, so
396*83ee113eSDavid van Moolenbroek 	   XXX not now. */
397*83ee113eSDavid van Moolenbroek 
398*83ee113eSDavid van Moolenbroek 	/* Not encoded using DNS format? */
399*83ee113eSDavid van Moolenbroek 	if (!bp -> data [0]) {
400*83ee113eSDavid van Moolenbroek 		unsigned i;
401*83ee113eSDavid van Moolenbroek 
402*83ee113eSDavid van Moolenbroek 		/* Some broken clients NUL-terminate this option. */
403*83ee113eSDavid van Moolenbroek 		if (buffer [length - 1] == 0) {
404*83ee113eSDavid van Moolenbroek 			--length;
405*83ee113eSDavid van Moolenbroek 			bp -> data [1] = 1;
406*83ee113eSDavid van Moolenbroek 		}
407*83ee113eSDavid van Moolenbroek 
408*83ee113eSDavid van Moolenbroek 		/* Determine the length of the hostname component of the
409*83ee113eSDavid van Moolenbroek 		   name.  If the name contains no '.' character, it
410*83ee113eSDavid van Moolenbroek 		   represents a non-qualified label. */
411*83ee113eSDavid van Moolenbroek 		for (i = 3; i < length && buffer [i] != '.'; i++);
412*83ee113eSDavid van Moolenbroek 		i -= 3;
413*83ee113eSDavid van Moolenbroek 
414*83ee113eSDavid van Moolenbroek 		/* Note: If the client sends a FQDN, the first '.' will
415*83ee113eSDavid van Moolenbroek 		   be used as a NUL terminator for the hostname. */
416*83ee113eSDavid van Moolenbroek 		if (i && (!save_option_buffer(&fqdn_universe, options, bp,
417*83ee113eSDavid van Moolenbroek 					      &bp->data[5], i,
418*83ee113eSDavid van Moolenbroek 					      FQDN_HOSTNAME, 0)))
419*83ee113eSDavid van Moolenbroek 			goto bad;
420*83ee113eSDavid van Moolenbroek 		/* Note: If the client sends a single label, the
421*83ee113eSDavid van Moolenbroek 		   FQDN_DOMAINNAME option won't be set. */
422*83ee113eSDavid van Moolenbroek 		if (length > 4 + i &&
423*83ee113eSDavid van Moolenbroek 		    (!save_option_buffer(&fqdn_universe, options, bp,
424*83ee113eSDavid van Moolenbroek 					 &bp -> data[6 + i], length - 4 - i,
425*83ee113eSDavid van Moolenbroek 					 FQDN_DOMAINNAME, 1)))
426*83ee113eSDavid van Moolenbroek 			goto bad;
427*83ee113eSDavid van Moolenbroek 		/* Also save the whole name. */
428*83ee113eSDavid van Moolenbroek 		if (length > 3) {
429*83ee113eSDavid van Moolenbroek 			if (!save_option_buffer(&fqdn_universe, options, bp,
430*83ee113eSDavid van Moolenbroek 						&bp -> data [5], length - 3,
431*83ee113eSDavid van Moolenbroek 						FQDN_FQDN, 1))
432*83ee113eSDavid van Moolenbroek 				goto bad;
433*83ee113eSDavid van Moolenbroek 		}
434*83ee113eSDavid van Moolenbroek 	} else {
435*83ee113eSDavid van Moolenbroek 		unsigned len;
436*83ee113eSDavid van Moolenbroek 		unsigned total_len = 0;
437*83ee113eSDavid van Moolenbroek 		unsigned first_len = 0;
438*83ee113eSDavid van Moolenbroek 		int terminated = 0;
439*83ee113eSDavid van Moolenbroek 		unsigned char *s;
440*83ee113eSDavid van Moolenbroek 
441*83ee113eSDavid van Moolenbroek 		s = &bp -> data[5];
442*83ee113eSDavid van Moolenbroek 
443*83ee113eSDavid van Moolenbroek 		while (s < &bp -> data[0] + length + 2) {
444*83ee113eSDavid van Moolenbroek 			len = *s;
445*83ee113eSDavid van Moolenbroek 			if (len > 63) {
446*83ee113eSDavid van Moolenbroek 				log_info ("fancy bits in fqdn option");
447*83ee113eSDavid van Moolenbroek 				return 0;
448*83ee113eSDavid van Moolenbroek 			}
449*83ee113eSDavid van Moolenbroek 			if (len == 0) {
450*83ee113eSDavid van Moolenbroek 				terminated = 1;
451*83ee113eSDavid van Moolenbroek 				break;
452*83ee113eSDavid van Moolenbroek 			}
453*83ee113eSDavid van Moolenbroek 			if (s + len > &bp -> data [0] + length + 3) {
454*83ee113eSDavid van Moolenbroek 				log_info ("fqdn tag longer than buffer");
455*83ee113eSDavid van Moolenbroek 				return 0;
456*83ee113eSDavid van Moolenbroek 			}
457*83ee113eSDavid van Moolenbroek 
458*83ee113eSDavid van Moolenbroek 			if (first_len == 0) {
459*83ee113eSDavid van Moolenbroek 				first_len = len;
460*83ee113eSDavid van Moolenbroek 			}
461*83ee113eSDavid van Moolenbroek 
462*83ee113eSDavid van Moolenbroek 			*s = '.';
463*83ee113eSDavid van Moolenbroek 			s += len + 1;
464*83ee113eSDavid van Moolenbroek 			total_len += len + 1;
465*83ee113eSDavid van Moolenbroek 		}
466*83ee113eSDavid van Moolenbroek 
467*83ee113eSDavid van Moolenbroek 		/* We wind up with a length that's one too many because
468*83ee113eSDavid van Moolenbroek 		   we shouldn't increment for the last label, but there's
469*83ee113eSDavid van Moolenbroek 		   no way to tell we're at the last label until we exit
470*83ee113eSDavid van Moolenbroek 		   the loop.   :'*/
471*83ee113eSDavid van Moolenbroek 		if (total_len > 0)
472*83ee113eSDavid van Moolenbroek 			total_len--;
473*83ee113eSDavid van Moolenbroek 
474*83ee113eSDavid van Moolenbroek 		if (!terminated) {
475*83ee113eSDavid van Moolenbroek 			first_len = total_len;
476*83ee113eSDavid van Moolenbroek 		}
477*83ee113eSDavid van Moolenbroek 
478*83ee113eSDavid van Moolenbroek 		if (first_len > 0 &&
479*83ee113eSDavid van Moolenbroek 		    !save_option_buffer(&fqdn_universe, options, bp,
480*83ee113eSDavid van Moolenbroek 					&bp -> data[6], first_len,
481*83ee113eSDavid van Moolenbroek 					FQDN_HOSTNAME, 0))
482*83ee113eSDavid van Moolenbroek 			goto bad;
483*83ee113eSDavid van Moolenbroek 		if (total_len > 0 && first_len != total_len) {
484*83ee113eSDavid van Moolenbroek 			if (!save_option_buffer(&fqdn_universe, options, bp,
485*83ee113eSDavid van Moolenbroek 						&bp->data[6 + first_len],
486*83ee113eSDavid van Moolenbroek 						total_len - first_len,
487*83ee113eSDavid van Moolenbroek 						FQDN_DOMAINNAME, 1))
488*83ee113eSDavid van Moolenbroek 				goto bad;
489*83ee113eSDavid van Moolenbroek 		}
490*83ee113eSDavid van Moolenbroek 		if (total_len > 0)
491*83ee113eSDavid van Moolenbroek 			if (!save_option_buffer (&fqdn_universe, options, bp,
492*83ee113eSDavid van Moolenbroek 						 &bp -> data [6], total_len,
493*83ee113eSDavid van Moolenbroek 						 FQDN_FQDN, 1))
494*83ee113eSDavid van Moolenbroek 				goto bad;
495*83ee113eSDavid van Moolenbroek 	}
496*83ee113eSDavid van Moolenbroek 
497*83ee113eSDavid van Moolenbroek 	if (!save_option_buffer (&fqdn_universe, options, bp,
498*83ee113eSDavid van Moolenbroek 				 &bp -> data [1], 1,
499*83ee113eSDavid van Moolenbroek 				 FQDN_NO_CLIENT_UPDATE, 0))
500*83ee113eSDavid van Moolenbroek 	    goto bad;
501*83ee113eSDavid van Moolenbroek 	if (!save_option_buffer (&fqdn_universe, options, bp,
502*83ee113eSDavid van Moolenbroek 				 &bp -> data [2], 1,
503*83ee113eSDavid van Moolenbroek 				 FQDN_SERVER_UPDATE, 0))
504*83ee113eSDavid van Moolenbroek 		goto bad;
505*83ee113eSDavid van Moolenbroek 
506*83ee113eSDavid van Moolenbroek 	if (!save_option_buffer (&fqdn_universe, options, bp,
507*83ee113eSDavid van Moolenbroek 				 &bp -> data [3], 1,
508*83ee113eSDavid van Moolenbroek 				 FQDN_RCODE1, 0))
509*83ee113eSDavid van Moolenbroek 		goto bad;
510*83ee113eSDavid van Moolenbroek 	if (!save_option_buffer (&fqdn_universe, options, bp,
511*83ee113eSDavid van Moolenbroek 				 &bp -> data [4], 1,
512*83ee113eSDavid van Moolenbroek 				 FQDN_RCODE2, 0))
513*83ee113eSDavid van Moolenbroek 		goto bad;
514*83ee113eSDavid van Moolenbroek 
515*83ee113eSDavid van Moolenbroek 	buffer_dereference (&bp, MDL);
516*83ee113eSDavid van Moolenbroek 	return 1;
517*83ee113eSDavid van Moolenbroek }
518*83ee113eSDavid van Moolenbroek 
519*83ee113eSDavid van Moolenbroek /*
520*83ee113eSDavid van Moolenbroek  * Load all options into a buffer, and then split them out into the three
521*83ee113eSDavid van Moolenbroek  * separate fields in the dhcp packet (options, file, and sname) where
522*83ee113eSDavid van Moolenbroek  * options can be stored.
523*83ee113eSDavid van Moolenbroek  *
524*83ee113eSDavid van Moolenbroek  * returns 0 on error, length of packet on success
525*83ee113eSDavid van Moolenbroek  */
526*83ee113eSDavid van Moolenbroek int
cons_options(struct packet * inpacket,struct dhcp_packet * outpacket,struct lease * lease,struct client_state * client_state,int mms,struct option_state * in_options,struct option_state * cfg_options,struct binding_scope ** scope,int overload_avail,int terminate,int bootpp,struct data_string * prl,const char * vuname)527*83ee113eSDavid van Moolenbroek cons_options(struct packet *inpacket, struct dhcp_packet *outpacket,
528*83ee113eSDavid van Moolenbroek 	     struct lease *lease, struct client_state *client_state,
529*83ee113eSDavid van Moolenbroek 	     int mms, struct option_state *in_options,
530*83ee113eSDavid van Moolenbroek 	     struct option_state *cfg_options,
531*83ee113eSDavid van Moolenbroek 	     struct binding_scope **scope,
532*83ee113eSDavid van Moolenbroek 	     int overload_avail, int terminate, int bootpp,
533*83ee113eSDavid van Moolenbroek 	     struct data_string *prl, const char *vuname)
534*83ee113eSDavid van Moolenbroek {
535*83ee113eSDavid van Moolenbroek #define PRIORITY_COUNT 300
536*83ee113eSDavid van Moolenbroek 	unsigned priority_list[PRIORITY_COUNT];
537*83ee113eSDavid van Moolenbroek 	int priority_len;
538*83ee113eSDavid van Moolenbroek 	unsigned char buffer[4096], agentopts[1024];
539*83ee113eSDavid van Moolenbroek 	unsigned index = 0;
540*83ee113eSDavid van Moolenbroek 	unsigned mb_size = 0, mb_max = 0;
541*83ee113eSDavid van Moolenbroek 	unsigned option_size = 0, agent_size = 0;
542*83ee113eSDavid van Moolenbroek 	unsigned length;
543*83ee113eSDavid van Moolenbroek 	int i;
544*83ee113eSDavid van Moolenbroek 	struct option_cache *op;
545*83ee113eSDavid van Moolenbroek 	struct data_string ds;
546*83ee113eSDavid van Moolenbroek 	pair pp, *hash;
547*83ee113eSDavid van Moolenbroek 	int overload_used = 0;
548*83ee113eSDavid van Moolenbroek 	int of1 = 0, of2 = 0;
549*83ee113eSDavid van Moolenbroek 
550*83ee113eSDavid van Moolenbroek 	memset(&ds, 0, sizeof ds);
551*83ee113eSDavid van Moolenbroek 
552*83ee113eSDavid van Moolenbroek 	/*
553*83ee113eSDavid van Moolenbroek 	 * If there's a Maximum Message Size option in the incoming packet
554*83ee113eSDavid van Moolenbroek 	 * and no alternate maximum message size has been specified, or
555*83ee113eSDavid van Moolenbroek 	 * if the one specified in the packet is shorter than the
556*83ee113eSDavid van Moolenbroek 	 * alternative, take the one in the packet.
557*83ee113eSDavid van Moolenbroek 	 */
558*83ee113eSDavid van Moolenbroek 
559*83ee113eSDavid van Moolenbroek 	if (inpacket &&
560*83ee113eSDavid van Moolenbroek 	    (op = lookup_option(&dhcp_universe, inpacket->options,
561*83ee113eSDavid van Moolenbroek 				DHO_DHCP_MAX_MESSAGE_SIZE)) &&
562*83ee113eSDavid van Moolenbroek 	    (evaluate_option_cache(&ds, inpacket, lease,
563*83ee113eSDavid van Moolenbroek 				   client_state, in_options,
564*83ee113eSDavid van Moolenbroek 				   cfg_options, scope, op, MDL) != 0)) {
565*83ee113eSDavid van Moolenbroek 		if (ds.len >= sizeof (u_int16_t)) {
566*83ee113eSDavid van Moolenbroek 			i = getUShort(ds.data);
567*83ee113eSDavid van Moolenbroek 			if(!mms || (i < mms))
568*83ee113eSDavid van Moolenbroek 				mms = i;
569*83ee113eSDavid van Moolenbroek 		}
570*83ee113eSDavid van Moolenbroek 		data_string_forget(&ds, MDL);
571*83ee113eSDavid van Moolenbroek 	}
572*83ee113eSDavid van Moolenbroek 
573*83ee113eSDavid van Moolenbroek 	/*
574*83ee113eSDavid van Moolenbroek 	 * If the client has provided a maximum DHCP message size,
575*83ee113eSDavid van Moolenbroek 	 * use that, up to the MTU limit.  Otherwise, if it's BOOTP,
576*83ee113eSDavid van Moolenbroek 	 * only 64 bytes; otherwise use up to the minimum IP MTU size
577*83ee113eSDavid van Moolenbroek 	 * (576 bytes).
578*83ee113eSDavid van Moolenbroek 	 *
579*83ee113eSDavid van Moolenbroek 	 * XXX if a BOOTP client specifies a max message size, we will
580*83ee113eSDavid van Moolenbroek 	 * honor it.
581*83ee113eSDavid van Moolenbroek 	 */
582*83ee113eSDavid van Moolenbroek 	if (mms) {
583*83ee113eSDavid van Moolenbroek 		if (mms < DHCP_MTU_MIN)
584*83ee113eSDavid van Moolenbroek 		        /* Enforce minimum packet size, per RFC 2132 */
585*83ee113eSDavid van Moolenbroek 			mb_size = DHCP_MIN_OPTION_LEN;
586*83ee113eSDavid van Moolenbroek 		else if (mms > DHCP_MTU_MAX)
587*83ee113eSDavid van Moolenbroek 			/*
588*83ee113eSDavid van Moolenbroek 			 * TODO: Packets longer than 1500 bytes really
589*83ee113eSDavid van Moolenbroek 			 * should be allowed, but it requires upstream
590*83ee113eSDavid van Moolenbroek 			 * changes to the way the packet is allocated.  For
591*83ee113eSDavid van Moolenbroek 			 * now, we forbid them.  They won't be needed very
592*83ee113eSDavid van Moolenbroek 			 * often anyway.
593*83ee113eSDavid van Moolenbroek 			 */
594*83ee113eSDavid van Moolenbroek 			mb_size = DHCP_MAX_OPTION_LEN;
595*83ee113eSDavid van Moolenbroek 		else
596*83ee113eSDavid van Moolenbroek 			mb_size = mms - DHCP_FIXED_LEN;
597*83ee113eSDavid van Moolenbroek 	} else if (bootpp) {
598*83ee113eSDavid van Moolenbroek 		mb_size = 64;
599*83ee113eSDavid van Moolenbroek 		if (inpacket != NULL &&
600*83ee113eSDavid van Moolenbroek 		    (inpacket->packet_length >= 64 + DHCP_FIXED_NON_UDP))
601*83ee113eSDavid van Moolenbroek 			mb_size = inpacket->packet_length - DHCP_FIXED_NON_UDP;
602*83ee113eSDavid van Moolenbroek 	} else
603*83ee113eSDavid van Moolenbroek 		mb_size = DHCP_MIN_OPTION_LEN;
604*83ee113eSDavid van Moolenbroek 
605*83ee113eSDavid van Moolenbroek 	/*
606*83ee113eSDavid van Moolenbroek 	 * If answering a client message, see whether any relay agent
607*83ee113eSDavid van Moolenbroek 	 * options were included with the message.  If so, save them
608*83ee113eSDavid van Moolenbroek 	 * to copy back in later, and make space in the main buffer
609*83ee113eSDavid van Moolenbroek 	 * to accommodate them
610*83ee113eSDavid van Moolenbroek 	 */
611*83ee113eSDavid van Moolenbroek 	if (client_state == NULL) {
612*83ee113eSDavid van Moolenbroek 		priority_list[0] = DHO_DHCP_AGENT_OPTIONS;
613*83ee113eSDavid van Moolenbroek 		priority_len = 1;
614*83ee113eSDavid van Moolenbroek 		agent_size = store_options(NULL, agentopts, 0,
615*83ee113eSDavid van Moolenbroek 					   sizeof(agentopts),
616*83ee113eSDavid van Moolenbroek 					   inpacket, lease, client_state,
617*83ee113eSDavid van Moolenbroek 					   in_options, cfg_options, scope,
618*83ee113eSDavid van Moolenbroek 					   priority_list, priority_len,
619*83ee113eSDavid van Moolenbroek 					   0, 0, 0, NULL);
620*83ee113eSDavid van Moolenbroek 
621*83ee113eSDavid van Moolenbroek 		mb_size += agent_size;
622*83ee113eSDavid van Moolenbroek 		if (mb_size > DHCP_MAX_OPTION_LEN)
623*83ee113eSDavid van Moolenbroek 			mb_size = DHCP_MAX_OPTION_LEN;
624*83ee113eSDavid van Moolenbroek 	}
625*83ee113eSDavid van Moolenbroek 
626*83ee113eSDavid van Moolenbroek 	/*
627*83ee113eSDavid van Moolenbroek 	 * Set offsets for buffer data to be copied into filename
628*83ee113eSDavid van Moolenbroek 	 * and servername fields
629*83ee113eSDavid van Moolenbroek 	 */
630*83ee113eSDavid van Moolenbroek 	mb_max = mb_size;
631*83ee113eSDavid van Moolenbroek 
632*83ee113eSDavid van Moolenbroek 	if (overload_avail & 1) {
633*83ee113eSDavid van Moolenbroek 		of1 = mb_max;
634*83ee113eSDavid van Moolenbroek 		mb_max += DHCP_FILE_LEN;
635*83ee113eSDavid van Moolenbroek 	}
636*83ee113eSDavid van Moolenbroek 
637*83ee113eSDavid van Moolenbroek 	if (overload_avail & 2) {
638*83ee113eSDavid van Moolenbroek 		of2 = mb_max;
639*83ee113eSDavid van Moolenbroek 		mb_max += DHCP_SNAME_LEN;
640*83ee113eSDavid van Moolenbroek 	}
641*83ee113eSDavid van Moolenbroek 
642*83ee113eSDavid van Moolenbroek 	/*
643*83ee113eSDavid van Moolenbroek 	 * Preload the option priority list with protocol-mandatory options.
644*83ee113eSDavid van Moolenbroek 	 * This effectively gives these options the highest priority.
645*83ee113eSDavid van Moolenbroek 	 * This provides the order for any available options, the option
646*83ee113eSDavid van Moolenbroek 	 * must be in the option cache in order to actually be included.
647*83ee113eSDavid van Moolenbroek 	 */
648*83ee113eSDavid van Moolenbroek 	priority_len = 0;
649*83ee113eSDavid van Moolenbroek 	priority_list[priority_len++] = DHO_DHCP_MESSAGE_TYPE;
650*83ee113eSDavid van Moolenbroek 	priority_list[priority_len++] = DHO_DHCP_SERVER_IDENTIFIER;
651*83ee113eSDavid van Moolenbroek 	priority_list[priority_len++] = DHO_DHCP_LEASE_TIME;
652*83ee113eSDavid van Moolenbroek 	priority_list[priority_len++] = DHO_DHCP_RENEWAL_TIME;
653*83ee113eSDavid van Moolenbroek 	priority_list[priority_len++] = DHO_DHCP_REBINDING_TIME;
654*83ee113eSDavid van Moolenbroek 	priority_list[priority_len++] = DHO_DHCP_MESSAGE;
655*83ee113eSDavid van Moolenbroek 	priority_list[priority_len++] = DHO_DHCP_REQUESTED_ADDRESS;
656*83ee113eSDavid van Moolenbroek 	priority_list[priority_len++] = DHO_ASSOCIATED_IP;
657*83ee113eSDavid van Moolenbroek 
658*83ee113eSDavid van Moolenbroek 	if (prl != NULL && prl->len > 0) {
659*83ee113eSDavid van Moolenbroek 		if ((op = lookup_option(&dhcp_universe, cfg_options,
660*83ee113eSDavid van Moolenbroek 					 DHO_SUBNET_SELECTION))) {
661*83ee113eSDavid van Moolenbroek 			if (priority_len < PRIORITY_COUNT)
662*83ee113eSDavid van Moolenbroek 				priority_list[priority_len++] =
663*83ee113eSDavid van Moolenbroek 					DHO_SUBNET_SELECTION;
664*83ee113eSDavid van Moolenbroek 		}
665*83ee113eSDavid van Moolenbroek 
666*83ee113eSDavid van Moolenbroek 		data_string_truncate(prl, (PRIORITY_COUNT - priority_len));
667*83ee113eSDavid van Moolenbroek 
668*83ee113eSDavid van Moolenbroek 		/*
669*83ee113eSDavid van Moolenbroek 		 * Copy the client's PRL onto the priority_list after our high
670*83ee113eSDavid van Moolenbroek 		 * priority header.
671*83ee113eSDavid van Moolenbroek 		 */
672*83ee113eSDavid van Moolenbroek 		for (i = 0; i < prl->len; i++) {
673*83ee113eSDavid van Moolenbroek 			/*
674*83ee113eSDavid van Moolenbroek 			 * Prevent client from changing order of delivery
675*83ee113eSDavid van Moolenbroek 			 * of relay agent information option.
676*83ee113eSDavid van Moolenbroek 			 */
677*83ee113eSDavid van Moolenbroek 			if (prl->data[i] != DHO_DHCP_AGENT_OPTIONS)
678*83ee113eSDavid van Moolenbroek 				priority_list[priority_len++] = prl->data[i];
679*83ee113eSDavid van Moolenbroek 		}
680*83ee113eSDavid van Moolenbroek 
681*83ee113eSDavid van Moolenbroek 		/*
682*83ee113eSDavid van Moolenbroek 		 * If the client doesn't request the FQDN option explicitly,
683*83ee113eSDavid van Moolenbroek 		 * to indicate priority, consider it lowest priority.  Fit
684*83ee113eSDavid van Moolenbroek 		 * in the packet if there is space.  Note that the option
685*83ee113eSDavid van Moolenbroek 		 * may only be included if the client supplied one.
686*83ee113eSDavid van Moolenbroek 		 */
687*83ee113eSDavid van Moolenbroek 		if ((inpacket != NULL) && (priority_len < PRIORITY_COUNT) &&
688*83ee113eSDavid van Moolenbroek 		    (lookup_option(&fqdn_universe, inpacket->options,
689*83ee113eSDavid van Moolenbroek 				   FQDN_ENCODED) != NULL))
690*83ee113eSDavid van Moolenbroek 			priority_list[priority_len++] = DHO_FQDN;
691*83ee113eSDavid van Moolenbroek 
692*83ee113eSDavid van Moolenbroek 		/*
693*83ee113eSDavid van Moolenbroek 		 * Some DHCP Servers will give the subnet-mask option if
694*83ee113eSDavid van Moolenbroek 		 * it is not on the parameter request list - so some client
695*83ee113eSDavid van Moolenbroek 		 * implementations have come to rely on this - so we will
696*83ee113eSDavid van Moolenbroek 		 * also make sure we supply this, at lowest priority.
697*83ee113eSDavid van Moolenbroek 		 *
698*83ee113eSDavid van Moolenbroek 		 * This is only done in response to DHCPDISCOVER or
699*83ee113eSDavid van Moolenbroek 		 * DHCPREQUEST messages, to avoid providing the option on
700*83ee113eSDavid van Moolenbroek 		 * DHCPINFORM or DHCPLEASEQUERY responses (if the client
701*83ee113eSDavid van Moolenbroek 		 * didn't request it).
702*83ee113eSDavid van Moolenbroek 		 */
703*83ee113eSDavid van Moolenbroek 		if ((inpacket != NULL) && (priority_len < PRIORITY_COUNT) &&
704*83ee113eSDavid van Moolenbroek 		    ((inpacket->packet_type == DHCPDISCOVER) ||
705*83ee113eSDavid van Moolenbroek 		     (inpacket->packet_type == DHCPREQUEST)))
706*83ee113eSDavid van Moolenbroek 			priority_list[priority_len++] = DHO_SUBNET_MASK;
707*83ee113eSDavid van Moolenbroek 	} else {
708*83ee113eSDavid van Moolenbroek 		/*
709*83ee113eSDavid van Moolenbroek 		 * First, hardcode some more options that ought to be
710*83ee113eSDavid van Moolenbroek 		 * sent first...these are high priority to have in the
711*83ee113eSDavid van Moolenbroek 		 * packet.
712*83ee113eSDavid van Moolenbroek 		 */
713*83ee113eSDavid van Moolenbroek 		priority_list[priority_len++] = DHO_SUBNET_MASK;
714*83ee113eSDavid van Moolenbroek 		priority_list[priority_len++] = DHO_ROUTERS;
715*83ee113eSDavid van Moolenbroek 		priority_list[priority_len++] = DHO_DOMAIN_NAME_SERVERS;
716*83ee113eSDavid van Moolenbroek 		priority_list[priority_len++] = DHO_HOST_NAME;
717*83ee113eSDavid van Moolenbroek 		priority_list[priority_len++] = DHO_FQDN;
718*83ee113eSDavid van Moolenbroek 
719*83ee113eSDavid van Moolenbroek 		/*
720*83ee113eSDavid van Moolenbroek 		 * Append a list of the standard DHCP options from the
721*83ee113eSDavid van Moolenbroek 		 * standard DHCP option space.  Actually, if a site
722*83ee113eSDavid van Moolenbroek 		 * option space hasn't been specified, we wind up
723*83ee113eSDavid van Moolenbroek 		 * treating the dhcp option space as the site option
724*83ee113eSDavid van Moolenbroek 		 * space, and the first for loop is skipped, because
725*83ee113eSDavid van Moolenbroek 		 * it's slightly more general to do it this way,
726*83ee113eSDavid van Moolenbroek 		 * taking the 1Q99 DHCP futures work into account.
727*83ee113eSDavid van Moolenbroek 		 */
728*83ee113eSDavid van Moolenbroek 		if (cfg_options->site_code_min) {
729*83ee113eSDavid van Moolenbroek 		    for (i = 0; i < OPTION_HASH_SIZE; i++) {
730*83ee113eSDavid van Moolenbroek 			hash = cfg_options->universes[dhcp_universe.index];
731*83ee113eSDavid van Moolenbroek 			if (hash) {
732*83ee113eSDavid van Moolenbroek 			    for (pp = hash[i]; pp; pp = pp->cdr) {
733*83ee113eSDavid van Moolenbroek 				op = (struct option_cache *)(pp->car);
734*83ee113eSDavid van Moolenbroek 				if (op->option->code <
735*83ee113eSDavid van Moolenbroek 				     cfg_options->site_code_min &&
736*83ee113eSDavid van Moolenbroek 				    priority_len < PRIORITY_COUNT &&
737*83ee113eSDavid van Moolenbroek 				    op->option->code != DHO_DHCP_AGENT_OPTIONS)
738*83ee113eSDavid van Moolenbroek 					priority_list[priority_len++] =
739*83ee113eSDavid van Moolenbroek 						op->option->code;
740*83ee113eSDavid van Moolenbroek 			    }
741*83ee113eSDavid van Moolenbroek 			}
742*83ee113eSDavid van Moolenbroek 		    }
743*83ee113eSDavid van Moolenbroek 		}
744*83ee113eSDavid van Moolenbroek 
745*83ee113eSDavid van Moolenbroek 		/*
746*83ee113eSDavid van Moolenbroek 		 * Now cycle through the site option space, or if there
747*83ee113eSDavid van Moolenbroek 		 * is no site option space, we'll be cycling through the
748*83ee113eSDavid van Moolenbroek 		 * dhcp option space.
749*83ee113eSDavid van Moolenbroek 		 */
750*83ee113eSDavid van Moolenbroek 		for (i = 0; i < OPTION_HASH_SIZE; i++) {
751*83ee113eSDavid van Moolenbroek 		    hash = cfg_options->universes[cfg_options->site_universe];
752*83ee113eSDavid van Moolenbroek 		    if (hash != NULL)
753*83ee113eSDavid van Moolenbroek 			for (pp = hash[i]; pp; pp = pp->cdr) {
754*83ee113eSDavid van Moolenbroek 				op = (struct option_cache *)(pp->car);
755*83ee113eSDavid van Moolenbroek 				if (op->option->code >=
756*83ee113eSDavid van Moolenbroek 				     cfg_options->site_code_min &&
757*83ee113eSDavid van Moolenbroek 				    priority_len < PRIORITY_COUNT &&
758*83ee113eSDavid van Moolenbroek 				    op->option->code != DHO_DHCP_AGENT_OPTIONS)
759*83ee113eSDavid van Moolenbroek 					priority_list[priority_len++] =
760*83ee113eSDavid van Moolenbroek 						op->option->code;
761*83ee113eSDavid van Moolenbroek 			}
762*83ee113eSDavid van Moolenbroek 		}
763*83ee113eSDavid van Moolenbroek 
764*83ee113eSDavid van Moolenbroek 		/*
765*83ee113eSDavid van Moolenbroek 		 * Put any spaces that are encapsulated on the list,
766*83ee113eSDavid van Moolenbroek 		 * sort out whether they contain values later.
767*83ee113eSDavid van Moolenbroek 		 */
768*83ee113eSDavid van Moolenbroek 		for (i = 0; i < cfg_options->universe_count; i++) {
769*83ee113eSDavid van Moolenbroek 		    if (universes[i]->enc_opt &&
770*83ee113eSDavid van Moolenbroek 			priority_len < PRIORITY_COUNT &&
771*83ee113eSDavid van Moolenbroek 			universes[i]->enc_opt->universe == &dhcp_universe) {
772*83ee113eSDavid van Moolenbroek 			    if (universes[i]->enc_opt->code !=
773*83ee113eSDavid van Moolenbroek 				DHO_DHCP_AGENT_OPTIONS)
774*83ee113eSDavid van Moolenbroek 				    priority_list[priority_len++] =
775*83ee113eSDavid van Moolenbroek 					    universes[i]->enc_opt->code;
776*83ee113eSDavid van Moolenbroek 		    }
777*83ee113eSDavid van Moolenbroek 		}
778*83ee113eSDavid van Moolenbroek 
779*83ee113eSDavid van Moolenbroek 		/*
780*83ee113eSDavid van Moolenbroek 		 * The vendor option space can't stand on its own, so always
781*83ee113eSDavid van Moolenbroek 		 * add it to the list.
782*83ee113eSDavid van Moolenbroek 		 */
783*83ee113eSDavid van Moolenbroek 		if (priority_len < PRIORITY_COUNT)
784*83ee113eSDavid van Moolenbroek 			priority_list[priority_len++] =
785*83ee113eSDavid van Moolenbroek 				DHO_VENDOR_ENCAPSULATED_OPTIONS;
786*83ee113eSDavid van Moolenbroek 	}
787*83ee113eSDavid van Moolenbroek 
788*83ee113eSDavid van Moolenbroek 	/* Put the cookie up front... */
789*83ee113eSDavid van Moolenbroek 	memcpy(buffer, DHCP_OPTIONS_COOKIE, 4);
790*83ee113eSDavid van Moolenbroek 	index += 4;
791*83ee113eSDavid van Moolenbroek 
792*83ee113eSDavid van Moolenbroek 	/* Copy the options into the big buffer... */
793*83ee113eSDavid van Moolenbroek 	option_size = store_options(&overload_used, buffer, index, mb_max,
794*83ee113eSDavid van Moolenbroek 				    inpacket, lease, client_state,
795*83ee113eSDavid van Moolenbroek 				    in_options, cfg_options, scope,
796*83ee113eSDavid van Moolenbroek 				    priority_list, priority_len,
797*83ee113eSDavid van Moolenbroek 				    of1, of2, terminate, vuname);
798*83ee113eSDavid van Moolenbroek 
799*83ee113eSDavid van Moolenbroek 	/* If store_options() failed */
800*83ee113eSDavid van Moolenbroek 	if (option_size == 0)
801*83ee113eSDavid van Moolenbroek 		return 0;
802*83ee113eSDavid van Moolenbroek 
803*83ee113eSDavid van Moolenbroek 	/* How much was stored in the main buffer? */
804*83ee113eSDavid van Moolenbroek 	index += option_size;
805*83ee113eSDavid van Moolenbroek 
806*83ee113eSDavid van Moolenbroek 	/*
807*83ee113eSDavid van Moolenbroek 	 * If we're going to have to overload, store the overload
808*83ee113eSDavid van Moolenbroek 	 * option first.
809*83ee113eSDavid van Moolenbroek 	 */
810*83ee113eSDavid van Moolenbroek 	if (overload_used) {
811*83ee113eSDavid van Moolenbroek 		if (mb_size - agent_size - index < 3)
812*83ee113eSDavid van Moolenbroek 			return 0;
813*83ee113eSDavid van Moolenbroek 
814*83ee113eSDavid van Moolenbroek 		buffer[index++] = DHO_DHCP_OPTION_OVERLOAD;
815*83ee113eSDavid van Moolenbroek 		buffer[index++] = 1;
816*83ee113eSDavid van Moolenbroek 		buffer[index++] = overload_used;
817*83ee113eSDavid van Moolenbroek 
818*83ee113eSDavid van Moolenbroek 		if (overload_used & 1)
819*83ee113eSDavid van Moolenbroek 			memcpy(outpacket->file, &buffer[of1], DHCP_FILE_LEN);
820*83ee113eSDavid van Moolenbroek 
821*83ee113eSDavid van Moolenbroek 		if (overload_used & 2)
822*83ee113eSDavid van Moolenbroek 			memcpy(outpacket->sname, &buffer[of2], DHCP_SNAME_LEN);
823*83ee113eSDavid van Moolenbroek 	}
824*83ee113eSDavid van Moolenbroek 
825*83ee113eSDavid van Moolenbroek 	/* Now copy in preserved agent options, if any */
826*83ee113eSDavid van Moolenbroek 	if (agent_size) {
827*83ee113eSDavid van Moolenbroek 		if (mb_size - index >= agent_size) {
828*83ee113eSDavid van Moolenbroek 			memcpy(&buffer[index], agentopts, agent_size);
829*83ee113eSDavid van Moolenbroek 			index += agent_size;
830*83ee113eSDavid van Moolenbroek 		} else
831*83ee113eSDavid van Moolenbroek 			log_error("Unable to store relay agent information "
832*83ee113eSDavid van Moolenbroek 				  "in reply packet.");
833*83ee113eSDavid van Moolenbroek 	}
834*83ee113eSDavid van Moolenbroek 
835*83ee113eSDavid van Moolenbroek 	/* Tack a DHO_END option onto the packet if we need to. */
836*83ee113eSDavid van Moolenbroek 	if (index < mb_size)
837*83ee113eSDavid van Moolenbroek 		buffer[index++] = DHO_END;
838*83ee113eSDavid van Moolenbroek 
839*83ee113eSDavid van Moolenbroek 	/* Copy main buffer into the options buffer of the packet */
840*83ee113eSDavid van Moolenbroek 	memcpy(outpacket->options, buffer, index);
841*83ee113eSDavid van Moolenbroek 
842*83ee113eSDavid van Moolenbroek 	/* Figure out the length. */
843*83ee113eSDavid van Moolenbroek 	length = DHCP_FIXED_NON_UDP + index;
844*83ee113eSDavid van Moolenbroek 	return length;
845*83ee113eSDavid van Moolenbroek }
846*83ee113eSDavid van Moolenbroek 
847*83ee113eSDavid van Moolenbroek /*
848*83ee113eSDavid van Moolenbroek  * XXX: We currently special case collecting VSIO options.
849*83ee113eSDavid van Moolenbroek  *      We should be able to handle this in a more generic fashion, by
850*83ee113eSDavid van Moolenbroek  *      including any encapsulated options that are present and desired.
851*83ee113eSDavid van Moolenbroek  *      This will look something like the VSIO handling VSIO code.
852*83ee113eSDavid van Moolenbroek  *      We may also consider handling the ORO-like options within
853*83ee113eSDavid van Moolenbroek  *      encapsulated spaces.
854*83ee113eSDavid van Moolenbroek  */
855*83ee113eSDavid van Moolenbroek 
856*83ee113eSDavid van Moolenbroek struct vsio_state {
857*83ee113eSDavid van Moolenbroek 	char *buf;
858*83ee113eSDavid van Moolenbroek 	int buflen;
859*83ee113eSDavid van Moolenbroek 	int bufpos;
860*83ee113eSDavid van Moolenbroek };
861*83ee113eSDavid van Moolenbroek 
862*83ee113eSDavid van Moolenbroek static void
vsio_options(struct option_cache * oc,struct packet * packet,struct lease * dummy_lease,struct client_state * dummy_client_state,struct option_state * dummy_opt_state,struct option_state * opt_state,struct binding_scope ** dummy_binding_scope,struct universe * universe,void * void_vsio_state)863*83ee113eSDavid van Moolenbroek vsio_options(struct option_cache *oc,
864*83ee113eSDavid van Moolenbroek 	     struct packet *packet,
865*83ee113eSDavid van Moolenbroek 	     struct lease *dummy_lease,
866*83ee113eSDavid van Moolenbroek 	     struct client_state *dummy_client_state,
867*83ee113eSDavid van Moolenbroek 	     struct option_state *dummy_opt_state,
868*83ee113eSDavid van Moolenbroek 	     struct option_state *opt_state,
869*83ee113eSDavid van Moolenbroek 	     struct binding_scope **dummy_binding_scope,
870*83ee113eSDavid van Moolenbroek 	     struct universe *universe,
871*83ee113eSDavid van Moolenbroek 	     void *void_vsio_state) {
872*83ee113eSDavid van Moolenbroek 	struct vsio_state *vs = (struct vsio_state *)void_vsio_state;
873*83ee113eSDavid van Moolenbroek 	struct data_string ds;
874*83ee113eSDavid van Moolenbroek 	int total_len;
875*83ee113eSDavid van Moolenbroek 
876*83ee113eSDavid van Moolenbroek 	memset(&ds, 0, sizeof(ds));
877*83ee113eSDavid van Moolenbroek 	if (evaluate_option_cache(&ds, packet, NULL,
878*83ee113eSDavid van Moolenbroek 				  NULL, opt_state, NULL,
879*83ee113eSDavid van Moolenbroek 				  &global_scope, oc, MDL)) {
880*83ee113eSDavid van Moolenbroek 		total_len = ds.len + universe->tag_size + universe->length_size;
881*83ee113eSDavid van Moolenbroek 		if (total_len <= (vs->buflen - vs->bufpos)) {
882*83ee113eSDavid van Moolenbroek 			if (universe->tag_size == 1) {
883*83ee113eSDavid van Moolenbroek 				vs->buf[vs->bufpos++] = oc->option->code;
884*83ee113eSDavid van Moolenbroek 			} else if (universe->tag_size == 2) {
885*83ee113eSDavid van Moolenbroek 				putUShort((unsigned char *)vs->buf+vs->bufpos,
886*83ee113eSDavid van Moolenbroek 					  oc->option->code);
887*83ee113eSDavid van Moolenbroek 				vs->bufpos += 2;
888*83ee113eSDavid van Moolenbroek 			} else if (universe->tag_size == 4) {
889*83ee113eSDavid van Moolenbroek 				putULong((unsigned char *)vs->buf+vs->bufpos,
890*83ee113eSDavid van Moolenbroek 					 oc->option->code);
891*83ee113eSDavid van Moolenbroek 				vs->bufpos += 4;
892*83ee113eSDavid van Moolenbroek 			}
893*83ee113eSDavid van Moolenbroek 			if (universe->length_size == 1) {
894*83ee113eSDavid van Moolenbroek 				vs->buf[vs->bufpos++] = ds.len;
895*83ee113eSDavid van Moolenbroek 			} else if (universe->length_size == 2) {
896*83ee113eSDavid van Moolenbroek 				putUShort((unsigned char *)vs->buf+vs->bufpos,
897*83ee113eSDavid van Moolenbroek 					  ds.len);
898*83ee113eSDavid van Moolenbroek 				vs->bufpos += 2;
899*83ee113eSDavid van Moolenbroek 			} else if (universe->length_size == 4) {
900*83ee113eSDavid van Moolenbroek 				putULong((unsigned char *)vs->buf+vs->bufpos,
901*83ee113eSDavid van Moolenbroek 					 ds.len);
902*83ee113eSDavid van Moolenbroek 				vs->bufpos += 4;
903*83ee113eSDavid van Moolenbroek 			}
904*83ee113eSDavid van Moolenbroek 			memcpy(vs->buf + vs->bufpos, ds.data, ds.len);
905*83ee113eSDavid van Moolenbroek 			vs->bufpos += ds.len;
906*83ee113eSDavid van Moolenbroek 		} else {
907*83ee113eSDavid van Moolenbroek 			log_debug("No space for option %d in VSIO space %s.",
908*83ee113eSDavid van Moolenbroek 		  		oc->option->code, universe->name);
909*83ee113eSDavid van Moolenbroek 		}
910*83ee113eSDavid van Moolenbroek 		data_string_forget(&ds, MDL);
911*83ee113eSDavid van Moolenbroek 	} else {
912*83ee113eSDavid van Moolenbroek 		log_error("Error evaluating option %d in VSIO space %s.",
913*83ee113eSDavid van Moolenbroek 		  	oc->option->code, universe->name);
914*83ee113eSDavid van Moolenbroek 	}
915*83ee113eSDavid van Moolenbroek }
916*83ee113eSDavid van Moolenbroek 
917*83ee113eSDavid van Moolenbroek /*
918*83ee113eSDavid van Moolenbroek  * Stores the options from the DHCPv6 universe into the buffer given.
919*83ee113eSDavid van Moolenbroek  *
920*83ee113eSDavid van Moolenbroek  * Required options are given as a 0-terminated list of option codes.
921*83ee113eSDavid van Moolenbroek  * Once those are added, the ORO is consulted.
922*83ee113eSDavid van Moolenbroek  */
923*83ee113eSDavid van Moolenbroek 
924*83ee113eSDavid van Moolenbroek int
store_options6(char * buf,int buflen,struct option_state * opt_state,struct packet * packet,const int * required_opts,struct data_string * oro)925*83ee113eSDavid van Moolenbroek store_options6(char *buf, int buflen,
926*83ee113eSDavid van Moolenbroek 	       struct option_state *opt_state,
927*83ee113eSDavid van Moolenbroek 	       struct packet *packet,
928*83ee113eSDavid van Moolenbroek 	       const int *required_opts,
929*83ee113eSDavid van Moolenbroek 	       struct data_string *oro) {
930*83ee113eSDavid van Moolenbroek 	int i, j;
931*83ee113eSDavid van Moolenbroek 	struct option_cache *oc;
932*83ee113eSDavid van Moolenbroek 	struct option *o;
933*83ee113eSDavid van Moolenbroek 	struct data_string ds;
934*83ee113eSDavid van Moolenbroek 	int bufpos;
935*83ee113eSDavid van Moolenbroek 	int oro_size;
936*83ee113eSDavid van Moolenbroek 	u_int16_t code;
937*83ee113eSDavid van Moolenbroek 	int in_required_opts;
938*83ee113eSDavid van Moolenbroek 	int vsio_option_code;
939*83ee113eSDavid van Moolenbroek 	int vsio_wanted;
940*83ee113eSDavid van Moolenbroek 	struct vsio_state vs;
941*83ee113eSDavid van Moolenbroek 	unsigned char *tmp;
942*83ee113eSDavid van Moolenbroek 
943*83ee113eSDavid van Moolenbroek 	bufpos = 0;
944*83ee113eSDavid van Moolenbroek 	vsio_wanted = 0;
945*83ee113eSDavid van Moolenbroek 
946*83ee113eSDavid van Moolenbroek 	/*
947*83ee113eSDavid van Moolenbroek 	 * Find the option code for the VSIO universe.
948*83ee113eSDavid van Moolenbroek 	 */
949*83ee113eSDavid van Moolenbroek 	vsio_option_code = 0;
950*83ee113eSDavid van Moolenbroek 	o = vsio_universe.enc_opt;
951*83ee113eSDavid van Moolenbroek 	while (o != NULL) {
952*83ee113eSDavid van Moolenbroek 		if (o->universe == &dhcpv6_universe) {
953*83ee113eSDavid van Moolenbroek 			vsio_option_code = o->code;
954*83ee113eSDavid van Moolenbroek 			break;
955*83ee113eSDavid van Moolenbroek 		}
956*83ee113eSDavid van Moolenbroek 		o = o->universe->enc_opt;
957*83ee113eSDavid van Moolenbroek 	}
958*83ee113eSDavid van Moolenbroek 	if (vsio_option_code == 0) {
959*83ee113eSDavid van Moolenbroek 		log_fatal("No VSIO option code found.");
960*83ee113eSDavid van Moolenbroek 	}
961*83ee113eSDavid van Moolenbroek 
962*83ee113eSDavid van Moolenbroek 	if (required_opts != NULL) {
963*83ee113eSDavid van Moolenbroek 		for (i=0; required_opts[i] != 0; i++) {
964*83ee113eSDavid van Moolenbroek 			if (required_opts[i] == vsio_option_code) {
965*83ee113eSDavid van Moolenbroek 				vsio_wanted = 1;
966*83ee113eSDavid van Moolenbroek 			}
967*83ee113eSDavid van Moolenbroek 
968*83ee113eSDavid van Moolenbroek 			oc = lookup_option(&dhcpv6_universe,
969*83ee113eSDavid van Moolenbroek 					   opt_state, required_opts[i]);
970*83ee113eSDavid van Moolenbroek 			if (oc == NULL) {
971*83ee113eSDavid van Moolenbroek 				continue;
972*83ee113eSDavid van Moolenbroek 			}
973*83ee113eSDavid van Moolenbroek 			memset(&ds, 0, sizeof(ds));
974*83ee113eSDavid van Moolenbroek 			for (; oc != NULL ; oc = oc->next) {
975*83ee113eSDavid van Moolenbroek 				if (evaluate_option_cache(&ds, packet, NULL,
976*83ee113eSDavid van Moolenbroek 							  NULL, opt_state,
977*83ee113eSDavid van Moolenbroek 							  NULL, &global_scope,
978*83ee113eSDavid van Moolenbroek 							  oc, MDL)) {
979*83ee113eSDavid van Moolenbroek 					if ((ds.len + 4) <=
980*83ee113eSDavid van Moolenbroek 					    (buflen - bufpos)) {
981*83ee113eSDavid van Moolenbroek 						tmp = (unsigned char *)buf;
982*83ee113eSDavid van Moolenbroek 						tmp += bufpos;
983*83ee113eSDavid van Moolenbroek 						/* option tag */
984*83ee113eSDavid van Moolenbroek 						putUShort(tmp,
985*83ee113eSDavid van Moolenbroek 							  required_opts[i]);
986*83ee113eSDavid van Moolenbroek 						/* option length */
987*83ee113eSDavid van Moolenbroek 						putUShort(tmp+2, ds.len);
988*83ee113eSDavid van Moolenbroek 						/* option data */
989*83ee113eSDavid van Moolenbroek 						memcpy(tmp+4, ds.data, ds.len);
990*83ee113eSDavid van Moolenbroek 						/* update position */
991*83ee113eSDavid van Moolenbroek 						bufpos += (4 + ds.len);
992*83ee113eSDavid van Moolenbroek 					} else {
993*83ee113eSDavid van Moolenbroek 						log_debug("No space for "
994*83ee113eSDavid van Moolenbroek 							  "option %d",
995*83ee113eSDavid van Moolenbroek 							  required_opts[i]);
996*83ee113eSDavid van Moolenbroek 					}
997*83ee113eSDavid van Moolenbroek 					data_string_forget(&ds, MDL);
998*83ee113eSDavid van Moolenbroek 				} else {
999*83ee113eSDavid van Moolenbroek 					log_error("Error evaluating option %d",
1000*83ee113eSDavid van Moolenbroek 					  	required_opts[i]);
1001*83ee113eSDavid van Moolenbroek 				}
1002*83ee113eSDavid van Moolenbroek 			}
1003*83ee113eSDavid van Moolenbroek 		}
1004*83ee113eSDavid van Moolenbroek 	}
1005*83ee113eSDavid van Moolenbroek 
1006*83ee113eSDavid van Moolenbroek 	if (oro == NULL) {
1007*83ee113eSDavid van Moolenbroek 		oro_size = 0;
1008*83ee113eSDavid van Moolenbroek 	} else {
1009*83ee113eSDavid van Moolenbroek 		oro_size = oro->len / 2;
1010*83ee113eSDavid van Moolenbroek 	}
1011*83ee113eSDavid van Moolenbroek 	for (i=0; i<oro_size; i++) {
1012*83ee113eSDavid van Moolenbroek 		memcpy(&code, oro->data+(i*2), 2);
1013*83ee113eSDavid van Moolenbroek 		code = ntohs(code);
1014*83ee113eSDavid van Moolenbroek 
1015*83ee113eSDavid van Moolenbroek 		/*
1016*83ee113eSDavid van Moolenbroek 		 * See if we've already included this option because
1017*83ee113eSDavid van Moolenbroek 		 * it is required.
1018*83ee113eSDavid van Moolenbroek 		 */
1019*83ee113eSDavid van Moolenbroek 		in_required_opts = 0;
1020*83ee113eSDavid van Moolenbroek 		if (required_opts != NULL) {
1021*83ee113eSDavid van Moolenbroek 			for (j=0; required_opts[j] != 0; j++) {
1022*83ee113eSDavid van Moolenbroek 				if (required_opts[j] == code) {
1023*83ee113eSDavid van Moolenbroek 					in_required_opts = 1;
1024*83ee113eSDavid van Moolenbroek 					break;
1025*83ee113eSDavid van Moolenbroek 				}
1026*83ee113eSDavid van Moolenbroek 			}
1027*83ee113eSDavid van Moolenbroek 		}
1028*83ee113eSDavid van Moolenbroek 		if (in_required_opts) {
1029*83ee113eSDavid van Moolenbroek 			continue;
1030*83ee113eSDavid van Moolenbroek 		}
1031*83ee113eSDavid van Moolenbroek 
1032*83ee113eSDavid van Moolenbroek 		/*
1033*83ee113eSDavid van Moolenbroek 		 * See if this is the VSIO option.
1034*83ee113eSDavid van Moolenbroek 		 */
1035*83ee113eSDavid van Moolenbroek 		if (code == vsio_option_code) {
1036*83ee113eSDavid van Moolenbroek 			vsio_wanted = 1;
1037*83ee113eSDavid van Moolenbroek 		}
1038*83ee113eSDavid van Moolenbroek 
1039*83ee113eSDavid van Moolenbroek 		/*
1040*83ee113eSDavid van Moolenbroek 		 * Not already added, find this option.
1041*83ee113eSDavid van Moolenbroek 		 */
1042*83ee113eSDavid van Moolenbroek 		oc = lookup_option(&dhcpv6_universe, opt_state, code);
1043*83ee113eSDavid van Moolenbroek 		memset(&ds, 0, sizeof(ds));
1044*83ee113eSDavid van Moolenbroek 		for (; oc != NULL ; oc = oc->next) {
1045*83ee113eSDavid van Moolenbroek 			if (evaluate_option_cache(&ds, packet, NULL, NULL,
1046*83ee113eSDavid van Moolenbroek 						  opt_state, NULL,
1047*83ee113eSDavid van Moolenbroek 						  &global_scope, oc, MDL)) {
1048*83ee113eSDavid van Moolenbroek 				if ((ds.len + 4) <= (buflen - bufpos)) {
1049*83ee113eSDavid van Moolenbroek 					tmp = (unsigned char *)buf + bufpos;
1050*83ee113eSDavid van Moolenbroek 					/* option tag */
1051*83ee113eSDavid van Moolenbroek 					putUShort(tmp, code);
1052*83ee113eSDavid van Moolenbroek 					/* option length */
1053*83ee113eSDavid van Moolenbroek 					putUShort(tmp+2, ds.len);
1054*83ee113eSDavid van Moolenbroek 					/* option data */
1055*83ee113eSDavid van Moolenbroek 					memcpy(tmp+4, ds.data, ds.len);
1056*83ee113eSDavid van Moolenbroek 					/* update position */
1057*83ee113eSDavid van Moolenbroek 					bufpos += (4 + ds.len);
1058*83ee113eSDavid van Moolenbroek 				} else {
1059*83ee113eSDavid van Moolenbroek 					log_debug("No space for option %d",
1060*83ee113eSDavid van Moolenbroek 						  code);
1061*83ee113eSDavid van Moolenbroek 				}
1062*83ee113eSDavid van Moolenbroek 				data_string_forget(&ds, MDL);
1063*83ee113eSDavid van Moolenbroek 			} else {
1064*83ee113eSDavid van Moolenbroek 				log_error("Error evaluating option %d", code);
1065*83ee113eSDavid van Moolenbroek 			}
1066*83ee113eSDavid van Moolenbroek 		}
1067*83ee113eSDavid van Moolenbroek 	}
1068*83ee113eSDavid van Moolenbroek 
1069*83ee113eSDavid van Moolenbroek 	if (vsio_wanted) {
1070*83ee113eSDavid van Moolenbroek 		for (i=0; i < opt_state->universe_count; i++) {
1071*83ee113eSDavid van Moolenbroek 			if (opt_state->universes[i] != NULL) {
1072*83ee113eSDavid van Moolenbroek 		    		o = universes[i]->enc_opt;
1073*83ee113eSDavid van Moolenbroek 				if ((o != NULL) &&
1074*83ee113eSDavid van Moolenbroek 				    (o->universe == &vsio_universe)) {
1075*83ee113eSDavid van Moolenbroek 					/*
1076*83ee113eSDavid van Moolenbroek 					 * Add the data from this VSIO option.
1077*83ee113eSDavid van Moolenbroek 					 */
1078*83ee113eSDavid van Moolenbroek 					vs.buf = buf;
1079*83ee113eSDavid van Moolenbroek 					vs.buflen = buflen;
1080*83ee113eSDavid van Moolenbroek 					vs.bufpos = bufpos+8;
1081*83ee113eSDavid van Moolenbroek 					option_space_foreach(packet, NULL,
1082*83ee113eSDavid van Moolenbroek 							     NULL,
1083*83ee113eSDavid van Moolenbroek 							     NULL, opt_state,
1084*83ee113eSDavid van Moolenbroek 			     				     NULL,
1085*83ee113eSDavid van Moolenbroek 							     universes[i],
1086*83ee113eSDavid van Moolenbroek 							     (void *)&vs,
1087*83ee113eSDavid van Moolenbroek 			     				     vsio_options);
1088*83ee113eSDavid van Moolenbroek 
1089*83ee113eSDavid van Moolenbroek 					/*
1090*83ee113eSDavid van Moolenbroek 					 * If there was actually data here,
1091*83ee113eSDavid van Moolenbroek 					 * add the "header".
1092*83ee113eSDavid van Moolenbroek 					 */
1093*83ee113eSDavid van Moolenbroek 					if (vs.bufpos > bufpos+8) {
1094*83ee113eSDavid van Moolenbroek 						tmp = (unsigned char *)buf +
1095*83ee113eSDavid van Moolenbroek 						      bufpos;
1096*83ee113eSDavid van Moolenbroek 						putUShort(tmp,
1097*83ee113eSDavid van Moolenbroek 							  vsio_option_code);
1098*83ee113eSDavid van Moolenbroek 						putUShort(tmp+2,
1099*83ee113eSDavid van Moolenbroek 							  vs.bufpos-bufpos-4);
1100*83ee113eSDavid van Moolenbroek 						putULong(tmp+4, o->code);
1101*83ee113eSDavid van Moolenbroek 
1102*83ee113eSDavid van Moolenbroek 						bufpos = vs.bufpos;
1103*83ee113eSDavid van Moolenbroek 					}
1104*83ee113eSDavid van Moolenbroek 				}
1105*83ee113eSDavid van Moolenbroek 			}
1106*83ee113eSDavid van Moolenbroek 		}
1107*83ee113eSDavid van Moolenbroek 	}
1108*83ee113eSDavid van Moolenbroek 
1109*83ee113eSDavid van Moolenbroek 	return bufpos;
1110*83ee113eSDavid van Moolenbroek }
1111*83ee113eSDavid van Moolenbroek 
1112*83ee113eSDavid van Moolenbroek /*
1113*83ee113eSDavid van Moolenbroek  * Store all the requested options into the requested buffer.
1114*83ee113eSDavid van Moolenbroek  * XXX: ought to be static
1115*83ee113eSDavid van Moolenbroek  */
1116*83ee113eSDavid van Moolenbroek int
store_options(int * ocount,unsigned char * buffer,unsigned index,unsigned buflen,struct packet * packet,struct lease * lease,struct client_state * client_state,struct option_state * in_options,struct option_state * cfg_options,struct binding_scope ** scope,unsigned * priority_list,int priority_len,unsigned first_cutoff,int second_cutoff,int terminate,const char * vuname)1117*83ee113eSDavid van Moolenbroek store_options(int *ocount,
1118*83ee113eSDavid van Moolenbroek 	      unsigned char *buffer, unsigned index, unsigned buflen,
1119*83ee113eSDavid van Moolenbroek 	      struct packet *packet, struct lease *lease,
1120*83ee113eSDavid van Moolenbroek 	      struct client_state *client_state,
1121*83ee113eSDavid van Moolenbroek 	      struct option_state *in_options,
1122*83ee113eSDavid van Moolenbroek 	      struct option_state *cfg_options,
1123*83ee113eSDavid van Moolenbroek 	      struct binding_scope **scope,
1124*83ee113eSDavid van Moolenbroek 	      unsigned *priority_list, int priority_len,
1125*83ee113eSDavid van Moolenbroek 	      unsigned first_cutoff, int second_cutoff, int terminate,
1126*83ee113eSDavid van Moolenbroek 	      const char *vuname)
1127*83ee113eSDavid van Moolenbroek {
1128*83ee113eSDavid van Moolenbroek 	int bufix = 0, six = 0, tix = 0;
1129*83ee113eSDavid van Moolenbroek 	int i;
1130*83ee113eSDavid van Moolenbroek 	int ix;
1131*83ee113eSDavid van Moolenbroek 	int tto;
1132*83ee113eSDavid van Moolenbroek 	int bufend, sbufend;
1133*83ee113eSDavid van Moolenbroek 	struct data_string od;
1134*83ee113eSDavid van Moolenbroek 	struct option_cache *oc;
1135*83ee113eSDavid van Moolenbroek 	struct option *option = NULL;
1136*83ee113eSDavid van Moolenbroek 	unsigned code;
1137*83ee113eSDavid van Moolenbroek 
1138*83ee113eSDavid van Moolenbroek 	/*
1139*83ee113eSDavid van Moolenbroek 	 * These arguments are relative to the start of the buffer, so
1140*83ee113eSDavid van Moolenbroek 	 * reduce them by the current buffer index, and advance the
1141*83ee113eSDavid van Moolenbroek 	 * buffer pointer to where we're going to start writing.
1142*83ee113eSDavid van Moolenbroek 	 */
1143*83ee113eSDavid van Moolenbroek 	buffer = &buffer[index];
1144*83ee113eSDavid van Moolenbroek 	buflen -= index;
1145*83ee113eSDavid van Moolenbroek 	if (first_cutoff)
1146*83ee113eSDavid van Moolenbroek 		first_cutoff -= index;
1147*83ee113eSDavid van Moolenbroek 	if (second_cutoff)
1148*83ee113eSDavid van Moolenbroek 		second_cutoff -= index;
1149*83ee113eSDavid van Moolenbroek 
1150*83ee113eSDavid van Moolenbroek 	/* Calculate the start and end of each section of the buffer */
1151*83ee113eSDavid van Moolenbroek 	bufend = sbufend = buflen;
1152*83ee113eSDavid van Moolenbroek 	if (first_cutoff) {
1153*83ee113eSDavid van Moolenbroek 	    if (first_cutoff >= buflen)
1154*83ee113eSDavid van Moolenbroek 		log_fatal("%s:%d:store_options: Invalid first cutoff.", MDL);
1155*83ee113eSDavid van Moolenbroek 	    bufend = first_cutoff;
1156*83ee113eSDavid van Moolenbroek 
1157*83ee113eSDavid van Moolenbroek 	    if (second_cutoff) {
1158*83ee113eSDavid van Moolenbroek 	        if (second_cutoff >= buflen)
1159*83ee113eSDavid van Moolenbroek 		    log_fatal("%s:%d:store_options: Invalid second cutoff.",
1160*83ee113eSDavid van Moolenbroek 			      MDL);
1161*83ee113eSDavid van Moolenbroek 	        sbufend = second_cutoff;
1162*83ee113eSDavid van Moolenbroek 	    }
1163*83ee113eSDavid van Moolenbroek 	} else if (second_cutoff) {
1164*83ee113eSDavid van Moolenbroek 	    if (second_cutoff >= buflen)
1165*83ee113eSDavid van Moolenbroek 		log_fatal("%s:%d:store_options: Invalid second cutoff.", MDL);
1166*83ee113eSDavid van Moolenbroek 	    bufend = second_cutoff;
1167*83ee113eSDavid van Moolenbroek 	}
1168*83ee113eSDavid van Moolenbroek 
1169*83ee113eSDavid van Moolenbroek 	memset (&od, 0, sizeof od);
1170*83ee113eSDavid van Moolenbroek 
1171*83ee113eSDavid van Moolenbroek 	/* Eliminate duplicate options from the parameter request list.
1172*83ee113eSDavid van Moolenbroek 	 * Enforce RFC-mandated ordering of options that are present.
1173*83ee113eSDavid van Moolenbroek 	 */
1174*83ee113eSDavid van Moolenbroek 	for (i = 0; i < priority_len - 1; i++) {
1175*83ee113eSDavid van Moolenbroek 		/* Eliminate duplicates. */
1176*83ee113eSDavid van Moolenbroek 		tto = 0;
1177*83ee113eSDavid van Moolenbroek 		for (ix = i + 1; ix < priority_len + tto; ix++) {
1178*83ee113eSDavid van Moolenbroek 			if (tto)
1179*83ee113eSDavid van Moolenbroek 				priority_list [ix - tto] =
1180*83ee113eSDavid van Moolenbroek 					priority_list [ix];
1181*83ee113eSDavid van Moolenbroek 			if (priority_list [i] == priority_list [ix]) {
1182*83ee113eSDavid van Moolenbroek 				tto++;
1183*83ee113eSDavid van Moolenbroek 				priority_len--;
1184*83ee113eSDavid van Moolenbroek 			}
1185*83ee113eSDavid van Moolenbroek 		}
1186*83ee113eSDavid van Moolenbroek 
1187*83ee113eSDavid van Moolenbroek 		/* Enforce ordering of SUBNET_MASK options, according to
1188*83ee113eSDavid van Moolenbroek 		 * RFC2132 Section 3.3:
1189*83ee113eSDavid van Moolenbroek 		 *
1190*83ee113eSDavid van Moolenbroek 		 *   If both the subnet mask and the router option are
1191*83ee113eSDavid van Moolenbroek 		 *   specified in a DHCP reply, the subnet mask option MUST
1192*83ee113eSDavid van Moolenbroek 		 *   be first.
1193*83ee113eSDavid van Moolenbroek 		 *
1194*83ee113eSDavid van Moolenbroek 		 * This guidance does not specify what to do if the client
1195*83ee113eSDavid van Moolenbroek 		 * PRL explicitly requests the options out of order, it is
1196*83ee113eSDavid van Moolenbroek 		 * a general statement.
1197*83ee113eSDavid van Moolenbroek 		 */
1198*83ee113eSDavid van Moolenbroek 		if (priority_list[i] == DHO_SUBNET_MASK) {
1199*83ee113eSDavid van Moolenbroek 			for (ix = i - 1 ; ix >= 0 ; ix--) {
1200*83ee113eSDavid van Moolenbroek 				if (priority_list[ix] == DHO_ROUTERS) {
1201*83ee113eSDavid van Moolenbroek                                         /* swap */
1202*83ee113eSDavid van Moolenbroek 					priority_list[ix] = DHO_SUBNET_MASK;
1203*83ee113eSDavid van Moolenbroek 					priority_list[i] = DHO_ROUTERS;
1204*83ee113eSDavid van Moolenbroek 					break;
1205*83ee113eSDavid van Moolenbroek 				}
1206*83ee113eSDavid van Moolenbroek 			}
1207*83ee113eSDavid van Moolenbroek 		}
1208*83ee113eSDavid van Moolenbroek 	}
1209*83ee113eSDavid van Moolenbroek 
1210*83ee113eSDavid van Moolenbroek 	/* Copy out the options in the order that they appear in the
1211*83ee113eSDavid van Moolenbroek 	   priority list... */
1212*83ee113eSDavid van Moolenbroek 	for (i = 0; i < priority_len; i++) {
1213*83ee113eSDavid van Moolenbroek 	    /* Number of bytes left to store (some may already
1214*83ee113eSDavid van Moolenbroek 	       have been stored by a previous pass). */
1215*83ee113eSDavid van Moolenbroek 	    unsigned length;
1216*83ee113eSDavid van Moolenbroek 	    int optstart, soptstart, toptstart;
1217*83ee113eSDavid van Moolenbroek 	    struct universe *u;
1218*83ee113eSDavid van Moolenbroek 	    int have_encapsulation = 0;
1219*83ee113eSDavid van Moolenbroek 	    struct data_string encapsulation;
1220*83ee113eSDavid van Moolenbroek 	    int splitup;
1221*83ee113eSDavid van Moolenbroek 
1222*83ee113eSDavid van Moolenbroek 	    memset (&encapsulation, 0, sizeof encapsulation);
1223*83ee113eSDavid van Moolenbroek 	    have_encapsulation = 0;
1224*83ee113eSDavid van Moolenbroek 
1225*83ee113eSDavid van Moolenbroek 	    if (option != NULL)
1226*83ee113eSDavid van Moolenbroek 		option_dereference(&option, MDL);
1227*83ee113eSDavid van Moolenbroek 
1228*83ee113eSDavid van Moolenbroek 	    /* Code for next option to try to store. */
1229*83ee113eSDavid van Moolenbroek 	    code = priority_list [i];
1230*83ee113eSDavid van Moolenbroek 
1231*83ee113eSDavid van Moolenbroek 	    /* Look up the option in the site option space if the code
1232*83ee113eSDavid van Moolenbroek 	       is above the cutoff, otherwise in the DHCP option space. */
1233*83ee113eSDavid van Moolenbroek 	    if (code >= cfg_options -> site_code_min)
1234*83ee113eSDavid van Moolenbroek 		    u = universes [cfg_options -> site_universe];
1235*83ee113eSDavid van Moolenbroek 	    else
1236*83ee113eSDavid van Moolenbroek 		    u = &dhcp_universe;
1237*83ee113eSDavid van Moolenbroek 
1238*83ee113eSDavid van Moolenbroek 	    oc = lookup_option (u, cfg_options, code);
1239*83ee113eSDavid van Moolenbroek 
1240*83ee113eSDavid van Moolenbroek 	    if (oc && oc->option)
1241*83ee113eSDavid van Moolenbroek 		option_reference(&option, oc->option, MDL);
1242*83ee113eSDavid van Moolenbroek 	    else
1243*83ee113eSDavid van Moolenbroek 		option_code_hash_lookup(&option, u->code_hash, &code, 0, MDL);
1244*83ee113eSDavid van Moolenbroek 
1245*83ee113eSDavid van Moolenbroek 	    /* If it's a straight encapsulation, and the user supplied a
1246*83ee113eSDavid van Moolenbroek 	     * value for the entire option, use that.  Otherwise, search
1247*83ee113eSDavid van Moolenbroek 	     * the encapsulated space.
1248*83ee113eSDavid van Moolenbroek 	     *
1249*83ee113eSDavid van Moolenbroek 	     * If it's a limited encapsulation with preceding data, and the
1250*83ee113eSDavid van Moolenbroek 	     * user supplied values for the preceding bytes, search the
1251*83ee113eSDavid van Moolenbroek 	     * encapsulated space.
1252*83ee113eSDavid van Moolenbroek 	     */
1253*83ee113eSDavid van Moolenbroek 	    if ((option != NULL) &&
1254*83ee113eSDavid van Moolenbroek 		(((oc == NULL) && (option->format[0] == 'E')) ||
1255*83ee113eSDavid van Moolenbroek 		 ((oc != NULL) && (option->format[0] == 'e')))) {
1256*83ee113eSDavid van Moolenbroek 		static char *s, *t;
1257*83ee113eSDavid van Moolenbroek 		struct option_cache *tmp;
1258*83ee113eSDavid van Moolenbroek 		struct data_string name;
1259*83ee113eSDavid van Moolenbroek 
1260*83ee113eSDavid van Moolenbroek 		s = strchr (option->format, 'E');
1261*83ee113eSDavid van Moolenbroek 		if (s)
1262*83ee113eSDavid van Moolenbroek 		    t = strchr (++s, '.');
1263*83ee113eSDavid van Moolenbroek 		if (s && t) {
1264*83ee113eSDavid van Moolenbroek 		    memset (&name, 0, sizeof name);
1265*83ee113eSDavid van Moolenbroek 
1266*83ee113eSDavid van Moolenbroek 		    /* A zero-length universe name means the vendor
1267*83ee113eSDavid van Moolenbroek 		       option space, if one is defined. */
1268*83ee113eSDavid van Moolenbroek 		    if (t == s) {
1269*83ee113eSDavid van Moolenbroek 			if (vendor_cfg_option) {
1270*83ee113eSDavid van Moolenbroek 			    tmp = lookup_option (vendor_cfg_option -> universe,
1271*83ee113eSDavid van Moolenbroek 						 cfg_options,
1272*83ee113eSDavid van Moolenbroek 						 vendor_cfg_option -> code);
1273*83ee113eSDavid van Moolenbroek 			    if (tmp)
1274*83ee113eSDavid van Moolenbroek 				/* No need to check the return as we check name.len below */
1275*83ee113eSDavid van Moolenbroek 				(void) evaluate_option_cache (&name, packet, lease,
1276*83ee113eSDavid van Moolenbroek 							      client_state,
1277*83ee113eSDavid van Moolenbroek 							      in_options,
1278*83ee113eSDavid van Moolenbroek 							      cfg_options,
1279*83ee113eSDavid van Moolenbroek 							      scope, tmp, MDL);
1280*83ee113eSDavid van Moolenbroek 			} else if (vuname) {
1281*83ee113eSDavid van Moolenbroek 			    name.data = (unsigned char *)s;
1282*83ee113eSDavid van Moolenbroek 			    name.len = strlen (s);
1283*83ee113eSDavid van Moolenbroek 			}
1284*83ee113eSDavid van Moolenbroek 		    } else {
1285*83ee113eSDavid van Moolenbroek 			name.data = (unsigned char *)s;
1286*83ee113eSDavid van Moolenbroek 			name.len = t - s;
1287*83ee113eSDavid van Moolenbroek 		    }
1288*83ee113eSDavid van Moolenbroek 
1289*83ee113eSDavid van Moolenbroek 		    /* If we found a universe, and there are options configured
1290*83ee113eSDavid van Moolenbroek 		       for that universe, try to encapsulate it. */
1291*83ee113eSDavid van Moolenbroek 		    if (name.len) {
1292*83ee113eSDavid van Moolenbroek 			have_encapsulation =
1293*83ee113eSDavid van Moolenbroek 				(option_space_encapsulate
1294*83ee113eSDavid van Moolenbroek 				 (&encapsulation, packet, lease, client_state,
1295*83ee113eSDavid van Moolenbroek 				  in_options, cfg_options, scope, &name));
1296*83ee113eSDavid van Moolenbroek 			data_string_forget (&name, MDL);
1297*83ee113eSDavid van Moolenbroek 		    }
1298*83ee113eSDavid van Moolenbroek 		}
1299*83ee113eSDavid van Moolenbroek 	    }
1300*83ee113eSDavid van Moolenbroek 
1301*83ee113eSDavid van Moolenbroek 	    /* In order to avoid memory leaks, we have to get to here
1302*83ee113eSDavid van Moolenbroek 	       with any option cache that we allocated in tmp not being
1303*83ee113eSDavid van Moolenbroek 	       referenced by tmp, and whatever option cache is referenced
1304*83ee113eSDavid van Moolenbroek 	       by oc being an actual reference.   lookup_option doesn't
1305*83ee113eSDavid van Moolenbroek 	       generate a reference (this needs to be fixed), so the
1306*83ee113eSDavid van Moolenbroek 	       preceding goop ensures that if we *didn't* generate a new
1307*83ee113eSDavid van Moolenbroek 	       option cache, oc still winds up holding an actual reference. */
1308*83ee113eSDavid van Moolenbroek 
1309*83ee113eSDavid van Moolenbroek 	    /* If no data is available for this option, skip it. */
1310*83ee113eSDavid van Moolenbroek 	    if (!oc && !have_encapsulation) {
1311*83ee113eSDavid van Moolenbroek 		    continue;
1312*83ee113eSDavid van Moolenbroek 	    }
1313*83ee113eSDavid van Moolenbroek 
1314*83ee113eSDavid van Moolenbroek 	    /* Find the value of the option... */
1315*83ee113eSDavid van Moolenbroek 	    od.len = 0;
1316*83ee113eSDavid van Moolenbroek 	    if (oc) {
1317*83ee113eSDavid van Moolenbroek 		/* No need to check the return as we check od.len below */
1318*83ee113eSDavid van Moolenbroek 		(void) evaluate_option_cache (&od, packet,
1319*83ee113eSDavid van Moolenbroek 					      lease, client_state, in_options,
1320*83ee113eSDavid van Moolenbroek 					      cfg_options, scope, oc, MDL);
1321*83ee113eSDavid van Moolenbroek 
1322*83ee113eSDavid van Moolenbroek 		/* If we have encapsulation for this option, and an oc
1323*83ee113eSDavid van Moolenbroek 		 * lookup succeeded, but the evaluation failed, it is
1324*83ee113eSDavid van Moolenbroek 		 * either because this is a complex atom (atoms before
1325*83ee113eSDavid van Moolenbroek 		 * E on format list) and the top half of the option is
1326*83ee113eSDavid van Moolenbroek 		 * not configured, or this is a simple encapsulated
1327*83ee113eSDavid van Moolenbroek 		 * space and the evaluator is giving us a NULL.  Prefer
1328*83ee113eSDavid van Moolenbroek 		 * the evaluator's opinion over the subspace.
1329*83ee113eSDavid van Moolenbroek 		 */
1330*83ee113eSDavid van Moolenbroek 		if (!od.len) {
1331*83ee113eSDavid van Moolenbroek 		    data_string_forget (&encapsulation, MDL);
1332*83ee113eSDavid van Moolenbroek 		    data_string_forget (&od, MDL);
1333*83ee113eSDavid van Moolenbroek 		    continue;
1334*83ee113eSDavid van Moolenbroek 		}
1335*83ee113eSDavid van Moolenbroek 	    }
1336*83ee113eSDavid van Moolenbroek 
1337*83ee113eSDavid van Moolenbroek 	    /* We should now have a constant length for the option. */
1338*83ee113eSDavid van Moolenbroek 	    length = od.len;
1339*83ee113eSDavid van Moolenbroek 	    if (have_encapsulation) {
1340*83ee113eSDavid van Moolenbroek 		    length += encapsulation.len;
1341*83ee113eSDavid van Moolenbroek 
1342*83ee113eSDavid van Moolenbroek 		    /* od.len can be nonzero if we got here without an
1343*83ee113eSDavid van Moolenbroek 		     * oc (cache lookup failed), but did have an encapsulated
1344*83ee113eSDavid van Moolenbroek 		     * simple encapsulation space.
1345*83ee113eSDavid van Moolenbroek 		     */
1346*83ee113eSDavid van Moolenbroek 		    if (!od.len) {
1347*83ee113eSDavid van Moolenbroek 			    data_string_copy (&od, &encapsulation, MDL);
1348*83ee113eSDavid van Moolenbroek 			    data_string_forget (&encapsulation, MDL);
1349*83ee113eSDavid van Moolenbroek 		    } else {
1350*83ee113eSDavid van Moolenbroek 			    struct buffer *bp = (struct buffer *)0;
1351*83ee113eSDavid van Moolenbroek 			    if (!buffer_allocate (&bp, length, MDL)) {
1352*83ee113eSDavid van Moolenbroek 				    option_cache_dereference (&oc, MDL);
1353*83ee113eSDavid van Moolenbroek 				    data_string_forget (&od, MDL);
1354*83ee113eSDavid van Moolenbroek 				    data_string_forget (&encapsulation, MDL);
1355*83ee113eSDavid van Moolenbroek 				    continue;
1356*83ee113eSDavid van Moolenbroek 			    }
1357*83ee113eSDavid van Moolenbroek 			    memcpy (&bp -> data [0], od.data, od.len);
1358*83ee113eSDavid van Moolenbroek 			    memcpy (&bp -> data [od.len], encapsulation.data,
1359*83ee113eSDavid van Moolenbroek 				    encapsulation.len);
1360*83ee113eSDavid van Moolenbroek 			    data_string_forget (&od, MDL);
1361*83ee113eSDavid van Moolenbroek 			    data_string_forget (&encapsulation, MDL);
1362*83ee113eSDavid van Moolenbroek 			    od.data = &bp -> data [0];
1363*83ee113eSDavid van Moolenbroek 			    buffer_reference (&od.buffer, bp, MDL);
1364*83ee113eSDavid van Moolenbroek 			    buffer_dereference (&bp, MDL);
1365*83ee113eSDavid van Moolenbroek 			    od.len = length;
1366*83ee113eSDavid van Moolenbroek 			    od.terminated = 0;
1367*83ee113eSDavid van Moolenbroek 		    }
1368*83ee113eSDavid van Moolenbroek 	    }
1369*83ee113eSDavid van Moolenbroek 
1370*83ee113eSDavid van Moolenbroek 	    /* Do we add a NUL? */
1371*83ee113eSDavid van Moolenbroek 	    if (terminate && option && format_has_text(option->format)) {
1372*83ee113eSDavid van Moolenbroek 		    length++;
1373*83ee113eSDavid van Moolenbroek 		    tto = 1;
1374*83ee113eSDavid van Moolenbroek 	    } else {
1375*83ee113eSDavid van Moolenbroek 		    tto = 0;
1376*83ee113eSDavid van Moolenbroek 	    }
1377*83ee113eSDavid van Moolenbroek 
1378*83ee113eSDavid van Moolenbroek 	    /* Try to store the option. */
1379*83ee113eSDavid van Moolenbroek 
1380*83ee113eSDavid van Moolenbroek 	    /* If the option's length is more than 255, we must store it
1381*83ee113eSDavid van Moolenbroek 	       in multiple hunks.   Store 255-byte hunks first.  However,
1382*83ee113eSDavid van Moolenbroek 	       in any case, if the option data will cross a buffer
1383*83ee113eSDavid van Moolenbroek 	       boundary, split it across that boundary. */
1384*83ee113eSDavid van Moolenbroek 
1385*83ee113eSDavid van Moolenbroek 	    if (length > 255)
1386*83ee113eSDavid van Moolenbroek 		splitup = 1;
1387*83ee113eSDavid van Moolenbroek 	    else
1388*83ee113eSDavid van Moolenbroek 		splitup = 0;
1389*83ee113eSDavid van Moolenbroek 
1390*83ee113eSDavid van Moolenbroek 	    ix = 0;
1391*83ee113eSDavid van Moolenbroek 	    optstart = bufix;
1392*83ee113eSDavid van Moolenbroek 	    soptstart = six;
1393*83ee113eSDavid van Moolenbroek 	    toptstart = tix;
1394*83ee113eSDavid van Moolenbroek 	    while (length) {
1395*83ee113eSDavid van Moolenbroek 		    unsigned incr = length;
1396*83ee113eSDavid van Moolenbroek 		    int *pix;
1397*83ee113eSDavid van Moolenbroek 		    unsigned char *base;
1398*83ee113eSDavid van Moolenbroek 
1399*83ee113eSDavid van Moolenbroek 		    /* Try to fit it in the options buffer. */
1400*83ee113eSDavid van Moolenbroek 		    if (!splitup &&
1401*83ee113eSDavid van Moolenbroek 			((!six && !tix && (i == priority_len - 1) &&
1402*83ee113eSDavid van Moolenbroek 			  (bufix + 2 + length < bufend)) ||
1403*83ee113eSDavid van Moolenbroek 			 (bufix + 5 + length < bufend))) {
1404*83ee113eSDavid van Moolenbroek 			base = buffer;
1405*83ee113eSDavid van Moolenbroek 			pix = &bufix;
1406*83ee113eSDavid van Moolenbroek 		    /* Try to fit it in the second buffer. */
1407*83ee113eSDavid van Moolenbroek 		    } else if (!splitup && first_cutoff &&
1408*83ee113eSDavid van Moolenbroek 			       (first_cutoff + six + 3 + length < sbufend)) {
1409*83ee113eSDavid van Moolenbroek 			base = &buffer[first_cutoff];
1410*83ee113eSDavid van Moolenbroek 			pix = &six;
1411*83ee113eSDavid van Moolenbroek 		    /* Try to fit it in the third buffer. */
1412*83ee113eSDavid van Moolenbroek 		    } else if (!splitup && second_cutoff &&
1413*83ee113eSDavid van Moolenbroek 			       (second_cutoff + tix + 3 + length < buflen)) {
1414*83ee113eSDavid van Moolenbroek 			base = &buffer[second_cutoff];
1415*83ee113eSDavid van Moolenbroek 			pix = &tix;
1416*83ee113eSDavid van Moolenbroek 		    /* Split the option up into the remaining space. */
1417*83ee113eSDavid van Moolenbroek 		    } else {
1418*83ee113eSDavid van Moolenbroek 			splitup = 1;
1419*83ee113eSDavid van Moolenbroek 
1420*83ee113eSDavid van Moolenbroek 			/* Use any remaining options space. */
1421*83ee113eSDavid van Moolenbroek 			if (bufix + 6 < bufend) {
1422*83ee113eSDavid van Moolenbroek 			    incr = bufend - bufix - 5;
1423*83ee113eSDavid van Moolenbroek 			    base = buffer;
1424*83ee113eSDavid van Moolenbroek 			    pix = &bufix;
1425*83ee113eSDavid van Moolenbroek 			/* Use any remaining first_cutoff space. */
1426*83ee113eSDavid van Moolenbroek 			} else if (first_cutoff &&
1427*83ee113eSDavid van Moolenbroek 				   (first_cutoff + six + 4 < sbufend)) {
1428*83ee113eSDavid van Moolenbroek 			    incr = sbufend - (first_cutoff + six) - 3;
1429*83ee113eSDavid van Moolenbroek 			    base = &buffer[first_cutoff];
1430*83ee113eSDavid van Moolenbroek 			    pix = &six;
1431*83ee113eSDavid van Moolenbroek 			/* Use any remaining second_cutoff space. */
1432*83ee113eSDavid van Moolenbroek 			} else if (second_cutoff &&
1433*83ee113eSDavid van Moolenbroek 				   (second_cutoff + tix + 4 < buflen)) {
1434*83ee113eSDavid van Moolenbroek 			    incr = buflen - (second_cutoff + tix) - 3;
1435*83ee113eSDavid van Moolenbroek 			    base = &buffer[second_cutoff];
1436*83ee113eSDavid van Moolenbroek 			    pix = &tix;
1437*83ee113eSDavid van Moolenbroek 			/* Give up, roll back this option. */
1438*83ee113eSDavid van Moolenbroek 			} else {
1439*83ee113eSDavid van Moolenbroek 			    bufix = optstart;
1440*83ee113eSDavid van Moolenbroek 			    six = soptstart;
1441*83ee113eSDavid van Moolenbroek 			    tix = toptstart;
1442*83ee113eSDavid van Moolenbroek 			    break;
1443*83ee113eSDavid van Moolenbroek 			}
1444*83ee113eSDavid van Moolenbroek 		    }
1445*83ee113eSDavid van Moolenbroek 
1446*83ee113eSDavid van Moolenbroek 		    if (incr > length)
1447*83ee113eSDavid van Moolenbroek 			incr = length;
1448*83ee113eSDavid van Moolenbroek 		    if (incr > 255)
1449*83ee113eSDavid van Moolenbroek 			incr = 255;
1450*83ee113eSDavid van Moolenbroek 
1451*83ee113eSDavid van Moolenbroek 		    /* Everything looks good - copy it in! */
1452*83ee113eSDavid van Moolenbroek 		    base [*pix] = code;
1453*83ee113eSDavid van Moolenbroek 		    base [*pix + 1] = (unsigned char)incr;
1454*83ee113eSDavid van Moolenbroek 		    if (tto && incr == length) {
1455*83ee113eSDavid van Moolenbroek 			    if (incr > 1)
1456*83ee113eSDavid van Moolenbroek 				memcpy (base + *pix + 2,
1457*83ee113eSDavid van Moolenbroek 					od.data + ix, (unsigned)(incr - 1));
1458*83ee113eSDavid van Moolenbroek 			    base [*pix + 2 + incr - 1] = 0;
1459*83ee113eSDavid van Moolenbroek 		    } else {
1460*83ee113eSDavid van Moolenbroek 			    memcpy (base + *pix + 2,
1461*83ee113eSDavid van Moolenbroek 				    od.data + ix, (unsigned)incr);
1462*83ee113eSDavid van Moolenbroek 		    }
1463*83ee113eSDavid van Moolenbroek 		    length -= incr;
1464*83ee113eSDavid van Moolenbroek 		    ix += incr;
1465*83ee113eSDavid van Moolenbroek 		    *pix += 2 + incr;
1466*83ee113eSDavid van Moolenbroek 	    }
1467*83ee113eSDavid van Moolenbroek 	    data_string_forget (&od, MDL);
1468*83ee113eSDavid van Moolenbroek 	}
1469*83ee113eSDavid van Moolenbroek 
1470*83ee113eSDavid van Moolenbroek 	if (option != NULL)
1471*83ee113eSDavid van Moolenbroek 	    option_dereference(&option, MDL);
1472*83ee113eSDavid van Moolenbroek 
1473*83ee113eSDavid van Moolenbroek 	/* If we can overload, and we have, then PAD and END those spaces. */
1474*83ee113eSDavid van Moolenbroek 	if (first_cutoff && six) {
1475*83ee113eSDavid van Moolenbroek 	    if ((first_cutoff + six + 1) < sbufend)
1476*83ee113eSDavid van Moolenbroek 		memset (&buffer[first_cutoff + six + 1], DHO_PAD,
1477*83ee113eSDavid van Moolenbroek 			sbufend - (first_cutoff + six + 1));
1478*83ee113eSDavid van Moolenbroek 	    else if (first_cutoff + six >= sbufend)
1479*83ee113eSDavid van Moolenbroek 		log_fatal("Second buffer overflow in overloaded options.");
1480*83ee113eSDavid van Moolenbroek 
1481*83ee113eSDavid van Moolenbroek 	    buffer[first_cutoff + six] = DHO_END;
1482*83ee113eSDavid van Moolenbroek 	    if (ocount != NULL)
1483*83ee113eSDavid van Moolenbroek 	    	*ocount |= 1; /* So that caller knows there's data there. */
1484*83ee113eSDavid van Moolenbroek 	}
1485*83ee113eSDavid van Moolenbroek 
1486*83ee113eSDavid van Moolenbroek 	if (second_cutoff && tix) {
1487*83ee113eSDavid van Moolenbroek 	    if (second_cutoff + tix + 1 < buflen) {
1488*83ee113eSDavid van Moolenbroek 		memset (&buffer[second_cutoff + tix + 1], DHO_PAD,
1489*83ee113eSDavid van Moolenbroek 			buflen - (second_cutoff + tix + 1));
1490*83ee113eSDavid van Moolenbroek 	    } else if (second_cutoff + tix >= buflen)
1491*83ee113eSDavid van Moolenbroek 		log_fatal("Third buffer overflow in overloaded options.");
1492*83ee113eSDavid van Moolenbroek 
1493*83ee113eSDavid van Moolenbroek 	    buffer[second_cutoff + tix] = DHO_END;
1494*83ee113eSDavid van Moolenbroek 	    if (ocount != NULL)
1495*83ee113eSDavid van Moolenbroek 	    	*ocount |= 2; /* So that caller knows there's data there. */
1496*83ee113eSDavid van Moolenbroek 	}
1497*83ee113eSDavid van Moolenbroek 
1498*83ee113eSDavid van Moolenbroek 	if ((six || tix) && (bufix + 3 > bufend))
1499*83ee113eSDavid van Moolenbroek 	    log_fatal("Not enough space for option overload option.");
1500*83ee113eSDavid van Moolenbroek 
1501*83ee113eSDavid van Moolenbroek 	return bufix;
1502*83ee113eSDavid van Moolenbroek }
1503*83ee113eSDavid van Moolenbroek 
1504*83ee113eSDavid van Moolenbroek /* Return true if the format string has a variable length text option
1505*83ee113eSDavid van Moolenbroek  * ("t"), return false otherwise.
1506*83ee113eSDavid van Moolenbroek  */
1507*83ee113eSDavid van Moolenbroek 
1508*83ee113eSDavid van Moolenbroek int
format_has_text(format)1509*83ee113eSDavid van Moolenbroek format_has_text(format)
1510*83ee113eSDavid van Moolenbroek 	const char *format;
1511*83ee113eSDavid van Moolenbroek {
1512*83ee113eSDavid van Moolenbroek 	const char *p;
1513*83ee113eSDavid van Moolenbroek 
1514*83ee113eSDavid van Moolenbroek 	p = format;
1515*83ee113eSDavid van Moolenbroek 	while (*p != '\0') {
1516*83ee113eSDavid van Moolenbroek 		switch (*p++) {
1517*83ee113eSDavid van Moolenbroek 		    case 'd':
1518*83ee113eSDavid van Moolenbroek 		    case 't':
1519*83ee113eSDavid van Moolenbroek 			return 1;
1520*83ee113eSDavid van Moolenbroek 
1521*83ee113eSDavid van Moolenbroek 			/* These symbols are arbitrary, not fixed or
1522*83ee113eSDavid van Moolenbroek 			 * determinable length...text options with them is
1523*83ee113eSDavid van Moolenbroek 			 * invalid (whatever the case, they are never NULL
1524*83ee113eSDavid van Moolenbroek 			 * terminated).
1525*83ee113eSDavid van Moolenbroek 			 */
1526*83ee113eSDavid van Moolenbroek 		    case 'A':
1527*83ee113eSDavid van Moolenbroek 		    case 'a':
1528*83ee113eSDavid van Moolenbroek 		    case 'X':
1529*83ee113eSDavid van Moolenbroek 		    case 'x':
1530*83ee113eSDavid van Moolenbroek 		    case 'D':
1531*83ee113eSDavid van Moolenbroek 			return 0;
1532*83ee113eSDavid van Moolenbroek 
1533*83ee113eSDavid van Moolenbroek 		    case 'c':
1534*83ee113eSDavid van Moolenbroek 			/* 'c' only follows 'D' atoms, and indicates that
1535*83ee113eSDavid van Moolenbroek 			 * compression may be used.  If there was a 'D'
1536*83ee113eSDavid van Moolenbroek 			 * atom already, we would have returned.  So this
1537*83ee113eSDavid van Moolenbroek 			 * is an error, but continue looking for 't' anyway.
1538*83ee113eSDavid van Moolenbroek 			 */
1539*83ee113eSDavid van Moolenbroek 			log_error("format_has_text(%s): 'c' atoms are illegal "
1540*83ee113eSDavid van Moolenbroek 				  "except after 'D' atoms.", format);
1541*83ee113eSDavid van Moolenbroek 			break;
1542*83ee113eSDavid van Moolenbroek 
1543*83ee113eSDavid van Moolenbroek 			/* 'E' is variable length, but not arbitrary...you
1544*83ee113eSDavid van Moolenbroek 			 * can find its length if you can find an END option.
1545*83ee113eSDavid van Moolenbroek 			 * N is (n)-byte in length but trails a name of a
1546*83ee113eSDavid van Moolenbroek 			 * space defining the enumeration values.  So treat
1547*83ee113eSDavid van Moolenbroek 			 * both the same - valid, fixed-length fields.
1548*83ee113eSDavid van Moolenbroek 			 */
1549*83ee113eSDavid van Moolenbroek 		    case 'E':
1550*83ee113eSDavid van Moolenbroek 		    case 'N':
1551*83ee113eSDavid van Moolenbroek 			/* Consume the space name. */
1552*83ee113eSDavid van Moolenbroek 			while ((*p != '\0') && (*p++ != '.'))
1553*83ee113eSDavid van Moolenbroek 				;
1554*83ee113eSDavid van Moolenbroek 			break;
1555*83ee113eSDavid van Moolenbroek 
1556*83ee113eSDavid van Moolenbroek 		    default:
1557*83ee113eSDavid van Moolenbroek 			break;
1558*83ee113eSDavid van Moolenbroek 		}
1559*83ee113eSDavid van Moolenbroek 	}
1560*83ee113eSDavid van Moolenbroek 
1561*83ee113eSDavid van Moolenbroek 	return 0;
1562*83ee113eSDavid van Moolenbroek }
1563*83ee113eSDavid van Moolenbroek 
1564*83ee113eSDavid van Moolenbroek /* Determine the minimum length of a DHCP option prior to any variable
1565*83ee113eSDavid van Moolenbroek  * or inconsistent length formats, according to its configured format
1566*83ee113eSDavid van Moolenbroek  * variable (and possibly from supplied option cache contents for variable
1567*83ee113eSDavid van Moolenbroek  * length format symbols).
1568*83ee113eSDavid van Moolenbroek  */
1569*83ee113eSDavid van Moolenbroek 
1570*83ee113eSDavid van Moolenbroek int
format_min_length(format,oc)1571*83ee113eSDavid van Moolenbroek format_min_length(format, oc)
1572*83ee113eSDavid van Moolenbroek 	const char *format;
1573*83ee113eSDavid van Moolenbroek 	struct option_cache *oc;
1574*83ee113eSDavid van Moolenbroek {
1575*83ee113eSDavid van Moolenbroek 	const char *p, *name;
1576*83ee113eSDavid van Moolenbroek 	int min_len = 0;
1577*83ee113eSDavid van Moolenbroek 	int last_size = 0;
1578*83ee113eSDavid van Moolenbroek 	struct enumeration *espace;
1579*83ee113eSDavid van Moolenbroek 
1580*83ee113eSDavid van Moolenbroek 	p = format;
1581*83ee113eSDavid van Moolenbroek 	while (*p != '\0') {
1582*83ee113eSDavid van Moolenbroek 		switch (*p++) {
1583*83ee113eSDavid van Moolenbroek 		    case '6': /* IPv6 Address */
1584*83ee113eSDavid van Moolenbroek 			min_len += 16;
1585*83ee113eSDavid van Moolenbroek 			last_size = 16;
1586*83ee113eSDavid van Moolenbroek 			break;
1587*83ee113eSDavid van Moolenbroek 
1588*83ee113eSDavid van Moolenbroek 		    case 'I': /* IPv4 Address */
1589*83ee113eSDavid van Moolenbroek 		    case 'l': /* int32_t */
1590*83ee113eSDavid van Moolenbroek 		    case 'L': /* uint32_t */
1591*83ee113eSDavid van Moolenbroek 		    case 'T': /* Lease Time, uint32_t equivalent */
1592*83ee113eSDavid van Moolenbroek 			min_len += 4;
1593*83ee113eSDavid van Moolenbroek 			last_size = 4;
1594*83ee113eSDavid van Moolenbroek 			break;
1595*83ee113eSDavid van Moolenbroek 
1596*83ee113eSDavid van Moolenbroek 		    case 's': /* int16_t */
1597*83ee113eSDavid van Moolenbroek 		    case 'S': /* uint16_t */
1598*83ee113eSDavid van Moolenbroek 			min_len += 2;
1599*83ee113eSDavid van Moolenbroek 			last_size = 2;
1600*83ee113eSDavid van Moolenbroek 			break;
1601*83ee113eSDavid van Moolenbroek 
1602*83ee113eSDavid van Moolenbroek 		    case 'N': /* Enumeration value. */
1603*83ee113eSDavid van Moolenbroek 			/* Consume space name. */
1604*83ee113eSDavid van Moolenbroek 			name = p;
1605*83ee113eSDavid van Moolenbroek 			p = strchr(p, '.');
1606*83ee113eSDavid van Moolenbroek 			if (p == NULL)
1607*83ee113eSDavid van Moolenbroek 				log_fatal("Corrupt format: %s", format);
1608*83ee113eSDavid van Moolenbroek 
1609*83ee113eSDavid van Moolenbroek 			espace = find_enumeration(name, p - name);
1610*83ee113eSDavid van Moolenbroek 			if (espace == NULL) {
1611*83ee113eSDavid van Moolenbroek 				log_error("Unknown enumeration: %s", format);
1612*83ee113eSDavid van Moolenbroek 				/* Max is safest value to return. */
1613*83ee113eSDavid van Moolenbroek 				return INT_MAX;
1614*83ee113eSDavid van Moolenbroek 			}
1615*83ee113eSDavid van Moolenbroek 
1616*83ee113eSDavid van Moolenbroek 			min_len += espace->width;
1617*83ee113eSDavid van Moolenbroek 			last_size = espace->width;
1618*83ee113eSDavid van Moolenbroek 			p++;
1619*83ee113eSDavid van Moolenbroek 
1620*83ee113eSDavid van Moolenbroek 			break;
1621*83ee113eSDavid van Moolenbroek 
1622*83ee113eSDavid van Moolenbroek 		    case 'b': /* int8_t */
1623*83ee113eSDavid van Moolenbroek 		    case 'B': /* uint8_t */
1624*83ee113eSDavid van Moolenbroek 		    case 'F': /* Flag that is always true. */
1625*83ee113eSDavid van Moolenbroek 		    case 'f': /* Flag */
1626*83ee113eSDavid van Moolenbroek 			min_len++;
1627*83ee113eSDavid van Moolenbroek 			last_size = 1;
1628*83ee113eSDavid van Moolenbroek 			break;
1629*83ee113eSDavid van Moolenbroek 
1630*83ee113eSDavid van Moolenbroek 		    case 'o': /* Last argument is optional. */
1631*83ee113eSDavid van Moolenbroek 			min_len -= last_size;
1632*83ee113eSDavid van Moolenbroek 
1633*83ee113eSDavid van Moolenbroek 		    /* XXX: It MAY be possible to sense the end of an
1634*83ee113eSDavid van Moolenbroek 		     * encapsulated space, but right now this is too
1635*83ee113eSDavid van Moolenbroek 		     * hard to support.  Return a safe value.
1636*83ee113eSDavid van Moolenbroek 		     */
1637*83ee113eSDavid van Moolenbroek 		    case 'e': /* Encapsulation hint (there is an 'E' later). */
1638*83ee113eSDavid van Moolenbroek 		    case 'E': /* Encapsulated options. */
1639*83ee113eSDavid van Moolenbroek 			return min_len;
1640*83ee113eSDavid van Moolenbroek 
1641*83ee113eSDavid van Moolenbroek 		    case 'd': /* "Domain name" */
1642*83ee113eSDavid van Moolenbroek 		    case 'D': /* "rfc1035 formatted names" */
1643*83ee113eSDavid van Moolenbroek 		    case 't': /* "ASCII Text" */
1644*83ee113eSDavid van Moolenbroek 		    case 'X': /* "ASCII or Hex Conditional */
1645*83ee113eSDavid van Moolenbroek 		    case 'x': /* "Hex" */
1646*83ee113eSDavid van Moolenbroek 		    case 'A': /* Array of all that precedes. */
1647*83ee113eSDavid van Moolenbroek 		    case 'a': /* Array of preceding symbol. */
1648*83ee113eSDavid van Moolenbroek 		    case 'Z': /* nothing. */
1649*83ee113eSDavid van Moolenbroek 			return min_len;
1650*83ee113eSDavid van Moolenbroek 
1651*83ee113eSDavid van Moolenbroek 		    case 'c': /* Compress flag for D atom. */
1652*83ee113eSDavid van Moolenbroek 			log_error("format_min_length(%s): 'c' atom is illegal "
1653*83ee113eSDavid van Moolenbroek 				  "except after 'D' atom.", format);
1654*83ee113eSDavid van Moolenbroek 			return INT_MAX;
1655*83ee113eSDavid van Moolenbroek 
1656*83ee113eSDavid van Moolenbroek 		    default:
1657*83ee113eSDavid van Moolenbroek 			/* No safe value is known. */
1658*83ee113eSDavid van Moolenbroek 			log_error("format_min_length(%s): No safe value "
1659*83ee113eSDavid van Moolenbroek 				  "for unknown format symbols.", format);
1660*83ee113eSDavid van Moolenbroek 			return INT_MAX;
1661*83ee113eSDavid van Moolenbroek 		}
1662*83ee113eSDavid van Moolenbroek 	}
1663*83ee113eSDavid van Moolenbroek 
1664*83ee113eSDavid van Moolenbroek 	return min_len;
1665*83ee113eSDavid van Moolenbroek }
1666*83ee113eSDavid van Moolenbroek 
1667*83ee113eSDavid van Moolenbroek 
1668*83ee113eSDavid van Moolenbroek /* Format the specified option so that a human can easily read it. */
1669*83ee113eSDavid van Moolenbroek 
pretty_print_option(option,data,len,emit_commas,emit_quotes)1670*83ee113eSDavid van Moolenbroek const char *pretty_print_option (option, data, len, emit_commas, emit_quotes)
1671*83ee113eSDavid van Moolenbroek 	struct option *option;
1672*83ee113eSDavid van Moolenbroek 	const unsigned char *data;
1673*83ee113eSDavid van Moolenbroek 	unsigned len;
1674*83ee113eSDavid van Moolenbroek 	int emit_commas;
1675*83ee113eSDavid van Moolenbroek 	int emit_quotes;
1676*83ee113eSDavid van Moolenbroek {
1677*83ee113eSDavid van Moolenbroek 	static char optbuf [32768]; /* XXX */
1678*83ee113eSDavid van Moolenbroek 	static char *endbuf = &optbuf[sizeof(optbuf)];
1679*83ee113eSDavid van Moolenbroek 	int hunksize = 0;
1680*83ee113eSDavid van Moolenbroek 	int opthunk = 0;
1681*83ee113eSDavid van Moolenbroek 	int hunkinc = 0;
1682*83ee113eSDavid van Moolenbroek 	int numhunk = -1;
1683*83ee113eSDavid van Moolenbroek 	int numelem = 0;
1684*83ee113eSDavid van Moolenbroek 	int count;
1685*83ee113eSDavid van Moolenbroek 	int i, j, k, l;
1686*83ee113eSDavid van Moolenbroek 	char fmtbuf[32] = "";
1687*83ee113eSDavid van Moolenbroek 	struct iaddr iaddr;
1688*83ee113eSDavid van Moolenbroek 	struct enumeration *enumbuf[32]; /* MUST be same as fmtbuf */
1689*83ee113eSDavid van Moolenbroek 	char *op = optbuf;
1690*83ee113eSDavid van Moolenbroek 	const unsigned char *dp = data;
1691*83ee113eSDavid van Moolenbroek 	char comma;
1692*83ee113eSDavid van Moolenbroek 	unsigned long tval;
1693*83ee113eSDavid van Moolenbroek 	isc_boolean_t a_array = ISC_FALSE;
1694*83ee113eSDavid van Moolenbroek 	int len_used;
1695*83ee113eSDavid van Moolenbroek 
1696*83ee113eSDavid van Moolenbroek 	if (emit_commas)
1697*83ee113eSDavid van Moolenbroek 		comma = ',';
1698*83ee113eSDavid van Moolenbroek 	else
1699*83ee113eSDavid van Moolenbroek 		comma = ' ';
1700*83ee113eSDavid van Moolenbroek 
1701*83ee113eSDavid van Moolenbroek 	memset (enumbuf, 0, sizeof enumbuf);
1702*83ee113eSDavid van Moolenbroek 
1703*83ee113eSDavid van Moolenbroek 	/* Figure out the size of the data. */
1704*83ee113eSDavid van Moolenbroek 	for (l = i = 0; option -> format [i]; i++, l++) {
1705*83ee113eSDavid van Moolenbroek 		if (l >= sizeof(fmtbuf) - 1)
1706*83ee113eSDavid van Moolenbroek 			log_fatal("Bounds failure on internal buffer at "
1707*83ee113eSDavid van Moolenbroek 				  "%s:%d", MDL);
1708*83ee113eSDavid van Moolenbroek 
1709*83ee113eSDavid van Moolenbroek 		if (!numhunk) {
1710*83ee113eSDavid van Moolenbroek 			log_error ("%s: Extra codes in format string: %s",
1711*83ee113eSDavid van Moolenbroek 				   option -> name,
1712*83ee113eSDavid van Moolenbroek 				   &(option -> format [i]));
1713*83ee113eSDavid van Moolenbroek 			break;
1714*83ee113eSDavid van Moolenbroek 		}
1715*83ee113eSDavid van Moolenbroek 		numelem++;
1716*83ee113eSDavid van Moolenbroek 		fmtbuf [l] = option -> format [i];
1717*83ee113eSDavid van Moolenbroek 		switch (option -> format [i]) {
1718*83ee113eSDavid van Moolenbroek 		      case 'a':
1719*83ee113eSDavid van Moolenbroek 			a_array = ISC_TRUE;
1720*83ee113eSDavid van Moolenbroek 			/* Fall through */
1721*83ee113eSDavid van Moolenbroek 		      case 'A':
1722*83ee113eSDavid van Moolenbroek 			--numelem;
1723*83ee113eSDavid van Moolenbroek 			fmtbuf [l] = 0;
1724*83ee113eSDavid van Moolenbroek 			numhunk = 0;
1725*83ee113eSDavid van Moolenbroek 			break;
1726*83ee113eSDavid van Moolenbroek 		      case 'E':
1727*83ee113eSDavid van Moolenbroek 			/* Skip the universe name. */
1728*83ee113eSDavid van Moolenbroek 			while (option -> format [i] &&
1729*83ee113eSDavid van Moolenbroek 			       option -> format [i] != '.')
1730*83ee113eSDavid van Moolenbroek 				i++;
1731*83ee113eSDavid van Moolenbroek 			/* Fall Through! */
1732*83ee113eSDavid van Moolenbroek 		      case 'X':
1733*83ee113eSDavid van Moolenbroek 			for (k = 0; k < len; k++) {
1734*83ee113eSDavid van Moolenbroek 				if (!isascii (data [k]) ||
1735*83ee113eSDavid van Moolenbroek 				    !isprint (data [k]))
1736*83ee113eSDavid van Moolenbroek 					break;
1737*83ee113eSDavid van Moolenbroek 			}
1738*83ee113eSDavid van Moolenbroek 			/* If we found no bogus characters, or the bogus
1739*83ee113eSDavid van Moolenbroek 			   character we found is a trailing NUL, it's
1740*83ee113eSDavid van Moolenbroek 			   okay to print this option as text. */
1741*83ee113eSDavid van Moolenbroek 			if (k == len || (k + 1 == len && data [k] == 0)) {
1742*83ee113eSDavid van Moolenbroek 				fmtbuf [l] = 't';
1743*83ee113eSDavid van Moolenbroek 				numhunk = -2;
1744*83ee113eSDavid van Moolenbroek 			} else {
1745*83ee113eSDavid van Moolenbroek 				fmtbuf [l] = 'x';
1746*83ee113eSDavid van Moolenbroek 				hunksize++;
1747*83ee113eSDavid van Moolenbroek 				comma = ':';
1748*83ee113eSDavid van Moolenbroek 				numhunk = 0;
1749*83ee113eSDavid van Moolenbroek 				a_array = ISC_TRUE;
1750*83ee113eSDavid van Moolenbroek 				hunkinc = 1;
1751*83ee113eSDavid van Moolenbroek 			}
1752*83ee113eSDavid van Moolenbroek 			fmtbuf [l + 1] = 0;
1753*83ee113eSDavid van Moolenbroek 			break;
1754*83ee113eSDavid van Moolenbroek 		      case 'c':
1755*83ee113eSDavid van Moolenbroek 			/* The 'c' atom is a 'D' modifier only. */
1756*83ee113eSDavid van Moolenbroek 			log_error("'c' atom not following D atom in format "
1757*83ee113eSDavid van Moolenbroek 				  "string: %s", option->format);
1758*83ee113eSDavid van Moolenbroek 			break;
1759*83ee113eSDavid van Moolenbroek 		      case 'D':
1760*83ee113eSDavid van Moolenbroek 			/*
1761*83ee113eSDavid van Moolenbroek 			 * Skip the 'c' atom, if present.  It does not affect
1762*83ee113eSDavid van Moolenbroek 			 * how we convert wire->text format (if compression is
1763*83ee113eSDavid van Moolenbroek 			 * present either way, we still process it).
1764*83ee113eSDavid van Moolenbroek 			 */
1765*83ee113eSDavid van Moolenbroek 			if (option->format[i+1] == 'c')
1766*83ee113eSDavid van Moolenbroek 				i++;
1767*83ee113eSDavid van Moolenbroek 			fmtbuf[l + 1] = 0;
1768*83ee113eSDavid van Moolenbroek 			numhunk = -2;
1769*83ee113eSDavid van Moolenbroek 			break;
1770*83ee113eSDavid van Moolenbroek 		      case 'd':
1771*83ee113eSDavid van Moolenbroek 			fmtbuf[l] = 't';
1772*83ee113eSDavid van Moolenbroek 			/* Fall Through ! */
1773*83ee113eSDavid van Moolenbroek 		      case 't':
1774*83ee113eSDavid van Moolenbroek 			fmtbuf[l + 1] = 0;
1775*83ee113eSDavid van Moolenbroek 			numhunk = -2;
1776*83ee113eSDavid van Moolenbroek 			break;
1777*83ee113eSDavid van Moolenbroek 		      case 'N':
1778*83ee113eSDavid van Moolenbroek 			k = i;
1779*83ee113eSDavid van Moolenbroek 			while (option -> format [i] &&
1780*83ee113eSDavid van Moolenbroek 			       option -> format [i] != '.')
1781*83ee113eSDavid van Moolenbroek 				i++;
1782*83ee113eSDavid van Moolenbroek 			enumbuf [l] =
1783*83ee113eSDavid van Moolenbroek 				find_enumeration (&option -> format [k] + 1,
1784*83ee113eSDavid van Moolenbroek 						  i - k - 1);
1785*83ee113eSDavid van Moolenbroek 			if (enumbuf[l] == NULL) {
1786*83ee113eSDavid van Moolenbroek 				hunksize += 1;
1787*83ee113eSDavid van Moolenbroek 				hunkinc = 1;
1788*83ee113eSDavid van Moolenbroek 			} else {
1789*83ee113eSDavid van Moolenbroek 				hunksize += enumbuf[l]->width;
1790*83ee113eSDavid van Moolenbroek 				hunkinc = enumbuf[l]->width;
1791*83ee113eSDavid van Moolenbroek 			}
1792*83ee113eSDavid van Moolenbroek 			break;
1793*83ee113eSDavid van Moolenbroek 		      case '6':
1794*83ee113eSDavid van Moolenbroek 			hunksize += 16;
1795*83ee113eSDavid van Moolenbroek 			hunkinc = 16;
1796*83ee113eSDavid van Moolenbroek 			break;
1797*83ee113eSDavid van Moolenbroek 		      case 'I':
1798*83ee113eSDavid van Moolenbroek 		      case 'l':
1799*83ee113eSDavid van Moolenbroek 		      case 'L':
1800*83ee113eSDavid van Moolenbroek 		      case 'T':
1801*83ee113eSDavid van Moolenbroek 			hunksize += 4;
1802*83ee113eSDavid van Moolenbroek 			hunkinc = 4;
1803*83ee113eSDavid van Moolenbroek 			break;
1804*83ee113eSDavid van Moolenbroek 		      case 's':
1805*83ee113eSDavid van Moolenbroek 		      case 'S':
1806*83ee113eSDavid van Moolenbroek 			hunksize += 2;
1807*83ee113eSDavid van Moolenbroek 			hunkinc = 2;
1808*83ee113eSDavid van Moolenbroek 			break;
1809*83ee113eSDavid van Moolenbroek 		      case 'b':
1810*83ee113eSDavid van Moolenbroek 		      case 'B':
1811*83ee113eSDavid van Moolenbroek 		      case 'f':
1812*83ee113eSDavid van Moolenbroek 		      case 'F':
1813*83ee113eSDavid van Moolenbroek 			hunksize++;
1814*83ee113eSDavid van Moolenbroek 			hunkinc = 1;
1815*83ee113eSDavid van Moolenbroek 			break;
1816*83ee113eSDavid van Moolenbroek 		      case 'e':
1817*83ee113eSDavid van Moolenbroek 		      case 'Z':
1818*83ee113eSDavid van Moolenbroek 			break;
1819*83ee113eSDavid van Moolenbroek 		      case 'o':
1820*83ee113eSDavid van Moolenbroek 			opthunk += hunkinc;
1821*83ee113eSDavid van Moolenbroek 			break;
1822*83ee113eSDavid van Moolenbroek 		      default:
1823*83ee113eSDavid van Moolenbroek 			log_error ("%s: garbage in format string: %s",
1824*83ee113eSDavid van Moolenbroek 			      option -> name,
1825*83ee113eSDavid van Moolenbroek 			      &(option -> format [i]));
1826*83ee113eSDavid van Moolenbroek 			break;
1827*83ee113eSDavid van Moolenbroek 		}
1828*83ee113eSDavid van Moolenbroek 	}
1829*83ee113eSDavid van Moolenbroek 
1830*83ee113eSDavid van Moolenbroek 	/* Check for too few bytes... */
1831*83ee113eSDavid van Moolenbroek 	if (hunksize - opthunk > len) {
1832*83ee113eSDavid van Moolenbroek 		log_error ("%s: expecting at least %d bytes; got %d",
1833*83ee113eSDavid van Moolenbroek 		      option -> name,
1834*83ee113eSDavid van Moolenbroek 		      hunksize, len);
1835*83ee113eSDavid van Moolenbroek 		return "<error>";
1836*83ee113eSDavid van Moolenbroek 	}
1837*83ee113eSDavid van Moolenbroek 	/* Check for too many bytes... */
1838*83ee113eSDavid van Moolenbroek 	if (numhunk == -1 && hunksize < len)
1839*83ee113eSDavid van Moolenbroek 		log_error ("%s: %d extra bytes",
1840*83ee113eSDavid van Moolenbroek 		      option -> name,
1841*83ee113eSDavid van Moolenbroek 		      len - hunksize);
1842*83ee113eSDavid van Moolenbroek 
1843*83ee113eSDavid van Moolenbroek 	/* If this is an array, compute its size. */
1844*83ee113eSDavid van Moolenbroek 	if (numhunk == 0) {
1845*83ee113eSDavid van Moolenbroek 		if (a_array == ISC_TRUE) {
1846*83ee113eSDavid van Moolenbroek 			/*
1847*83ee113eSDavid van Moolenbroek 			 * It is an 'a' type array - we repeat the
1848*83ee113eSDavid van Moolenbroek 			 * last format type.  A binary string for 'X'
1849*83ee113eSDavid van Moolenbroek 			 * is also like this.  hunkinc is the size
1850*83ee113eSDavid van Moolenbroek 			 * of the last format type and we add 1 to
1851*83ee113eSDavid van Moolenbroek 			 * cover the entire first record.
1852*83ee113eSDavid van Moolenbroek 			 */
1853*83ee113eSDavid van Moolenbroek 			numhunk = ((len - hunksize) / hunkinc) + 1;
1854*83ee113eSDavid van Moolenbroek 			len_used = hunksize + ((numhunk - 1) * hunkinc);
1855*83ee113eSDavid van Moolenbroek 		} else {
1856*83ee113eSDavid van Moolenbroek 			/*
1857*83ee113eSDavid van Moolenbroek 			 * It is an 'A' type array - we repeat the
1858*83ee113eSDavid van Moolenbroek 			 * entire record
1859*83ee113eSDavid van Moolenbroek 			 */
1860*83ee113eSDavid van Moolenbroek 			numhunk = len / hunksize;
1861*83ee113eSDavid van Moolenbroek 			len_used = numhunk * hunksize;
1862*83ee113eSDavid van Moolenbroek 		}
1863*83ee113eSDavid van Moolenbroek 
1864*83ee113eSDavid van Moolenbroek 		/* See if we got an exact number of hunks. */
1865*83ee113eSDavid van Moolenbroek 		if (len_used < len) {
1866*83ee113eSDavid van Moolenbroek 			log_error ("%s: %d extra bytes at end of array\n",
1867*83ee113eSDavid van Moolenbroek 				   option -> name,
1868*83ee113eSDavid van Moolenbroek 				   len - len_used);
1869*83ee113eSDavid van Moolenbroek 		}
1870*83ee113eSDavid van Moolenbroek 	}
1871*83ee113eSDavid van Moolenbroek 
1872*83ee113eSDavid van Moolenbroek 
1873*83ee113eSDavid van Moolenbroek 	/* A one-hunk array prints the same as a single hunk. */
1874*83ee113eSDavid van Moolenbroek 	if (numhunk < 0)
1875*83ee113eSDavid van Moolenbroek 		numhunk = 1;
1876*83ee113eSDavid van Moolenbroek 
1877*83ee113eSDavid van Moolenbroek 	/* Cycle through the array (or hunk) printing the data. */
1878*83ee113eSDavid van Moolenbroek 	for (i = 0; i < numhunk; i++) {
1879*83ee113eSDavid van Moolenbroek 		if ((a_array == ISC_TRUE) && (i != 0) && (numelem > 0)) {
1880*83ee113eSDavid van Moolenbroek 			/*
1881*83ee113eSDavid van Moolenbroek 			 * For 'a' type of arrays we repeat
1882*83ee113eSDavid van Moolenbroek 			 * only the last format character
1883*83ee113eSDavid van Moolenbroek 			 * We should never hit the case of numelem == 0
1884*83ee113eSDavid van Moolenbroek 			 * but let's include the check to be safe.
1885*83ee113eSDavid van Moolenbroek 			 */
1886*83ee113eSDavid van Moolenbroek 			j = numelem - 1;
1887*83ee113eSDavid van Moolenbroek 		} else {
1888*83ee113eSDavid van Moolenbroek 			/*
1889*83ee113eSDavid van Moolenbroek 			 * for other types of arrays or the first
1890*83ee113eSDavid van Moolenbroek 			 * time through for 'a' types, we go through
1891*83ee113eSDavid van Moolenbroek 			 * the entire set of format characters.
1892*83ee113eSDavid van Moolenbroek 			 */
1893*83ee113eSDavid van Moolenbroek 			j = 0;
1894*83ee113eSDavid van Moolenbroek 		}
1895*83ee113eSDavid van Moolenbroek 
1896*83ee113eSDavid van Moolenbroek 		for (; j < numelem; j++) {
1897*83ee113eSDavid van Moolenbroek 			switch (fmtbuf [j]) {
1898*83ee113eSDavid van Moolenbroek 			      case 't':
1899*83ee113eSDavid van Moolenbroek 				/* endbuf-1 leaves room for NULL. */
1900*83ee113eSDavid van Moolenbroek 				k = pretty_text(&op, endbuf - 1, &dp,
1901*83ee113eSDavid van Moolenbroek 						data + len, emit_quotes);
1902*83ee113eSDavid van Moolenbroek 				if (k == -1) {
1903*83ee113eSDavid van Moolenbroek 					log_error("Error printing text.");
1904*83ee113eSDavid van Moolenbroek 					break;
1905*83ee113eSDavid van Moolenbroek 				}
1906*83ee113eSDavid van Moolenbroek 				*op = 0;
1907*83ee113eSDavid van Moolenbroek 				break;
1908*83ee113eSDavid van Moolenbroek 			      case 'D': /* RFC1035 format name list */
1909*83ee113eSDavid van Moolenbroek 				for( ; dp < (data + len) ; dp += k) {
1910*83ee113eSDavid van Moolenbroek 					unsigned char nbuff[NS_MAXCDNAME];
1911*83ee113eSDavid van Moolenbroek 					const unsigned char *nbp, *nend;
1912*83ee113eSDavid van Moolenbroek 
1913*83ee113eSDavid van Moolenbroek 					nend = &nbuff[sizeof(nbuff)];
1914*83ee113eSDavid van Moolenbroek 
1915*83ee113eSDavid van Moolenbroek 					/* If this is for ISC DHCP consumption
1916*83ee113eSDavid van Moolenbroek 					 * (emit_quotes), lay it out as a list
1917*83ee113eSDavid van Moolenbroek 					 * of STRING tokens.  Otherwise, it is
1918*83ee113eSDavid van Moolenbroek 					 * a space-separated list of DNS-
1919*83ee113eSDavid van Moolenbroek 					 * escaped names as /etc/resolv.conf
1920*83ee113eSDavid van Moolenbroek 					 * might digest.
1921*83ee113eSDavid van Moolenbroek 					 */
1922*83ee113eSDavid van Moolenbroek 					if (dp != data) {
1923*83ee113eSDavid van Moolenbroek 						if (op + 2 > endbuf)
1924*83ee113eSDavid van Moolenbroek 							break;
1925*83ee113eSDavid van Moolenbroek 
1926*83ee113eSDavid van Moolenbroek 						if (emit_quotes)
1927*83ee113eSDavid van Moolenbroek 							*op++ = ',';
1928*83ee113eSDavid van Moolenbroek 						*op++ = ' ';
1929*83ee113eSDavid van Moolenbroek 					}
1930*83ee113eSDavid van Moolenbroek 
1931*83ee113eSDavid van Moolenbroek 					/* XXX: if fmtbuf[j+1] != 'c', we
1932*83ee113eSDavid van Moolenbroek 					 * should warn if the data was
1933*83ee113eSDavid van Moolenbroek 					 * compressed anyway.
1934*83ee113eSDavid van Moolenbroek 					 */
1935*83ee113eSDavid van Moolenbroek 					k = MRns_name_unpack(data,
1936*83ee113eSDavid van Moolenbroek 							     data + len,
1937*83ee113eSDavid van Moolenbroek 							     dp, nbuff,
1938*83ee113eSDavid van Moolenbroek 							     sizeof(nbuff));
1939*83ee113eSDavid van Moolenbroek 
1940*83ee113eSDavid van Moolenbroek 					if (k == -1) {
1941*83ee113eSDavid van Moolenbroek 						log_error("Invalid domain "
1942*83ee113eSDavid van Moolenbroek 							  "list.");
1943*83ee113eSDavid van Moolenbroek 						break;
1944*83ee113eSDavid van Moolenbroek 					}
1945*83ee113eSDavid van Moolenbroek 
1946*83ee113eSDavid van Moolenbroek 					/* If emit_quotes, then use ISC DHCP
1947*83ee113eSDavid van Moolenbroek 					 * escapes.  Otherwise, rely only on
1948*83ee113eSDavid van Moolenbroek 					 * ns_name_ntop().
1949*83ee113eSDavid van Moolenbroek 					 */
1950*83ee113eSDavid van Moolenbroek 					if (emit_quotes) {
1951*83ee113eSDavid van Moolenbroek 						nbp = nbuff;
1952*83ee113eSDavid van Moolenbroek 						pretty_domain(&op, endbuf-1,
1953*83ee113eSDavid van Moolenbroek 							      &nbp, nend);
1954*83ee113eSDavid van Moolenbroek 					} else {
1955*83ee113eSDavid van Moolenbroek 						/* ns_name_ntop() includes
1956*83ee113eSDavid van Moolenbroek 						 * a trailing NUL in its
1957*83ee113eSDavid van Moolenbroek 						 * count.
1958*83ee113eSDavid van Moolenbroek 						 */
1959*83ee113eSDavid van Moolenbroek 						count = MRns_name_ntop(
1960*83ee113eSDavid van Moolenbroek 								nbuff, op,
1961*83ee113eSDavid van Moolenbroek 								(endbuf-op)-1);
1962*83ee113eSDavid van Moolenbroek 
1963*83ee113eSDavid van Moolenbroek 						if (count <= 0) {
1964*83ee113eSDavid van Moolenbroek 							log_error("Invalid "
1965*83ee113eSDavid van Moolenbroek 								"domain name.");
1966*83ee113eSDavid van Moolenbroek 							break;
1967*83ee113eSDavid van Moolenbroek 						}
1968*83ee113eSDavid van Moolenbroek 
1969*83ee113eSDavid van Moolenbroek 						/* Consume all but the trailing
1970*83ee113eSDavid van Moolenbroek 						 * NUL.
1971*83ee113eSDavid van Moolenbroek 						 */
1972*83ee113eSDavid van Moolenbroek 						op += count - 1;
1973*83ee113eSDavid van Moolenbroek 
1974*83ee113eSDavid van Moolenbroek 						/* Replace the trailing NUL
1975*83ee113eSDavid van Moolenbroek 						 * with the implicit root
1976*83ee113eSDavid van Moolenbroek 						 * (in the unlikely event the
1977*83ee113eSDavid van Moolenbroek 						 * domain name /is/ the root).
1978*83ee113eSDavid van Moolenbroek 						 */
1979*83ee113eSDavid van Moolenbroek 						*op++ = '.';
1980*83ee113eSDavid van Moolenbroek 					}
1981*83ee113eSDavid van Moolenbroek 				}
1982*83ee113eSDavid van Moolenbroek 				*op = '\0';
1983*83ee113eSDavid van Moolenbroek 				break;
1984*83ee113eSDavid van Moolenbroek 				/* pretty-printing an array of enums is
1985*83ee113eSDavid van Moolenbroek 				   going to get ugly. */
1986*83ee113eSDavid van Moolenbroek 			      case 'N':
1987*83ee113eSDavid van Moolenbroek 				if (!enumbuf [j]) {
1988*83ee113eSDavid van Moolenbroek 					tval = *dp++;
1989*83ee113eSDavid van Moolenbroek 					goto enum_as_num;
1990*83ee113eSDavid van Moolenbroek 				}
1991*83ee113eSDavid van Moolenbroek 
1992*83ee113eSDavid van Moolenbroek 				switch (enumbuf[j]->width) {
1993*83ee113eSDavid van Moolenbroek 				      case 1:
1994*83ee113eSDavid van Moolenbroek 					tval = getUChar(dp);
1995*83ee113eSDavid van Moolenbroek 					break;
1996*83ee113eSDavid van Moolenbroek 
1997*83ee113eSDavid van Moolenbroek 				     case 2:
1998*83ee113eSDavid van Moolenbroek 					tval = getUShort(dp);
1999*83ee113eSDavid van Moolenbroek 					break;
2000*83ee113eSDavid van Moolenbroek 
2001*83ee113eSDavid van Moolenbroek 				    case 4:
2002*83ee113eSDavid van Moolenbroek 					tval = getULong(dp);
2003*83ee113eSDavid van Moolenbroek 					break;
2004*83ee113eSDavid van Moolenbroek 
2005*83ee113eSDavid van Moolenbroek 				    default:
2006*83ee113eSDavid van Moolenbroek 					log_fatal("Impossible case at %s:%d.",
2007*83ee113eSDavid van Moolenbroek 						  MDL);
2008*83ee113eSDavid van Moolenbroek 					return "<double impossible condition>";
2009*83ee113eSDavid van Moolenbroek 				}
2010*83ee113eSDavid van Moolenbroek 
2011*83ee113eSDavid van Moolenbroek 				for (i = 0; ;i++) {
2012*83ee113eSDavid van Moolenbroek 					if (!enumbuf [j] -> values [i].name)
2013*83ee113eSDavid van Moolenbroek 						goto enum_as_num;
2014*83ee113eSDavid van Moolenbroek 					if (enumbuf [j] -> values [i].value ==
2015*83ee113eSDavid van Moolenbroek 					    tval)
2016*83ee113eSDavid van Moolenbroek 						break;
2017*83ee113eSDavid van Moolenbroek 				}
2018*83ee113eSDavid van Moolenbroek 				strcpy (op, enumbuf [j] -> values [i].name);
2019*83ee113eSDavid van Moolenbroek 				dp += enumbuf[j]->width;
2020*83ee113eSDavid van Moolenbroek 				break;
2021*83ee113eSDavid van Moolenbroek 
2022*83ee113eSDavid van Moolenbroek 			      enum_as_num:
2023*83ee113eSDavid van Moolenbroek 				sprintf(op, "%lu", tval);
2024*83ee113eSDavid van Moolenbroek 				break;
2025*83ee113eSDavid van Moolenbroek 
2026*83ee113eSDavid van Moolenbroek 			      case 'I':
2027*83ee113eSDavid van Moolenbroek 				iaddr.len = 4;
2028*83ee113eSDavid van Moolenbroek 				memcpy(iaddr.iabuf, dp, 4);
2029*83ee113eSDavid van Moolenbroek 				strcpy(op, piaddr(iaddr));
2030*83ee113eSDavid van Moolenbroek 				dp += 4;
2031*83ee113eSDavid van Moolenbroek 				break;
2032*83ee113eSDavid van Moolenbroek 			      case '6':
2033*83ee113eSDavid van Moolenbroek 				iaddr.len = 16;
2034*83ee113eSDavid van Moolenbroek 				memcpy(iaddr.iabuf, dp, 16);
2035*83ee113eSDavid van Moolenbroek 				strcpy(op, piaddr(iaddr));
2036*83ee113eSDavid van Moolenbroek 				dp += 16;
2037*83ee113eSDavid van Moolenbroek 				break;
2038*83ee113eSDavid van Moolenbroek 			      case 'l':
2039*83ee113eSDavid van Moolenbroek 				sprintf (op, "%ld", (long)getLong (dp));
2040*83ee113eSDavid van Moolenbroek 				dp += 4;
2041*83ee113eSDavid van Moolenbroek 				break;
2042*83ee113eSDavid van Moolenbroek 			      case 'T':
2043*83ee113eSDavid van Moolenbroek 				tval = getULong (dp);
2044*83ee113eSDavid van Moolenbroek 				if (tval == -1)
2045*83ee113eSDavid van Moolenbroek 					sprintf (op, "%s", "infinite");
2046*83ee113eSDavid van Moolenbroek 				else
2047*83ee113eSDavid van Moolenbroek 					sprintf(op, "%lu", tval);
2048*83ee113eSDavid van Moolenbroek 				break;
2049*83ee113eSDavid van Moolenbroek 			      case 'L':
2050*83ee113eSDavid van Moolenbroek 				sprintf(op, "%lu",
2051*83ee113eSDavid van Moolenbroek 					(unsigned long)getULong(dp));
2052*83ee113eSDavid van Moolenbroek 				dp += 4;
2053*83ee113eSDavid van Moolenbroek 				break;
2054*83ee113eSDavid van Moolenbroek 			      case 's':
2055*83ee113eSDavid van Moolenbroek 				sprintf (op, "%d", (int)getShort (dp));
2056*83ee113eSDavid van Moolenbroek 				dp += 2;
2057*83ee113eSDavid van Moolenbroek 				break;
2058*83ee113eSDavid van Moolenbroek 			      case 'S':
2059*83ee113eSDavid van Moolenbroek 				sprintf(op, "%u", (unsigned)getUShort(dp));
2060*83ee113eSDavid van Moolenbroek 				dp += 2;
2061*83ee113eSDavid van Moolenbroek 				break;
2062*83ee113eSDavid van Moolenbroek 			      case 'b':
2063*83ee113eSDavid van Moolenbroek 				sprintf (op, "%d", *(const char *)dp++);
2064*83ee113eSDavid van Moolenbroek 				break;
2065*83ee113eSDavid van Moolenbroek 			      case 'B':
2066*83ee113eSDavid van Moolenbroek 				sprintf (op, "%d", *dp++);
2067*83ee113eSDavid van Moolenbroek 				break;
2068*83ee113eSDavid van Moolenbroek 			      case 'X':
2069*83ee113eSDavid van Moolenbroek 			      case 'x':
2070*83ee113eSDavid van Moolenbroek 				sprintf (op, "%x", *dp++);
2071*83ee113eSDavid van Moolenbroek 				break;
2072*83ee113eSDavid van Moolenbroek 			      case 'f':
2073*83ee113eSDavid van Moolenbroek 				strcpy (op, *dp++ ? "true" : "false");
2074*83ee113eSDavid van Moolenbroek 				break;
2075*83ee113eSDavid van Moolenbroek 			      case 'F':
2076*83ee113eSDavid van Moolenbroek 				strcpy (op, "true");
2077*83ee113eSDavid van Moolenbroek 				break;
2078*83ee113eSDavid van Moolenbroek 			      case 'e':
2079*83ee113eSDavid van Moolenbroek 			      case 'Z':
2080*83ee113eSDavid van Moolenbroek 				*op = '\0';
2081*83ee113eSDavid van Moolenbroek 				break;
2082*83ee113eSDavid van Moolenbroek 			      default:
2083*83ee113eSDavid van Moolenbroek 				log_error ("Unexpected format code %c",
2084*83ee113eSDavid van Moolenbroek 					   fmtbuf [j]);
2085*83ee113eSDavid van Moolenbroek 			}
2086*83ee113eSDavid van Moolenbroek 			op += strlen (op);
2087*83ee113eSDavid van Moolenbroek 			if (dp == data + len)
2088*83ee113eSDavid van Moolenbroek 				break;
2089*83ee113eSDavid van Moolenbroek 			if (j + 1 < numelem && comma != ':')
2090*83ee113eSDavid van Moolenbroek 				*op++ = ' ';
2091*83ee113eSDavid van Moolenbroek 		}
2092*83ee113eSDavid van Moolenbroek 		if (i + 1 < numhunk) {
2093*83ee113eSDavid van Moolenbroek 			*op++ = comma;
2094*83ee113eSDavid van Moolenbroek 		}
2095*83ee113eSDavid van Moolenbroek 		if (dp == data + len)
2096*83ee113eSDavid van Moolenbroek 			break;
2097*83ee113eSDavid van Moolenbroek 	}
2098*83ee113eSDavid van Moolenbroek 	return optbuf;
2099*83ee113eSDavid van Moolenbroek }
2100*83ee113eSDavid van Moolenbroek 
get_option(result,universe,packet,lease,client_state,in_options,cfg_options,options,scope,code,file,line)2101*83ee113eSDavid van Moolenbroek int get_option (result, universe, packet, lease, client_state,
2102*83ee113eSDavid van Moolenbroek 		in_options, cfg_options, options, scope, code, file, line)
2103*83ee113eSDavid van Moolenbroek 	struct data_string *result;
2104*83ee113eSDavid van Moolenbroek 	struct universe *universe;
2105*83ee113eSDavid van Moolenbroek 	struct packet *packet;
2106*83ee113eSDavid van Moolenbroek 	struct lease *lease;
2107*83ee113eSDavid van Moolenbroek 	struct client_state *client_state;
2108*83ee113eSDavid van Moolenbroek 	struct option_state *in_options;
2109*83ee113eSDavid van Moolenbroek 	struct option_state *cfg_options;
2110*83ee113eSDavid van Moolenbroek 	struct option_state *options;
2111*83ee113eSDavid van Moolenbroek 	struct binding_scope **scope;
2112*83ee113eSDavid van Moolenbroek 	unsigned code;
2113*83ee113eSDavid van Moolenbroek 	const char *file;
2114*83ee113eSDavid van Moolenbroek 	int line;
2115*83ee113eSDavid van Moolenbroek {
2116*83ee113eSDavid van Moolenbroek 	struct option_cache *oc;
2117*83ee113eSDavid van Moolenbroek 
2118*83ee113eSDavid van Moolenbroek 	if (!universe -> lookup_func)
2119*83ee113eSDavid van Moolenbroek 		return 0;
2120*83ee113eSDavid van Moolenbroek 	oc = ((*universe -> lookup_func) (universe, options, code));
2121*83ee113eSDavid van Moolenbroek 	if (!oc)
2122*83ee113eSDavid van Moolenbroek 		return 0;
2123*83ee113eSDavid van Moolenbroek 	if (!evaluate_option_cache (result, packet, lease, client_state,
2124*83ee113eSDavid van Moolenbroek 				    in_options, cfg_options, scope, oc,
2125*83ee113eSDavid van Moolenbroek 				    file, line))
2126*83ee113eSDavid van Moolenbroek 		return 0;
2127*83ee113eSDavid van Moolenbroek 	return 1;
2128*83ee113eSDavid van Moolenbroek }
2129*83ee113eSDavid van Moolenbroek 
set_option(universe,options,option,op)2130*83ee113eSDavid van Moolenbroek void set_option (universe, options, option, op)
2131*83ee113eSDavid van Moolenbroek 	struct universe *universe;
2132*83ee113eSDavid van Moolenbroek 	struct option_state *options;
2133*83ee113eSDavid van Moolenbroek 	struct option_cache *option;
2134*83ee113eSDavid van Moolenbroek 	enum statement_op op;
2135*83ee113eSDavid van Moolenbroek {
2136*83ee113eSDavid van Moolenbroek 	struct option_cache *oc, *noc;
2137*83ee113eSDavid van Moolenbroek 
2138*83ee113eSDavid van Moolenbroek 	switch (op) {
2139*83ee113eSDavid van Moolenbroek 	      case if_statement:
2140*83ee113eSDavid van Moolenbroek 	      case add_statement:
2141*83ee113eSDavid van Moolenbroek 	      case eval_statement:
2142*83ee113eSDavid van Moolenbroek 	      case break_statement:
2143*83ee113eSDavid van Moolenbroek 	      default:
2144*83ee113eSDavid van Moolenbroek 		log_error ("bogus statement type in set_option.");
2145*83ee113eSDavid van Moolenbroek 		break;
2146*83ee113eSDavid van Moolenbroek 
2147*83ee113eSDavid van Moolenbroek 	      case default_option_statement:
2148*83ee113eSDavid van Moolenbroek 		oc = lookup_option (universe, options,
2149*83ee113eSDavid van Moolenbroek 				    option -> option -> code);
2150*83ee113eSDavid van Moolenbroek 		if (oc)
2151*83ee113eSDavid van Moolenbroek 			break;
2152*83ee113eSDavid van Moolenbroek 		save_option (universe, options, option);
2153*83ee113eSDavid van Moolenbroek 		break;
2154*83ee113eSDavid van Moolenbroek 
2155*83ee113eSDavid van Moolenbroek 	      case supersede_option_statement:
2156*83ee113eSDavid van Moolenbroek 	      case send_option_statement:
2157*83ee113eSDavid van Moolenbroek 		/* Install the option, replacing any existing version. */
2158*83ee113eSDavid van Moolenbroek 		save_option (universe, options, option);
2159*83ee113eSDavid van Moolenbroek 		break;
2160*83ee113eSDavid van Moolenbroek 
2161*83ee113eSDavid van Moolenbroek 	      case append_option_statement:
2162*83ee113eSDavid van Moolenbroek 	      case prepend_option_statement:
2163*83ee113eSDavid van Moolenbroek 		oc = lookup_option (universe, options,
2164*83ee113eSDavid van Moolenbroek 				    option -> option -> code);
2165*83ee113eSDavid van Moolenbroek 		if (!oc) {
2166*83ee113eSDavid van Moolenbroek 			save_option (universe, options, option);
2167*83ee113eSDavid van Moolenbroek 			break;
2168*83ee113eSDavid van Moolenbroek 		}
2169*83ee113eSDavid van Moolenbroek 		/* If it's not an expression, make it into one. */
2170*83ee113eSDavid van Moolenbroek 		if (!oc -> expression && oc -> data.len) {
2171*83ee113eSDavid van Moolenbroek 			if (!expression_allocate (&oc -> expression, MDL)) {
2172*83ee113eSDavid van Moolenbroek 				log_error ("Can't allocate const expression.");
2173*83ee113eSDavid van Moolenbroek 				break;
2174*83ee113eSDavid van Moolenbroek 			}
2175*83ee113eSDavid van Moolenbroek 			oc -> expression -> op = expr_const_data;
2176*83ee113eSDavid van Moolenbroek 			data_string_copy
2177*83ee113eSDavid van Moolenbroek 				(&oc -> expression -> data.const_data,
2178*83ee113eSDavid van Moolenbroek 				 &oc -> data, MDL);
2179*83ee113eSDavid van Moolenbroek 			data_string_forget (&oc -> data, MDL);
2180*83ee113eSDavid van Moolenbroek 		}
2181*83ee113eSDavid van Moolenbroek 		noc = (struct option_cache *)0;
2182*83ee113eSDavid van Moolenbroek 		if (!option_cache_allocate (&noc, MDL))
2183*83ee113eSDavid van Moolenbroek 			break;
2184*83ee113eSDavid van Moolenbroek 		if (op == append_option_statement) {
2185*83ee113eSDavid van Moolenbroek 			if (!make_concat (&noc -> expression,
2186*83ee113eSDavid van Moolenbroek 					  oc -> expression,
2187*83ee113eSDavid van Moolenbroek 					  option -> expression)) {
2188*83ee113eSDavid van Moolenbroek 				option_cache_dereference (&noc, MDL);
2189*83ee113eSDavid van Moolenbroek 				break;
2190*83ee113eSDavid van Moolenbroek 			}
2191*83ee113eSDavid van Moolenbroek 		} else {
2192*83ee113eSDavid van Moolenbroek 			if (!make_concat (&noc -> expression,
2193*83ee113eSDavid van Moolenbroek 					  option -> expression,
2194*83ee113eSDavid van Moolenbroek 					  oc -> expression)) {
2195*83ee113eSDavid van Moolenbroek 				option_cache_dereference (&noc, MDL);
2196*83ee113eSDavid van Moolenbroek 				break;
2197*83ee113eSDavid van Moolenbroek 			}
2198*83ee113eSDavid van Moolenbroek 		}
2199*83ee113eSDavid van Moolenbroek 		option_reference(&(noc->option), oc->option, MDL);
2200*83ee113eSDavid van Moolenbroek 		save_option (universe, options, noc);
2201*83ee113eSDavid van Moolenbroek 		option_cache_dereference (&noc, MDL);
2202*83ee113eSDavid van Moolenbroek 		break;
2203*83ee113eSDavid van Moolenbroek 	}
2204*83ee113eSDavid van Moolenbroek }
2205*83ee113eSDavid van Moolenbroek 
lookup_option(universe,options,code)2206*83ee113eSDavid van Moolenbroek struct option_cache *lookup_option (universe, options, code)
2207*83ee113eSDavid van Moolenbroek 	struct universe *universe;
2208*83ee113eSDavid van Moolenbroek 	struct option_state *options;
2209*83ee113eSDavid van Moolenbroek 	unsigned code;
2210*83ee113eSDavid van Moolenbroek {
2211*83ee113eSDavid van Moolenbroek 	if (!options)
2212*83ee113eSDavid van Moolenbroek 		return (struct option_cache *)0;
2213*83ee113eSDavid van Moolenbroek 	if (universe -> lookup_func)
2214*83ee113eSDavid van Moolenbroek 		return (*universe -> lookup_func) (universe, options, code);
2215*83ee113eSDavid van Moolenbroek 	else
2216*83ee113eSDavid van Moolenbroek 		log_error ("can't look up options in %s space.",
2217*83ee113eSDavid van Moolenbroek 			   universe -> name);
2218*83ee113eSDavid van Moolenbroek 	return (struct option_cache *)0;
2219*83ee113eSDavid van Moolenbroek }
2220*83ee113eSDavid van Moolenbroek 
lookup_hashed_option(universe,options,code)2221*83ee113eSDavid van Moolenbroek struct option_cache *lookup_hashed_option (universe, options, code)
2222*83ee113eSDavid van Moolenbroek 	struct universe *universe;
2223*83ee113eSDavid van Moolenbroek 	struct option_state *options;
2224*83ee113eSDavid van Moolenbroek 	unsigned code;
2225*83ee113eSDavid van Moolenbroek {
2226*83ee113eSDavid van Moolenbroek 	int hashix;
2227*83ee113eSDavid van Moolenbroek 	pair bptr;
2228*83ee113eSDavid van Moolenbroek 	pair *hash;
2229*83ee113eSDavid van Moolenbroek 
2230*83ee113eSDavid van Moolenbroek 	/* Make sure there's a hash table. */
2231*83ee113eSDavid van Moolenbroek 	if (universe -> index >= options -> universe_count ||
2232*83ee113eSDavid van Moolenbroek 	    !(options -> universes [universe -> index]))
2233*83ee113eSDavid van Moolenbroek 		return (struct option_cache *)0;
2234*83ee113eSDavid van Moolenbroek 
2235*83ee113eSDavid van Moolenbroek 	hash = options -> universes [universe -> index];
2236*83ee113eSDavid van Moolenbroek 
2237*83ee113eSDavid van Moolenbroek 	hashix = compute_option_hash (code);
2238*83ee113eSDavid van Moolenbroek 	for (bptr = hash [hashix]; bptr; bptr = bptr -> cdr) {
2239*83ee113eSDavid van Moolenbroek 		if (((struct option_cache *)(bptr -> car)) -> option -> code ==
2240*83ee113eSDavid van Moolenbroek 		    code)
2241*83ee113eSDavid van Moolenbroek 			return (struct option_cache *)(bptr -> car);
2242*83ee113eSDavid van Moolenbroek 	}
2243*83ee113eSDavid van Moolenbroek 	return (struct option_cache *)0;
2244*83ee113eSDavid van Moolenbroek }
2245*83ee113eSDavid van Moolenbroek 
2246*83ee113eSDavid van Moolenbroek /* Save a specified buffer into an option cache. */
2247*83ee113eSDavid van Moolenbroek int
save_option_buffer(struct universe * universe,struct option_state * options,struct buffer * bp,unsigned char * buffer,unsigned length,unsigned code,int terminatep)2248*83ee113eSDavid van Moolenbroek save_option_buffer(struct universe *universe, struct option_state *options,
2249*83ee113eSDavid van Moolenbroek 		   struct buffer *bp, unsigned char *buffer, unsigned length,
2250*83ee113eSDavid van Moolenbroek 		   unsigned code, int terminatep)
2251*83ee113eSDavid van Moolenbroek {
2252*83ee113eSDavid van Moolenbroek 	struct option_cache *op = NULL;
2253*83ee113eSDavid van Moolenbroek 	int status = 1;
2254*83ee113eSDavid van Moolenbroek 
2255*83ee113eSDavid van Moolenbroek 	status = prepare_option_buffer(universe, bp, buffer, length, code,
2256*83ee113eSDavid van Moolenbroek 				       terminatep, &op);
2257*83ee113eSDavid van Moolenbroek 
2258*83ee113eSDavid van Moolenbroek 	if (status == 0)
2259*83ee113eSDavid van Moolenbroek 		goto cleanup;
2260*83ee113eSDavid van Moolenbroek 
2261*83ee113eSDavid van Moolenbroek 	save_option(universe, options, op);
2262*83ee113eSDavid van Moolenbroek 
2263*83ee113eSDavid van Moolenbroek     cleanup:
2264*83ee113eSDavid van Moolenbroek 	if (op != NULL)
2265*83ee113eSDavid van Moolenbroek 		option_cache_dereference(&op, MDL);
2266*83ee113eSDavid van Moolenbroek 
2267*83ee113eSDavid van Moolenbroek 	return status;
2268*83ee113eSDavid van Moolenbroek }
2269*83ee113eSDavid van Moolenbroek 
2270*83ee113eSDavid van Moolenbroek /* Append a specified buffer onto the tail of an option cache. */
2271*83ee113eSDavid van Moolenbroek int
append_option_buffer(struct universe * universe,struct option_state * options,struct buffer * bp,unsigned char * buffer,unsigned length,unsigned code,int terminatep)2272*83ee113eSDavid van Moolenbroek append_option_buffer(struct universe *universe, struct option_state *options,
2273*83ee113eSDavid van Moolenbroek 		     struct buffer *bp, unsigned char *buffer, unsigned length,
2274*83ee113eSDavid van Moolenbroek 		     unsigned code, int terminatep)
2275*83ee113eSDavid van Moolenbroek {
2276*83ee113eSDavid van Moolenbroek 	struct option_cache *op = NULL;
2277*83ee113eSDavid van Moolenbroek 	int status = 1;
2278*83ee113eSDavid van Moolenbroek 
2279*83ee113eSDavid van Moolenbroek 	status = prepare_option_buffer(universe, bp, buffer, length, code,
2280*83ee113eSDavid van Moolenbroek 				       terminatep, &op);
2281*83ee113eSDavid van Moolenbroek 
2282*83ee113eSDavid van Moolenbroek 	if (status == 0)
2283*83ee113eSDavid van Moolenbroek 		goto cleanup;
2284*83ee113eSDavid van Moolenbroek 
2285*83ee113eSDavid van Moolenbroek 	also_save_option(universe, options, op);
2286*83ee113eSDavid van Moolenbroek 
2287*83ee113eSDavid van Moolenbroek       cleanup:
2288*83ee113eSDavid van Moolenbroek 	if (op != NULL)
2289*83ee113eSDavid van Moolenbroek 		option_cache_dereference(&op, MDL);
2290*83ee113eSDavid van Moolenbroek 
2291*83ee113eSDavid van Moolenbroek 	return status;
2292*83ee113eSDavid van Moolenbroek }
2293*83ee113eSDavid van Moolenbroek 
2294*83ee113eSDavid van Moolenbroek /* Create/copy a buffer into a new option cache. */
2295*83ee113eSDavid van Moolenbroek static int
prepare_option_buffer(struct universe * universe,struct buffer * bp,unsigned char * buffer,unsigned length,unsigned code,int terminatep,struct option_cache ** opp)2296*83ee113eSDavid van Moolenbroek prepare_option_buffer(struct universe *universe, struct buffer *bp,
2297*83ee113eSDavid van Moolenbroek 		      unsigned char *buffer, unsigned length, unsigned code,
2298*83ee113eSDavid van Moolenbroek 		      int terminatep, struct option_cache **opp)
2299*83ee113eSDavid van Moolenbroek {
2300*83ee113eSDavid van Moolenbroek 	struct buffer *lbp = NULL;
2301*83ee113eSDavid van Moolenbroek 	struct option *option = NULL;
2302*83ee113eSDavid van Moolenbroek 	struct option_cache *op;
2303*83ee113eSDavid van Moolenbroek 	int status = 1;
2304*83ee113eSDavid van Moolenbroek 
2305*83ee113eSDavid van Moolenbroek 	/* Code sizes of 8, 16, and 32 bits are allowed. */
2306*83ee113eSDavid van Moolenbroek 	switch(universe->tag_size) {
2307*83ee113eSDavid van Moolenbroek 	      case 1:
2308*83ee113eSDavid van Moolenbroek 		if (code > 0xff)
2309*83ee113eSDavid van Moolenbroek 			return 0;
2310*83ee113eSDavid van Moolenbroek 		break;
2311*83ee113eSDavid van Moolenbroek 	      case 2:
2312*83ee113eSDavid van Moolenbroek 		if (code > 0xffff)
2313*83ee113eSDavid van Moolenbroek 			return 0;
2314*83ee113eSDavid van Moolenbroek 		break;
2315*83ee113eSDavid van Moolenbroek 	      case 4:
2316*83ee113eSDavid van Moolenbroek 		if (code > 0xffffffff)
2317*83ee113eSDavid van Moolenbroek 			return 0;
2318*83ee113eSDavid van Moolenbroek 		break;
2319*83ee113eSDavid van Moolenbroek 
2320*83ee113eSDavid van Moolenbroek 	      default:
2321*83ee113eSDavid van Moolenbroek 		log_fatal("Inconsistent universe tag size at %s:%d.", MDL);
2322*83ee113eSDavid van Moolenbroek 	}
2323*83ee113eSDavid van Moolenbroek 
2324*83ee113eSDavid van Moolenbroek 	option_code_hash_lookup(&option, universe->code_hash, &code, 0, MDL);
2325*83ee113eSDavid van Moolenbroek 
2326*83ee113eSDavid van Moolenbroek 	/* If we created an option structure for each option a client
2327*83ee113eSDavid van Moolenbroek 	 * supplied, it's possible we may create > 2^32 option structures.
2328*83ee113eSDavid van Moolenbroek 	 * That's not feasible.  So by failing to enter these option
2329*83ee113eSDavid van Moolenbroek 	 * structures into the code and name hash tables, references will
2330*83ee113eSDavid van Moolenbroek 	 * never be more than 1 - when the option cache is destroyed, this
2331*83ee113eSDavid van Moolenbroek 	 * will be cleaned up.
2332*83ee113eSDavid van Moolenbroek 	 */
2333*83ee113eSDavid van Moolenbroek 	if (!option) {
2334*83ee113eSDavid van Moolenbroek 		char nbuf[sizeof("unknown-4294967295")];
2335*83ee113eSDavid van Moolenbroek 
2336*83ee113eSDavid van Moolenbroek 		sprintf(nbuf, "unknown-%u", code);
2337*83ee113eSDavid van Moolenbroek 
2338*83ee113eSDavid van Moolenbroek 		option = new_option(nbuf, MDL);
2339*83ee113eSDavid van Moolenbroek 
2340*83ee113eSDavid van Moolenbroek 		if (!option)
2341*83ee113eSDavid van Moolenbroek 			return 0;
2342*83ee113eSDavid van Moolenbroek 
2343*83ee113eSDavid van Moolenbroek 		option->format = default_option_format;
2344*83ee113eSDavid van Moolenbroek 		option->universe = universe;
2345*83ee113eSDavid van Moolenbroek 		option->code = code;
2346*83ee113eSDavid van Moolenbroek 
2347*83ee113eSDavid van Moolenbroek 		/* new_option() doesn't set references, pretend. */
2348*83ee113eSDavid van Moolenbroek 		option->refcnt = 1;
2349*83ee113eSDavid van Moolenbroek 	}
2350*83ee113eSDavid van Moolenbroek 
2351*83ee113eSDavid van Moolenbroek 	if (!option_cache_allocate (opp, MDL)) {
2352*83ee113eSDavid van Moolenbroek 		log_error("No memory for option code %s.%s.",
2353*83ee113eSDavid van Moolenbroek 			  universe->name, option->name);
2354*83ee113eSDavid van Moolenbroek 		status = 0;
2355*83ee113eSDavid van Moolenbroek 		goto cleanup;
2356*83ee113eSDavid van Moolenbroek 	}
2357*83ee113eSDavid van Moolenbroek 
2358*83ee113eSDavid van Moolenbroek 	/* Pointer rather than double pointer makes for less parens. */
2359*83ee113eSDavid van Moolenbroek 	op = *opp;
2360*83ee113eSDavid van Moolenbroek 
2361*83ee113eSDavid van Moolenbroek 	option_reference(&op->option, option, MDL);
2362*83ee113eSDavid van Moolenbroek 
2363*83ee113eSDavid van Moolenbroek 	/* If we weren't passed a buffer in which the data are saved and
2364*83ee113eSDavid van Moolenbroek 	   refcounted, allocate one now. */
2365*83ee113eSDavid van Moolenbroek 	if (!bp) {
2366*83ee113eSDavid van Moolenbroek 		if (!buffer_allocate (&lbp, length + terminatep, MDL)) {
2367*83ee113eSDavid van Moolenbroek 			log_error ("no memory for option buffer.");
2368*83ee113eSDavid van Moolenbroek 
2369*83ee113eSDavid van Moolenbroek 			status = 0;
2370*83ee113eSDavid van Moolenbroek 			goto cleanup;
2371*83ee113eSDavid van Moolenbroek 		}
2372*83ee113eSDavid van Moolenbroek 		memcpy (lbp -> data, buffer, length + terminatep);
2373*83ee113eSDavid van Moolenbroek 		bp = lbp;
2374*83ee113eSDavid van Moolenbroek 		buffer = &bp -> data [0]; /* Refer to saved buffer. */
2375*83ee113eSDavid van Moolenbroek 	}
2376*83ee113eSDavid van Moolenbroek 
2377*83ee113eSDavid van Moolenbroek 	/* Reference buffer copy to option cache. */
2378*83ee113eSDavid van Moolenbroek 	op -> data.buffer = (struct buffer *)0;
2379*83ee113eSDavid van Moolenbroek 	buffer_reference (&op -> data.buffer, bp, MDL);
2380*83ee113eSDavid van Moolenbroek 
2381*83ee113eSDavid van Moolenbroek 	/* Point option cache into buffer. */
2382*83ee113eSDavid van Moolenbroek 	op -> data.data = buffer;
2383*83ee113eSDavid van Moolenbroek 	op -> data.len = length;
2384*83ee113eSDavid van Moolenbroek 
2385*83ee113eSDavid van Moolenbroek 	if (terminatep) {
2386*83ee113eSDavid van Moolenbroek 		/* NUL terminate (we can get away with this because we (or
2387*83ee113eSDavid van Moolenbroek 		   the caller!) allocated one more than the buffer size, and
2388*83ee113eSDavid van Moolenbroek 		   because the byte following the end of an option is always
2389*83ee113eSDavid van Moolenbroek 		   the code of the next option, which the caller is getting
2390*83ee113eSDavid van Moolenbroek 		   out of the *original* buffer. */
2391*83ee113eSDavid van Moolenbroek 		buffer [length] = 0;
2392*83ee113eSDavid van Moolenbroek 		op -> data.terminated = 1;
2393*83ee113eSDavid van Moolenbroek 	} else
2394*83ee113eSDavid van Moolenbroek 		op -> data.terminated = 0;
2395*83ee113eSDavid van Moolenbroek 
2396*83ee113eSDavid van Moolenbroek 	/* If this option is ultimately a text option, null determinate to
2397*83ee113eSDavid van Moolenbroek 	 * comply with RFC2132 section 2.  Mark a flag so this can be sensed
2398*83ee113eSDavid van Moolenbroek 	 * later to echo NULLs back to clients that supplied them (they
2399*83ee113eSDavid van Moolenbroek 	 * probably expect them).
2400*83ee113eSDavid van Moolenbroek 	 */
2401*83ee113eSDavid van Moolenbroek 	if (format_has_text(option->format)) {
2402*83ee113eSDavid van Moolenbroek 		int min_len = format_min_length(option->format, op);
2403*83ee113eSDavid van Moolenbroek 
2404*83ee113eSDavid van Moolenbroek 		while ((op->data.len > min_len) &&
2405*83ee113eSDavid van Moolenbroek 		       (op->data.data[op->data.len-1] == '\0')) {
2406*83ee113eSDavid van Moolenbroek 			op->data.len--;
2407*83ee113eSDavid van Moolenbroek 			op->flags |= OPTION_HAD_NULLS;
2408*83ee113eSDavid van Moolenbroek 		}
2409*83ee113eSDavid van Moolenbroek 	}
2410*83ee113eSDavid van Moolenbroek 
2411*83ee113eSDavid van Moolenbroek 	/* And let go of our references. */
2412*83ee113eSDavid van Moolenbroek       cleanup:
2413*83ee113eSDavid van Moolenbroek 	if (lbp != NULL)
2414*83ee113eSDavid van Moolenbroek 		buffer_dereference(&lbp, MDL);
2415*83ee113eSDavid van Moolenbroek 	option_dereference(&option, MDL);
2416*83ee113eSDavid van Moolenbroek 
2417*83ee113eSDavid van Moolenbroek 	return status;
2418*83ee113eSDavid van Moolenbroek }
2419*83ee113eSDavid van Moolenbroek 
2420*83ee113eSDavid van Moolenbroek static void
count_options(struct option_cache * dummy_oc,struct packet * dummy_packet,struct lease * dummy_lease,struct client_state * dummy_client_state,struct option_state * dummy_opt_state,struct option_state * opt_state,struct binding_scope ** dummy_binding_scope,struct universe * dummy_universe,void * void_accumulator)2421*83ee113eSDavid van Moolenbroek count_options(struct option_cache *dummy_oc,
2422*83ee113eSDavid van Moolenbroek 	      struct packet *dummy_packet,
2423*83ee113eSDavid van Moolenbroek 	      struct lease *dummy_lease,
2424*83ee113eSDavid van Moolenbroek 	      struct client_state *dummy_client_state,
2425*83ee113eSDavid van Moolenbroek 	      struct option_state *dummy_opt_state,
2426*83ee113eSDavid van Moolenbroek 	      struct option_state *opt_state,
2427*83ee113eSDavid van Moolenbroek 	      struct binding_scope **dummy_binding_scope,
2428*83ee113eSDavid van Moolenbroek 	      struct universe *dummy_universe,
2429*83ee113eSDavid van Moolenbroek 	      void *void_accumulator) {
2430*83ee113eSDavid van Moolenbroek 	int *accumulator = (int *)void_accumulator;
2431*83ee113eSDavid van Moolenbroek 
2432*83ee113eSDavid van Moolenbroek 	*accumulator += 1;
2433*83ee113eSDavid van Moolenbroek }
2434*83ee113eSDavid van Moolenbroek 
2435*83ee113eSDavid van Moolenbroek static void
collect_oro(struct option_cache * oc,struct packet * dummy_packet,struct lease * dummy_lease,struct client_state * dummy_client_state,struct option_state * dummy_opt_state,struct option_state * opt_state,struct binding_scope ** dummy_binding_scope,struct universe * dummy_universe,void * void_oro)2436*83ee113eSDavid van Moolenbroek collect_oro(struct option_cache *oc,
2437*83ee113eSDavid van Moolenbroek 	    struct packet *dummy_packet,
2438*83ee113eSDavid van Moolenbroek 	    struct lease *dummy_lease,
2439*83ee113eSDavid van Moolenbroek 	    struct client_state *dummy_client_state,
2440*83ee113eSDavid van Moolenbroek 	    struct option_state *dummy_opt_state,
2441*83ee113eSDavid van Moolenbroek 	    struct option_state *opt_state,
2442*83ee113eSDavid van Moolenbroek 	    struct binding_scope **dummy_binding_scope,
2443*83ee113eSDavid van Moolenbroek 	    struct universe *dummy_universe,
2444*83ee113eSDavid van Moolenbroek 	    void *void_oro) {
2445*83ee113eSDavid van Moolenbroek 	struct data_string *oro = (struct data_string *)void_oro;
2446*83ee113eSDavid van Moolenbroek 
2447*83ee113eSDavid van Moolenbroek 	putUShort(oro->buffer->data + oro->len, oc->option->code);
2448*83ee113eSDavid van Moolenbroek 	oro->len += 2;
2449*83ee113eSDavid van Moolenbroek }
2450*83ee113eSDavid van Moolenbroek 
2451*83ee113eSDavid van Moolenbroek /* build_server_oro() is presently unusued, but may be used at a future date
2452*83ee113eSDavid van Moolenbroek  * with support for Reconfigure messages (as a hint to the client about new
2453*83ee113eSDavid van Moolenbroek  * option value contents).
2454*83ee113eSDavid van Moolenbroek  */
2455*83ee113eSDavid van Moolenbroek void
build_server_oro(struct data_string * server_oro,struct option_state * options,const char * file,int line)2456*83ee113eSDavid van Moolenbroek build_server_oro(struct data_string *server_oro,
2457*83ee113eSDavid van Moolenbroek 		 struct option_state *options,
2458*83ee113eSDavid van Moolenbroek 		 const char *file, int line) {
2459*83ee113eSDavid van Moolenbroek 	int num_opts;
2460*83ee113eSDavid van Moolenbroek 	int i;
2461*83ee113eSDavid van Moolenbroek 	struct option *o;
2462*83ee113eSDavid van Moolenbroek 
2463*83ee113eSDavid van Moolenbroek 	/*
2464*83ee113eSDavid van Moolenbroek 	 * Count the number of options, so we can allocate enough memory.
2465*83ee113eSDavid van Moolenbroek 	 * We want to mention sub-options too, so check all universes.
2466*83ee113eSDavid van Moolenbroek 	 */
2467*83ee113eSDavid van Moolenbroek 	num_opts = 0;
2468*83ee113eSDavid van Moolenbroek 	option_space_foreach(NULL, NULL, NULL, NULL, options,
2469*83ee113eSDavid van Moolenbroek 			     NULL, &dhcpv6_universe, (void *)&num_opts,
2470*83ee113eSDavid van Moolenbroek 			     count_options);
2471*83ee113eSDavid van Moolenbroek 	for (i=0; i < options->universe_count; i++) {
2472*83ee113eSDavid van Moolenbroek 		if (options->universes[i] != NULL) {
2473*83ee113eSDavid van Moolenbroek 		    	o = universes[i]->enc_opt;
2474*83ee113eSDavid van Moolenbroek 			while (o != NULL) {
2475*83ee113eSDavid van Moolenbroek 				if (o->universe == &dhcpv6_universe) {
2476*83ee113eSDavid van Moolenbroek 					num_opts++;
2477*83ee113eSDavid van Moolenbroek 					break;
2478*83ee113eSDavid van Moolenbroek 				}
2479*83ee113eSDavid van Moolenbroek 				o = o->universe->enc_opt;
2480*83ee113eSDavid van Moolenbroek 			}
2481*83ee113eSDavid van Moolenbroek 		}
2482*83ee113eSDavid van Moolenbroek 	}
2483*83ee113eSDavid van Moolenbroek 
2484*83ee113eSDavid van Moolenbroek 	/*
2485*83ee113eSDavid van Moolenbroek 	 * Allocate space.
2486*83ee113eSDavid van Moolenbroek 	 */
2487*83ee113eSDavid van Moolenbroek 	memset(server_oro, 0, sizeof(*server_oro));
2488*83ee113eSDavid van Moolenbroek 	if (!buffer_allocate(&server_oro->buffer, num_opts * 2, MDL)) {
2489*83ee113eSDavid van Moolenbroek 		log_fatal("no memory to build server ORO");
2490*83ee113eSDavid van Moolenbroek 	}
2491*83ee113eSDavid van Moolenbroek 	server_oro->data = server_oro->buffer->data;
2492*83ee113eSDavid van Moolenbroek 
2493*83ee113eSDavid van Moolenbroek 	/*
2494*83ee113eSDavid van Moolenbroek 	 * Copy the data in.
2495*83ee113eSDavid van Moolenbroek 	 * We want to mention sub-options too, so check all universes.
2496*83ee113eSDavid van Moolenbroek 	 */
2497*83ee113eSDavid van Moolenbroek 	server_oro->len = 0; 	/* gets set in collect_oro */
2498*83ee113eSDavid van Moolenbroek 	option_space_foreach(NULL, NULL, NULL, NULL, options,
2499*83ee113eSDavid van Moolenbroek 			     NULL, &dhcpv6_universe, (void *)server_oro,
2500*83ee113eSDavid van Moolenbroek 			     collect_oro);
2501*83ee113eSDavid van Moolenbroek 	for (i=0; i < options->universe_count; i++) {
2502*83ee113eSDavid van Moolenbroek 		if (options->universes[i] != NULL) {
2503*83ee113eSDavid van Moolenbroek 		    	o = universes[i]->enc_opt;
2504*83ee113eSDavid van Moolenbroek 			while (o != NULL) {
2505*83ee113eSDavid van Moolenbroek 				if (o->universe == &dhcpv6_universe) {
2506*83ee113eSDavid van Moolenbroek 					unsigned char *tmp;
2507*83ee113eSDavid van Moolenbroek 					tmp = server_oro->buffer->data;
2508*83ee113eSDavid van Moolenbroek 					putUShort(tmp + server_oro->len,
2509*83ee113eSDavid van Moolenbroek 						  o->code);
2510*83ee113eSDavid van Moolenbroek 					server_oro->len += 2;
2511*83ee113eSDavid van Moolenbroek 					break;
2512*83ee113eSDavid van Moolenbroek 				}
2513*83ee113eSDavid van Moolenbroek 				o = o->universe->enc_opt;
2514*83ee113eSDavid van Moolenbroek 			}
2515*83ee113eSDavid van Moolenbroek 		}
2516*83ee113eSDavid van Moolenbroek 	}
2517*83ee113eSDavid van Moolenbroek }
2518*83ee113eSDavid van Moolenbroek 
2519*83ee113eSDavid van Moolenbroek /* Wrapper function to put an option cache into an option state. */
2520*83ee113eSDavid van Moolenbroek void
save_option(struct universe * universe,struct option_state * options,struct option_cache * oc)2521*83ee113eSDavid van Moolenbroek save_option(struct universe *universe, struct option_state *options,
2522*83ee113eSDavid van Moolenbroek 	    struct option_cache *oc)
2523*83ee113eSDavid van Moolenbroek {
2524*83ee113eSDavid van Moolenbroek 	if (universe->save_func)
2525*83ee113eSDavid van Moolenbroek 		(*universe->save_func)(universe, options, oc, ISC_FALSE);
2526*83ee113eSDavid van Moolenbroek 	else
2527*83ee113eSDavid van Moolenbroek 		log_error("can't store options in %s space.", universe->name);
2528*83ee113eSDavid van Moolenbroek }
2529*83ee113eSDavid van Moolenbroek 
2530*83ee113eSDavid van Moolenbroek /* Wrapper function to append an option cache into an option state's list. */
2531*83ee113eSDavid van Moolenbroek void
also_save_option(struct universe * universe,struct option_state * options,struct option_cache * oc)2532*83ee113eSDavid van Moolenbroek also_save_option(struct universe *universe, struct option_state *options,
2533*83ee113eSDavid van Moolenbroek 		 struct option_cache *oc)
2534*83ee113eSDavid van Moolenbroek {
2535*83ee113eSDavid van Moolenbroek 	if (universe->save_func)
2536*83ee113eSDavid van Moolenbroek 		(*universe->save_func)(universe, options, oc, ISC_TRUE);
2537*83ee113eSDavid van Moolenbroek 	else
2538*83ee113eSDavid van Moolenbroek 		log_error("can't store options in %s space.", universe->name);
2539*83ee113eSDavid van Moolenbroek }
2540*83ee113eSDavid van Moolenbroek 
2541*83ee113eSDavid van Moolenbroek void
save_hashed_option(struct universe * universe,struct option_state * options,struct option_cache * oc,isc_boolean_t appendp)2542*83ee113eSDavid van Moolenbroek save_hashed_option(struct universe *universe, struct option_state *options,
2543*83ee113eSDavid van Moolenbroek 		   struct option_cache *oc, isc_boolean_t appendp)
2544*83ee113eSDavid van Moolenbroek {
2545*83ee113eSDavid van Moolenbroek 	int hashix;
2546*83ee113eSDavid van Moolenbroek 	pair bptr;
2547*83ee113eSDavid van Moolenbroek 	pair *hash = options -> universes [universe -> index];
2548*83ee113eSDavid van Moolenbroek 	struct option_cache **ocloc;
2549*83ee113eSDavid van Moolenbroek 
2550*83ee113eSDavid van Moolenbroek 	if (oc -> refcnt == 0)
2551*83ee113eSDavid van Moolenbroek 		abort ();
2552*83ee113eSDavid van Moolenbroek 
2553*83ee113eSDavid van Moolenbroek 	/* Compute the hash. */
2554*83ee113eSDavid van Moolenbroek 	hashix = compute_option_hash (oc -> option -> code);
2555*83ee113eSDavid van Moolenbroek 
2556*83ee113eSDavid van Moolenbroek 	/* If there's no hash table, make one. */
2557*83ee113eSDavid van Moolenbroek 	if (!hash) {
2558*83ee113eSDavid van Moolenbroek 		hash = (pair *)dmalloc (OPTION_HASH_SIZE * sizeof *hash, MDL);
2559*83ee113eSDavid van Moolenbroek 		if (!hash) {
2560*83ee113eSDavid van Moolenbroek 			log_error ("no memory to store %s.%s",
2561*83ee113eSDavid van Moolenbroek 				   universe -> name, oc -> option -> name);
2562*83ee113eSDavid van Moolenbroek 			return;
2563*83ee113eSDavid van Moolenbroek 		}
2564*83ee113eSDavid van Moolenbroek 		memset (hash, 0, OPTION_HASH_SIZE * sizeof *hash);
2565*83ee113eSDavid van Moolenbroek 		options -> universes [universe -> index] = (void *)hash;
2566*83ee113eSDavid van Moolenbroek 	} else {
2567*83ee113eSDavid van Moolenbroek 		/* Try to find an existing option matching the new one. */
2568*83ee113eSDavid van Moolenbroek 		for (bptr = hash [hashix]; bptr; bptr = bptr -> cdr) {
2569*83ee113eSDavid van Moolenbroek 			if (((struct option_cache *)
2570*83ee113eSDavid van Moolenbroek 			     (bptr -> car)) -> option -> code ==
2571*83ee113eSDavid van Moolenbroek 			    oc -> option -> code)
2572*83ee113eSDavid van Moolenbroek 				break;
2573*83ee113eSDavid van Moolenbroek 		}
2574*83ee113eSDavid van Moolenbroek 
2575*83ee113eSDavid van Moolenbroek 		/* Deal with collisions on the hash list. */
2576*83ee113eSDavid van Moolenbroek 		if (bptr) {
2577*83ee113eSDavid van Moolenbroek 			ocloc = (struct option_cache **)&bptr->car;
2578*83ee113eSDavid van Moolenbroek 
2579*83ee113eSDavid van Moolenbroek 			/*
2580*83ee113eSDavid van Moolenbroek 			 * If appendp is set, append it onto the tail of the
2581*83ee113eSDavid van Moolenbroek 			 * ->next list.  If it is not set, rotate it into
2582*83ee113eSDavid van Moolenbroek 			 * position at the head of the list.
2583*83ee113eSDavid van Moolenbroek 			 */
2584*83ee113eSDavid van Moolenbroek 			if (appendp) {
2585*83ee113eSDavid van Moolenbroek 				do {
2586*83ee113eSDavid van Moolenbroek 					ocloc = &(*ocloc)->next;
2587*83ee113eSDavid van Moolenbroek 				} while (*ocloc != NULL);
2588*83ee113eSDavid van Moolenbroek 			} else {
2589*83ee113eSDavid van Moolenbroek 				option_cache_dereference(ocloc, MDL);
2590*83ee113eSDavid van Moolenbroek 			}
2591*83ee113eSDavid van Moolenbroek 
2592*83ee113eSDavid van Moolenbroek 			option_cache_reference(ocloc, oc, MDL);
2593*83ee113eSDavid van Moolenbroek 			return;
2594*83ee113eSDavid van Moolenbroek 		}
2595*83ee113eSDavid van Moolenbroek 	}
2596*83ee113eSDavid van Moolenbroek 
2597*83ee113eSDavid van Moolenbroek 	/* Otherwise, just put the new one at the head of the list. */
2598*83ee113eSDavid van Moolenbroek 	bptr = new_pair (MDL);
2599*83ee113eSDavid van Moolenbroek 	if (!bptr) {
2600*83ee113eSDavid van Moolenbroek 		log_error ("No memory for option_cache reference.");
2601*83ee113eSDavid van Moolenbroek 		return;
2602*83ee113eSDavid van Moolenbroek 	}
2603*83ee113eSDavid van Moolenbroek 	bptr -> cdr = hash [hashix];
2604*83ee113eSDavid van Moolenbroek 	bptr -> car = 0;
2605*83ee113eSDavid van Moolenbroek 	option_cache_reference ((struct option_cache **)&bptr -> car, oc, MDL);
2606*83ee113eSDavid van Moolenbroek 	hash [hashix] = bptr;
2607*83ee113eSDavid van Moolenbroek }
2608*83ee113eSDavid van Moolenbroek 
delete_option(universe,options,code)2609*83ee113eSDavid van Moolenbroek void delete_option (universe, options, code)
2610*83ee113eSDavid van Moolenbroek 	struct universe *universe;
2611*83ee113eSDavid van Moolenbroek 	struct option_state *options;
2612*83ee113eSDavid van Moolenbroek 	int code;
2613*83ee113eSDavid van Moolenbroek {
2614*83ee113eSDavid van Moolenbroek 	if (universe -> delete_func)
2615*83ee113eSDavid van Moolenbroek 		(*universe -> delete_func) (universe, options, code);
2616*83ee113eSDavid van Moolenbroek 	else
2617*83ee113eSDavid van Moolenbroek 		log_error ("can't delete options from %s space.",
2618*83ee113eSDavid van Moolenbroek 			   universe -> name);
2619*83ee113eSDavid van Moolenbroek }
2620*83ee113eSDavid van Moolenbroek 
delete_hashed_option(universe,options,code)2621*83ee113eSDavid van Moolenbroek void delete_hashed_option (universe, options, code)
2622*83ee113eSDavid van Moolenbroek 	struct universe *universe;
2623*83ee113eSDavid van Moolenbroek 	struct option_state *options;
2624*83ee113eSDavid van Moolenbroek 	int code;
2625*83ee113eSDavid van Moolenbroek {
2626*83ee113eSDavid van Moolenbroek 	int hashix;
2627*83ee113eSDavid van Moolenbroek 	pair bptr, prev = (pair)0;
2628*83ee113eSDavid van Moolenbroek 	pair *hash = options -> universes [universe -> index];
2629*83ee113eSDavid van Moolenbroek 
2630*83ee113eSDavid van Moolenbroek 	/* There may not be any options in this space. */
2631*83ee113eSDavid van Moolenbroek 	if (!hash)
2632*83ee113eSDavid van Moolenbroek 		return;
2633*83ee113eSDavid van Moolenbroek 
2634*83ee113eSDavid van Moolenbroek 	/* Try to find an existing option matching the new one. */
2635*83ee113eSDavid van Moolenbroek 	hashix = compute_option_hash (code);
2636*83ee113eSDavid van Moolenbroek 	for (bptr = hash [hashix]; bptr; bptr = bptr -> cdr) {
2637*83ee113eSDavid van Moolenbroek 		if (((struct option_cache *)(bptr -> car)) -> option -> code
2638*83ee113eSDavid van Moolenbroek 		    == code)
2639*83ee113eSDavid van Moolenbroek 			break;
2640*83ee113eSDavid van Moolenbroek 		prev = bptr;
2641*83ee113eSDavid van Moolenbroek 	}
2642*83ee113eSDavid van Moolenbroek 	/* If we found one, wipe it out... */
2643*83ee113eSDavid van Moolenbroek 	if (bptr) {
2644*83ee113eSDavid van Moolenbroek 		if (prev)
2645*83ee113eSDavid van Moolenbroek 			prev -> cdr = bptr -> cdr;
2646*83ee113eSDavid van Moolenbroek 		else
2647*83ee113eSDavid van Moolenbroek 			hash [hashix] = bptr -> cdr;
2648*83ee113eSDavid van Moolenbroek 		option_cache_dereference
2649*83ee113eSDavid van Moolenbroek 			((struct option_cache **)(&bptr -> car), MDL);
2650*83ee113eSDavid van Moolenbroek 		free_pair (bptr, MDL);
2651*83ee113eSDavid van Moolenbroek 	}
2652*83ee113eSDavid van Moolenbroek }
2653*83ee113eSDavid van Moolenbroek 
2654*83ee113eSDavid van Moolenbroek extern struct option_cache *free_option_caches; /* XXX */
2655*83ee113eSDavid van Moolenbroek 
option_cache_dereference(ptr,file,line)2656*83ee113eSDavid van Moolenbroek int option_cache_dereference (ptr, file, line)
2657*83ee113eSDavid van Moolenbroek 	struct option_cache **ptr;
2658*83ee113eSDavid van Moolenbroek 	const char *file;
2659*83ee113eSDavid van Moolenbroek 	int line;
2660*83ee113eSDavid van Moolenbroek {
2661*83ee113eSDavid van Moolenbroek 	if (!ptr || !*ptr) {
2662*83ee113eSDavid van Moolenbroek 		log_error ("Null pointer in option_cache_dereference: %s(%d)",
2663*83ee113eSDavid van Moolenbroek 			   file, line);
2664*83ee113eSDavid van Moolenbroek #if defined (POINTER_DEBUG)
2665*83ee113eSDavid van Moolenbroek 		abort ();
2666*83ee113eSDavid van Moolenbroek #else
2667*83ee113eSDavid van Moolenbroek 		return 0;
2668*83ee113eSDavid van Moolenbroek #endif
2669*83ee113eSDavid van Moolenbroek 	}
2670*83ee113eSDavid van Moolenbroek 
2671*83ee113eSDavid van Moolenbroek 	(*ptr) -> refcnt--;
2672*83ee113eSDavid van Moolenbroek 	rc_register (file, line, ptr, *ptr, (*ptr) -> refcnt, 1, RC_MISC);
2673*83ee113eSDavid van Moolenbroek 	if (!(*ptr) -> refcnt) {
2674*83ee113eSDavid van Moolenbroek 		if ((*ptr) -> data.buffer)
2675*83ee113eSDavid van Moolenbroek 			data_string_forget (&(*ptr) -> data, file, line);
2676*83ee113eSDavid van Moolenbroek 		if ((*ptr)->option)
2677*83ee113eSDavid van Moolenbroek 			option_dereference(&(*ptr)->option, MDL);
2678*83ee113eSDavid van Moolenbroek 		if ((*ptr) -> expression)
2679*83ee113eSDavid van Moolenbroek 			expression_dereference (&(*ptr) -> expression,
2680*83ee113eSDavid van Moolenbroek 						file, line);
2681*83ee113eSDavid van Moolenbroek 		if ((*ptr) -> next)
2682*83ee113eSDavid van Moolenbroek 			option_cache_dereference (&((*ptr) -> next),
2683*83ee113eSDavid van Moolenbroek 						  file, line);
2684*83ee113eSDavid van Moolenbroek 		/* Put it back on the free list... */
2685*83ee113eSDavid van Moolenbroek 		(*ptr) -> expression = (struct expression *)free_option_caches;
2686*83ee113eSDavid van Moolenbroek 		free_option_caches = *ptr;
2687*83ee113eSDavid van Moolenbroek 		dmalloc_reuse (free_option_caches, (char *)0, 0, 0);
2688*83ee113eSDavid van Moolenbroek 	}
2689*83ee113eSDavid van Moolenbroek 	if ((*ptr) -> refcnt < 0) {
2690*83ee113eSDavid van Moolenbroek 		log_error ("%s(%d): negative refcnt!", file, line);
2691*83ee113eSDavid van Moolenbroek #if defined (DEBUG_RC_HISTORY)
2692*83ee113eSDavid van Moolenbroek 		dump_rc_history (*ptr);
2693*83ee113eSDavid van Moolenbroek #endif
2694*83ee113eSDavid van Moolenbroek #if defined (POINTER_DEBUG)
2695*83ee113eSDavid van Moolenbroek 		abort ();
2696*83ee113eSDavid van Moolenbroek #else
2697*83ee113eSDavid van Moolenbroek 		*ptr = (struct option_cache *)0;
2698*83ee113eSDavid van Moolenbroek 		return 0;
2699*83ee113eSDavid van Moolenbroek #endif
2700*83ee113eSDavid van Moolenbroek 	}
2701*83ee113eSDavid van Moolenbroek 	*ptr = (struct option_cache *)0;
2702*83ee113eSDavid van Moolenbroek 	return 1;
2703*83ee113eSDavid van Moolenbroek 
2704*83ee113eSDavid van Moolenbroek }
2705*83ee113eSDavid van Moolenbroek 
hashed_option_state_dereference(universe,state,file,line)2706*83ee113eSDavid van Moolenbroek int hashed_option_state_dereference (universe, state, file, line)
2707*83ee113eSDavid van Moolenbroek 	struct universe *universe;
2708*83ee113eSDavid van Moolenbroek 	struct option_state *state;
2709*83ee113eSDavid van Moolenbroek 	const char *file;
2710*83ee113eSDavid van Moolenbroek 	int line;
2711*83ee113eSDavid van Moolenbroek {
2712*83ee113eSDavid van Moolenbroek 	pair *heads;
2713*83ee113eSDavid van Moolenbroek 	pair cp, next;
2714*83ee113eSDavid van Moolenbroek 	int i;
2715*83ee113eSDavid van Moolenbroek 
2716*83ee113eSDavid van Moolenbroek 	/* Get the pointer to the array of hash table bucket heads. */
2717*83ee113eSDavid van Moolenbroek 	heads = (pair *)(state -> universes [universe -> index]);
2718*83ee113eSDavid van Moolenbroek 	if (!heads)
2719*83ee113eSDavid van Moolenbroek 		return 0;
2720*83ee113eSDavid van Moolenbroek 
2721*83ee113eSDavid van Moolenbroek 	/* For each non-null head, loop through all the buckets dereferencing
2722*83ee113eSDavid van Moolenbroek 	   the attached option cache structures and freeing the buckets. */
2723*83ee113eSDavid van Moolenbroek 	for (i = 0; i < OPTION_HASH_SIZE; i++) {
2724*83ee113eSDavid van Moolenbroek 		for (cp = heads [i]; cp; cp = next) {
2725*83ee113eSDavid van Moolenbroek 			next = cp -> cdr;
2726*83ee113eSDavid van Moolenbroek 			option_cache_dereference
2727*83ee113eSDavid van Moolenbroek 				((struct option_cache **)&cp -> car,
2728*83ee113eSDavid van Moolenbroek 				 file, line);
2729*83ee113eSDavid van Moolenbroek 			free_pair (cp, file, line);
2730*83ee113eSDavid van Moolenbroek 		}
2731*83ee113eSDavid van Moolenbroek 	}
2732*83ee113eSDavid van Moolenbroek 
2733*83ee113eSDavid van Moolenbroek 	dfree (heads, file, line);
2734*83ee113eSDavid van Moolenbroek 	state -> universes [universe -> index] = (void *)0;
2735*83ee113eSDavid van Moolenbroek 	return 1;
2736*83ee113eSDavid van Moolenbroek }
2737*83ee113eSDavid van Moolenbroek 
2738*83ee113eSDavid van Moolenbroek /* The 'data_string' primitive doesn't have an appension mechanism.
2739*83ee113eSDavid van Moolenbroek  * This function must then append a new option onto an existing buffer
2740*83ee113eSDavid van Moolenbroek  * by first duplicating the original buffer and appending the desired
2741*83ee113eSDavid van Moolenbroek  * values, followed by coping the new value into place.
2742*83ee113eSDavid van Moolenbroek  */
2743*83ee113eSDavid van Moolenbroek int
append_option(struct data_string * dst,struct universe * universe,struct option * option,struct data_string * src)2744*83ee113eSDavid van Moolenbroek append_option(struct data_string *dst, struct universe *universe,
2745*83ee113eSDavid van Moolenbroek 	      struct option *option, struct data_string *src)
2746*83ee113eSDavid van Moolenbroek {
2747*83ee113eSDavid van Moolenbroek 	struct data_string tmp;
2748*83ee113eSDavid van Moolenbroek 
2749*83ee113eSDavid van Moolenbroek 	if (src->len == 0 && option->format[0] != 'Z')
2750*83ee113eSDavid van Moolenbroek 		return 0;
2751*83ee113eSDavid van Moolenbroek 
2752*83ee113eSDavid van Moolenbroek 	memset(&tmp, 0, sizeof(tmp));
2753*83ee113eSDavid van Moolenbroek 
2754*83ee113eSDavid van Moolenbroek 	/* Allocate a buffer to hold existing data, the current option's
2755*83ee113eSDavid van Moolenbroek 	 * tag and length, and the option's content.
2756*83ee113eSDavid van Moolenbroek 	 */
2757*83ee113eSDavid van Moolenbroek 	if (!buffer_allocate(&tmp.buffer,
2758*83ee113eSDavid van Moolenbroek 			     (dst->len + universe->length_size +
2759*83ee113eSDavid van Moolenbroek 			      universe->tag_size + src->len), MDL)) {
2760*83ee113eSDavid van Moolenbroek 		/* XXX: This kills all options presently stored in the
2761*83ee113eSDavid van Moolenbroek 		 * destination buffer.  This is the way the original code
2762*83ee113eSDavid van Moolenbroek 		 * worked, and assumes an 'all or nothing' approach to
2763*83ee113eSDavid van Moolenbroek 		 * eg encapsulated option spaces.  It may or may not be
2764*83ee113eSDavid van Moolenbroek 		 * desirable.
2765*83ee113eSDavid van Moolenbroek 		 */
2766*83ee113eSDavid van Moolenbroek 		data_string_forget(dst, MDL);
2767*83ee113eSDavid van Moolenbroek 		return 0;
2768*83ee113eSDavid van Moolenbroek 	}
2769*83ee113eSDavid van Moolenbroek 	tmp.data = tmp.buffer->data;
2770*83ee113eSDavid van Moolenbroek 
2771*83ee113eSDavid van Moolenbroek 	/* Copy the existing data off the destination. */
2772*83ee113eSDavid van Moolenbroek 	if (dst->len != 0)
2773*83ee113eSDavid van Moolenbroek 		memcpy(tmp.buffer->data, dst->data, dst->len);
2774*83ee113eSDavid van Moolenbroek 	tmp.len = dst->len;
2775*83ee113eSDavid van Moolenbroek 
2776*83ee113eSDavid van Moolenbroek 	/* Place the new option tag and length. */
2777*83ee113eSDavid van Moolenbroek 	(*universe->store_tag)(tmp.buffer->data + tmp.len, option->code);
2778*83ee113eSDavid van Moolenbroek 	tmp.len += universe->tag_size;
2779*83ee113eSDavid van Moolenbroek 	(*universe->store_length)(tmp.buffer->data + tmp.len, src->len);
2780*83ee113eSDavid van Moolenbroek 	tmp.len += universe->length_size;
2781*83ee113eSDavid van Moolenbroek 
2782*83ee113eSDavid van Moolenbroek 	/* Copy the option contents onto the end. */
2783*83ee113eSDavid van Moolenbroek 	memcpy(tmp.buffer->data + tmp.len, src->data, src->len);
2784*83ee113eSDavid van Moolenbroek 	tmp.len += src->len;
2785*83ee113eSDavid van Moolenbroek 
2786*83ee113eSDavid van Moolenbroek 	/* Play the shell game. */
2787*83ee113eSDavid van Moolenbroek 	data_string_forget(dst, MDL);
2788*83ee113eSDavid van Moolenbroek 	data_string_copy(dst, &tmp, MDL);
2789*83ee113eSDavid van Moolenbroek 	data_string_forget(&tmp, MDL);
2790*83ee113eSDavid van Moolenbroek 	return 1;
2791*83ee113eSDavid van Moolenbroek }
2792*83ee113eSDavid van Moolenbroek 
2793*83ee113eSDavid van Moolenbroek int
store_option(struct data_string * result,struct universe * universe,struct packet * packet,struct lease * lease,struct client_state * client_state,struct option_state * in_options,struct option_state * cfg_options,struct binding_scope ** scope,struct option_cache * oc)2794*83ee113eSDavid van Moolenbroek store_option(struct data_string *result, struct universe *universe,
2795*83ee113eSDavid van Moolenbroek 	     struct packet *packet, struct lease *lease,
2796*83ee113eSDavid van Moolenbroek 	     struct client_state *client_state,
2797*83ee113eSDavid van Moolenbroek 	     struct option_state *in_options, struct option_state *cfg_options,
2798*83ee113eSDavid van Moolenbroek 	     struct binding_scope **scope, struct option_cache *oc)
2799*83ee113eSDavid van Moolenbroek {
2800*83ee113eSDavid van Moolenbroek 	struct data_string tmp;
2801*83ee113eSDavid van Moolenbroek 	struct universe *subu=NULL;
2802*83ee113eSDavid van Moolenbroek 	int status;
2803*83ee113eSDavid van Moolenbroek 	char *start, *end;
2804*83ee113eSDavid van Moolenbroek 
2805*83ee113eSDavid van Moolenbroek 	memset(&tmp, 0, sizeof(tmp));
2806*83ee113eSDavid van Moolenbroek 
2807*83ee113eSDavid van Moolenbroek 	if (evaluate_option_cache(&tmp, packet, lease, client_state,
2808*83ee113eSDavid van Moolenbroek 				  in_options, cfg_options, scope, oc, MDL)) {
2809*83ee113eSDavid van Moolenbroek 		/* If the option is an extended 'e'ncapsulation (not a
2810*83ee113eSDavid van Moolenbroek 		 * direct 'E'ncapsulation), append the encapsulated space
2811*83ee113eSDavid van Moolenbroek 		 * onto the currently prepared value.
2812*83ee113eSDavid van Moolenbroek 		 */
2813*83ee113eSDavid van Moolenbroek 		do {
2814*83ee113eSDavid van Moolenbroek 			if (oc->option->format &&
2815*83ee113eSDavid van Moolenbroek 			    oc->option->format[0] == 'e') {
2816*83ee113eSDavid van Moolenbroek 				/* Skip forward to the universe name. */
2817*83ee113eSDavid van Moolenbroek 				start = strchr(oc->option->format, 'E');
2818*83ee113eSDavid van Moolenbroek 				if (start == NULL)
2819*83ee113eSDavid van Moolenbroek 					break;
2820*83ee113eSDavid van Moolenbroek 
2821*83ee113eSDavid van Moolenbroek 				/* Locate the name-terminating '.'. */
2822*83ee113eSDavid van Moolenbroek 				end = strchr(++start, '.');
2823*83ee113eSDavid van Moolenbroek 
2824*83ee113eSDavid van Moolenbroek 				/* A zero-length name is not allowed in
2825*83ee113eSDavid van Moolenbroek 				 * these kinds of encapsulations.
2826*83ee113eSDavid van Moolenbroek 				 */
2827*83ee113eSDavid van Moolenbroek 				if (end == NULL || start == end)
2828*83ee113eSDavid van Moolenbroek 					break;
2829*83ee113eSDavid van Moolenbroek 
2830*83ee113eSDavid van Moolenbroek 				universe_hash_lookup(&subu, universe_hash,
2831*83ee113eSDavid van Moolenbroek 						     start, end - start, MDL);
2832*83ee113eSDavid van Moolenbroek 
2833*83ee113eSDavid van Moolenbroek 				if (subu == NULL) {
2834*83ee113eSDavid van Moolenbroek 					log_error("store_option: option %d "
2835*83ee113eSDavid van Moolenbroek 						  "refers to unknown "
2836*83ee113eSDavid van Moolenbroek 						  "option space '%.*s'.",
2837*83ee113eSDavid van Moolenbroek 						  oc->option->code,
2838*83ee113eSDavid van Moolenbroek 						  (int)(end - start), start);
2839*83ee113eSDavid van Moolenbroek 					break;
2840*83ee113eSDavid van Moolenbroek 				}
2841*83ee113eSDavid van Moolenbroek 
2842*83ee113eSDavid van Moolenbroek 				/* Append encapsulations, if any.  We
2843*83ee113eSDavid van Moolenbroek 				 * already have the prepended values, so
2844*83ee113eSDavid van Moolenbroek 				 * we send those even if there are no
2845*83ee113eSDavid van Moolenbroek 				 * encapsulated options (and ->encapsulate()
2846*83ee113eSDavid van Moolenbroek 				 * returns zero).
2847*83ee113eSDavid van Moolenbroek 				 */
2848*83ee113eSDavid van Moolenbroek 				subu->encapsulate(&tmp, packet, lease,
2849*83ee113eSDavid van Moolenbroek 						  client_state, in_options,
2850*83ee113eSDavid van Moolenbroek 						  cfg_options, scope, subu);
2851*83ee113eSDavid van Moolenbroek 				subu = NULL;
2852*83ee113eSDavid van Moolenbroek 			}
2853*83ee113eSDavid van Moolenbroek 		} while (ISC_FALSE);
2854*83ee113eSDavid van Moolenbroek 
2855*83ee113eSDavid van Moolenbroek 		status = append_option(result, universe, oc->option, &tmp);
2856*83ee113eSDavid van Moolenbroek 		data_string_forget(&tmp, MDL);
2857*83ee113eSDavid van Moolenbroek 
2858*83ee113eSDavid van Moolenbroek 		return status;
2859*83ee113eSDavid van Moolenbroek 	}
2860*83ee113eSDavid van Moolenbroek 
2861*83ee113eSDavid van Moolenbroek 	return 0;
2862*83ee113eSDavid van Moolenbroek }
2863*83ee113eSDavid van Moolenbroek 
option_space_encapsulate(result,packet,lease,client_state,in_options,cfg_options,scope,name)2864*83ee113eSDavid van Moolenbroek int option_space_encapsulate (result, packet, lease, client_state,
2865*83ee113eSDavid van Moolenbroek 			      in_options, cfg_options, scope, name)
2866*83ee113eSDavid van Moolenbroek 	struct data_string *result;
2867*83ee113eSDavid van Moolenbroek 	struct packet *packet;
2868*83ee113eSDavid van Moolenbroek 	struct lease *lease;
2869*83ee113eSDavid van Moolenbroek 	struct client_state *client_state;
2870*83ee113eSDavid van Moolenbroek 	struct option_state *in_options;
2871*83ee113eSDavid van Moolenbroek 	struct option_state *cfg_options;
2872*83ee113eSDavid van Moolenbroek 	struct binding_scope **scope;
2873*83ee113eSDavid van Moolenbroek 	struct data_string *name;
2874*83ee113eSDavid van Moolenbroek {
2875*83ee113eSDavid van Moolenbroek 	struct universe *u = NULL;
2876*83ee113eSDavid van Moolenbroek 	int status = 0;
2877*83ee113eSDavid van Moolenbroek 
2878*83ee113eSDavid van Moolenbroek 	universe_hash_lookup(&u, universe_hash,
2879*83ee113eSDavid van Moolenbroek 			     (const char *)name->data, name->len, MDL);
2880*83ee113eSDavid van Moolenbroek 	if (u == NULL) {
2881*83ee113eSDavid van Moolenbroek 		log_error("option_space_encapsulate: option space '%.*s' does "
2882*83ee113eSDavid van Moolenbroek 			  "not exist, but is configured.",
2883*83ee113eSDavid van Moolenbroek 			  (int)name->len, name->data);
2884*83ee113eSDavid van Moolenbroek 		return status;
2885*83ee113eSDavid van Moolenbroek 	}
2886*83ee113eSDavid van Moolenbroek 
2887*83ee113eSDavid van Moolenbroek 	if (u->encapsulate != NULL) {
2888*83ee113eSDavid van Moolenbroek 		if (u->encapsulate(result, packet, lease, client_state,
2889*83ee113eSDavid van Moolenbroek 				   in_options, cfg_options, scope, u))
2890*83ee113eSDavid van Moolenbroek 			status = 1;
2891*83ee113eSDavid van Moolenbroek 	} else
2892*83ee113eSDavid van Moolenbroek 		log_error("encapsulation requested for '%s' with no support.",
2893*83ee113eSDavid van Moolenbroek 			  name->data);
2894*83ee113eSDavid van Moolenbroek 
2895*83ee113eSDavid van Moolenbroek 	return status;
2896*83ee113eSDavid van Moolenbroek }
2897*83ee113eSDavid van Moolenbroek 
2898*83ee113eSDavid van Moolenbroek /* Attempt to store any 'E'ncapsulated options that have not yet been
2899*83ee113eSDavid van Moolenbroek  * placed on the option buffer by the above (configuring a value in
2900*83ee113eSDavid van Moolenbroek  * the space over-rides any values in the child universe).
2901*83ee113eSDavid van Moolenbroek  *
2902*83ee113eSDavid van Moolenbroek  * Note that there are far fewer universes than there will ever be
2903*83ee113eSDavid van Moolenbroek  * options in any universe.  So it is faster to traverse the
2904*83ee113eSDavid van Moolenbroek  * configured universes, checking if each is encapsulated in the
2905*83ee113eSDavid van Moolenbroek  * current universe, and if so attempting to do so.
2906*83ee113eSDavid van Moolenbroek  *
2907*83ee113eSDavid van Moolenbroek  * For each configured universe for this configuration option space,
2908*83ee113eSDavid van Moolenbroek  * which is encapsulated within the current universe, can not be found
2909*83ee113eSDavid van Moolenbroek  * by the lookup function (the universe-specific encapsulation
2910*83ee113eSDavid van Moolenbroek  * functions would already have stored such a value), and encapsulates
2911*83ee113eSDavid van Moolenbroek  * at least one option, append it.
2912*83ee113eSDavid van Moolenbroek  */
2913*83ee113eSDavid van Moolenbroek static int
search_subencapsulation(struct data_string * result,struct packet * packet,struct lease * lease,struct client_state * client_state,struct option_state * in_options,struct option_state * cfg_options,struct binding_scope ** scope,struct universe * universe)2914*83ee113eSDavid van Moolenbroek search_subencapsulation(struct data_string *result, struct packet *packet,
2915*83ee113eSDavid van Moolenbroek 			struct lease *lease, struct client_state *client_state,
2916*83ee113eSDavid van Moolenbroek 			struct option_state *in_options,
2917*83ee113eSDavid van Moolenbroek 			struct option_state *cfg_options,
2918*83ee113eSDavid van Moolenbroek 			struct binding_scope **scope,
2919*83ee113eSDavid van Moolenbroek 			struct universe *universe)
2920*83ee113eSDavid van Moolenbroek {
2921*83ee113eSDavid van Moolenbroek 	struct data_string sub;
2922*83ee113eSDavid van Moolenbroek 	struct universe *subu;
2923*83ee113eSDavid van Moolenbroek 	int i, status = 0;
2924*83ee113eSDavid van Moolenbroek 
2925*83ee113eSDavid van Moolenbroek 	memset(&sub, 0, sizeof(sub));
2926*83ee113eSDavid van Moolenbroek 	for (i = 0 ; i < cfg_options->universe_count ; i++) {
2927*83ee113eSDavid van Moolenbroek 		subu = universes[i];
2928*83ee113eSDavid van Moolenbroek 
2929*83ee113eSDavid van Moolenbroek 		if (subu == NULL)
2930*83ee113eSDavid van Moolenbroek 			log_fatal("Impossible condition at %s:%d.", MDL);
2931*83ee113eSDavid van Moolenbroek 
2932*83ee113eSDavid van Moolenbroek 		if (subu->enc_opt != NULL &&
2933*83ee113eSDavid van Moolenbroek 		    subu->enc_opt->universe == universe &&
2934*83ee113eSDavid van Moolenbroek 		    subu->enc_opt->format != NULL &&
2935*83ee113eSDavid van Moolenbroek 		    subu->enc_opt->format[0] == 'E' &&
2936*83ee113eSDavid van Moolenbroek 		    lookup_option(universe, cfg_options,
2937*83ee113eSDavid van Moolenbroek 				  subu->enc_opt->code) == NULL &&
2938*83ee113eSDavid van Moolenbroek 		    subu->encapsulate(&sub, packet, lease, client_state,
2939*83ee113eSDavid van Moolenbroek 				      in_options, cfg_options,
2940*83ee113eSDavid van Moolenbroek 				      scope, subu)) {
2941*83ee113eSDavid van Moolenbroek 			if (append_option(result, universe,
2942*83ee113eSDavid van Moolenbroek 					  subu->enc_opt, &sub))
2943*83ee113eSDavid van Moolenbroek 				status = 1;
2944*83ee113eSDavid van Moolenbroek 
2945*83ee113eSDavid van Moolenbroek 			data_string_forget(&sub, MDL);
2946*83ee113eSDavid van Moolenbroek 		}
2947*83ee113eSDavid van Moolenbroek 	}
2948*83ee113eSDavid van Moolenbroek 
2949*83ee113eSDavid van Moolenbroek 	return status;
2950*83ee113eSDavid van Moolenbroek }
2951*83ee113eSDavid van Moolenbroek 
hashed_option_space_encapsulate(result,packet,lease,client_state,in_options,cfg_options,scope,universe)2952*83ee113eSDavid van Moolenbroek int hashed_option_space_encapsulate (result, packet, lease, client_state,
2953*83ee113eSDavid van Moolenbroek 				     in_options, cfg_options, scope, universe)
2954*83ee113eSDavid van Moolenbroek 	struct data_string *result;
2955*83ee113eSDavid van Moolenbroek 	struct packet *packet;
2956*83ee113eSDavid van Moolenbroek 	struct lease *lease;
2957*83ee113eSDavid van Moolenbroek 	struct client_state *client_state;
2958*83ee113eSDavid van Moolenbroek 	struct option_state *in_options;
2959*83ee113eSDavid van Moolenbroek 	struct option_state *cfg_options;
2960*83ee113eSDavid van Moolenbroek 	struct binding_scope **scope;
2961*83ee113eSDavid van Moolenbroek 	struct universe *universe;
2962*83ee113eSDavid van Moolenbroek {
2963*83ee113eSDavid van Moolenbroek 	pair p, *hash;
2964*83ee113eSDavid van Moolenbroek 	int status;
2965*83ee113eSDavid van Moolenbroek 	int i;
2966*83ee113eSDavid van Moolenbroek 
2967*83ee113eSDavid van Moolenbroek 	if (universe -> index >= cfg_options -> universe_count)
2968*83ee113eSDavid van Moolenbroek 		return 0;
2969*83ee113eSDavid van Moolenbroek 
2970*83ee113eSDavid van Moolenbroek 	hash = cfg_options -> universes [universe -> index];
2971*83ee113eSDavid van Moolenbroek 	if (!hash)
2972*83ee113eSDavid van Moolenbroek 		return 0;
2973*83ee113eSDavid van Moolenbroek 
2974*83ee113eSDavid van Moolenbroek 	/* For each hash bucket, and each configured option cache within
2975*83ee113eSDavid van Moolenbroek 	 * that bucket, append the option onto the buffer in encapsulated
2976*83ee113eSDavid van Moolenbroek 	 * format appropriate to the universe.
2977*83ee113eSDavid van Moolenbroek 	 */
2978*83ee113eSDavid van Moolenbroek 	status = 0;
2979*83ee113eSDavid van Moolenbroek 	for (i = 0; i < OPTION_HASH_SIZE; i++) {
2980*83ee113eSDavid van Moolenbroek 		for (p = hash [i]; p; p = p -> cdr) {
2981*83ee113eSDavid van Moolenbroek 			if (store_option(result, universe, packet, lease,
2982*83ee113eSDavid van Moolenbroek 					 client_state, in_options, cfg_options,
2983*83ee113eSDavid van Moolenbroek 					 scope, (struct option_cache *)p->car))
2984*83ee113eSDavid van Moolenbroek 				status = 1;
2985*83ee113eSDavid van Moolenbroek 		}
2986*83ee113eSDavid van Moolenbroek 	}
2987*83ee113eSDavid van Moolenbroek 
2988*83ee113eSDavid van Moolenbroek 	if (search_subencapsulation(result, packet, lease, client_state,
2989*83ee113eSDavid van Moolenbroek 				    in_options, cfg_options, scope, universe))
2990*83ee113eSDavid van Moolenbroek 		status = 1;
2991*83ee113eSDavid van Moolenbroek 
2992*83ee113eSDavid van Moolenbroek 	return status;
2993*83ee113eSDavid van Moolenbroek }
2994*83ee113eSDavid van Moolenbroek 
nwip_option_space_encapsulate(result,packet,lease,client_state,in_options,cfg_options,scope,universe)2995*83ee113eSDavid van Moolenbroek int nwip_option_space_encapsulate (result, packet, lease, client_state,
2996*83ee113eSDavid van Moolenbroek 				   in_options, cfg_options, scope, universe)
2997*83ee113eSDavid van Moolenbroek 	struct data_string *result;
2998*83ee113eSDavid van Moolenbroek 	struct packet *packet;
2999*83ee113eSDavid van Moolenbroek 	struct lease *lease;
3000*83ee113eSDavid van Moolenbroek 	struct client_state *client_state;
3001*83ee113eSDavid van Moolenbroek 	struct option_state *in_options;
3002*83ee113eSDavid van Moolenbroek 	struct option_state *cfg_options;
3003*83ee113eSDavid van Moolenbroek 	struct binding_scope **scope;
3004*83ee113eSDavid van Moolenbroek 	struct universe *universe;
3005*83ee113eSDavid van Moolenbroek {
3006*83ee113eSDavid van Moolenbroek 	pair ocp;
3007*83ee113eSDavid van Moolenbroek 	int status;
3008*83ee113eSDavid van Moolenbroek 	static struct option_cache *no_nwip;
3009*83ee113eSDavid van Moolenbroek 	struct data_string ds;
3010*83ee113eSDavid van Moolenbroek 	struct option_chain_head *head;
3011*83ee113eSDavid van Moolenbroek 
3012*83ee113eSDavid van Moolenbroek 	if (universe -> index >= cfg_options -> universe_count)
3013*83ee113eSDavid van Moolenbroek 		return 0;
3014*83ee113eSDavid van Moolenbroek 	head = ((struct option_chain_head *)
3015*83ee113eSDavid van Moolenbroek 		cfg_options -> universes [nwip_universe.index]);
3016*83ee113eSDavid van Moolenbroek 	if (!head)
3017*83ee113eSDavid van Moolenbroek 		return 0;
3018*83ee113eSDavid van Moolenbroek 
3019*83ee113eSDavid van Moolenbroek 	status = 0;
3020*83ee113eSDavid van Moolenbroek 	for (ocp = head -> first; ocp; ocp = ocp -> cdr) {
3021*83ee113eSDavid van Moolenbroek 		if (store_option (result, universe, packet,
3022*83ee113eSDavid van Moolenbroek 				  lease, client_state, in_options,
3023*83ee113eSDavid van Moolenbroek 				  cfg_options, scope,
3024*83ee113eSDavid van Moolenbroek 				  (struct option_cache *)ocp -> car))
3025*83ee113eSDavid van Moolenbroek 			status = 1;
3026*83ee113eSDavid van Moolenbroek 	}
3027*83ee113eSDavid van Moolenbroek 
3028*83ee113eSDavid van Moolenbroek 	/* If there's no data, the nwip suboption is supposed to contain
3029*83ee113eSDavid van Moolenbroek 	   a suboption saying there's no data. */
3030*83ee113eSDavid van Moolenbroek 	if (!status) {
3031*83ee113eSDavid van Moolenbroek 		if (!no_nwip) {
3032*83ee113eSDavid van Moolenbroek 			unsigned one = 1;
3033*83ee113eSDavid van Moolenbroek 			static unsigned char nni [] = { 1, 0 };
3034*83ee113eSDavid van Moolenbroek 
3035*83ee113eSDavid van Moolenbroek 			memset (&ds, 0, sizeof ds);
3036*83ee113eSDavid van Moolenbroek 			ds.data = nni;
3037*83ee113eSDavid van Moolenbroek 			ds.len = 2;
3038*83ee113eSDavid van Moolenbroek 			if (option_cache_allocate (&no_nwip, MDL))
3039*83ee113eSDavid van Moolenbroek 				data_string_copy (&no_nwip -> data, &ds, MDL);
3040*83ee113eSDavid van Moolenbroek 			if (!option_code_hash_lookup(&no_nwip->option,
3041*83ee113eSDavid van Moolenbroek 						     nwip_universe.code_hash,
3042*83ee113eSDavid van Moolenbroek 						     &one, 0, MDL))
3043*83ee113eSDavid van Moolenbroek 				log_fatal("Nwip option hash does not contain "
3044*83ee113eSDavid van Moolenbroek 					  "1 (%s:%d).", MDL);
3045*83ee113eSDavid van Moolenbroek 		}
3046*83ee113eSDavid van Moolenbroek 		if (no_nwip) {
3047*83ee113eSDavid van Moolenbroek 			if (store_option (result, universe, packet, lease,
3048*83ee113eSDavid van Moolenbroek 					  client_state, in_options,
3049*83ee113eSDavid van Moolenbroek 					  cfg_options, scope, no_nwip))
3050*83ee113eSDavid van Moolenbroek 				status = 1;
3051*83ee113eSDavid van Moolenbroek 		}
3052*83ee113eSDavid van Moolenbroek 	} else {
3053*83ee113eSDavid van Moolenbroek 		memset (&ds, 0, sizeof ds);
3054*83ee113eSDavid van Moolenbroek 
3055*83ee113eSDavid van Moolenbroek 		/* If we have nwip options, the first one has to be the
3056*83ee113eSDavid van Moolenbroek 		   nwip-exists-in-option-area option. */
3057*83ee113eSDavid van Moolenbroek 		if (!buffer_allocate (&ds.buffer, result -> len + 2, MDL)) {
3058*83ee113eSDavid van Moolenbroek 			data_string_forget (result, MDL);
3059*83ee113eSDavid van Moolenbroek 			return 0;
3060*83ee113eSDavid van Moolenbroek 		}
3061*83ee113eSDavid van Moolenbroek 		ds.data = &ds.buffer -> data [0];
3062*83ee113eSDavid van Moolenbroek 		ds.buffer -> data [0] = 2;
3063*83ee113eSDavid van Moolenbroek 		ds.buffer -> data [1] = 0;
3064*83ee113eSDavid van Moolenbroek 		memcpy (&ds.buffer -> data [2], result -> data, result -> len);
3065*83ee113eSDavid van Moolenbroek 		data_string_forget (result, MDL);
3066*83ee113eSDavid van Moolenbroek 		data_string_copy (result, &ds, MDL);
3067*83ee113eSDavid van Moolenbroek 		data_string_forget (&ds, MDL);
3068*83ee113eSDavid van Moolenbroek 	}
3069*83ee113eSDavid van Moolenbroek 
3070*83ee113eSDavid van Moolenbroek 	return status;
3071*83ee113eSDavid van Moolenbroek }
3072*83ee113eSDavid van Moolenbroek 
3073*83ee113eSDavid van Moolenbroek /* We don't want to use ns_name_pton()...it doesn't tell us how many bytes
3074*83ee113eSDavid van Moolenbroek  * it has consumed, and it plays havoc with our escapes.
3075*83ee113eSDavid van Moolenbroek  *
3076*83ee113eSDavid van Moolenbroek  * So this function does DNS encoding, and returns either the number of
3077*83ee113eSDavid van Moolenbroek  * octects consumed (on success), or -1 on failure.
3078*83ee113eSDavid van Moolenbroek  */
3079*83ee113eSDavid van Moolenbroek static int
fqdn_encode(unsigned char * dst,int dstlen,const unsigned char * src,int srclen)3080*83ee113eSDavid van Moolenbroek fqdn_encode(unsigned char *dst, int dstlen, const unsigned char *src,
3081*83ee113eSDavid van Moolenbroek 	    int srclen)
3082*83ee113eSDavid van Moolenbroek {
3083*83ee113eSDavid van Moolenbroek 	unsigned char *out;
3084*83ee113eSDavid van Moolenbroek 	int i, j, len, outlen=0;
3085*83ee113eSDavid van Moolenbroek 
3086*83ee113eSDavid van Moolenbroek 	out = dst;
3087*83ee113eSDavid van Moolenbroek 	for (i = 0, j = 0 ; i < srclen ; i = j) {
3088*83ee113eSDavid van Moolenbroek 		while ((j < srclen) && (src[j] != '.') && (src[j] != '\0'))
3089*83ee113eSDavid van Moolenbroek 			j++;
3090*83ee113eSDavid van Moolenbroek 
3091*83ee113eSDavid van Moolenbroek 		len = j - i;
3092*83ee113eSDavid van Moolenbroek 		if ((outlen + 1 + len) > dstlen)
3093*83ee113eSDavid van Moolenbroek 			return -1;
3094*83ee113eSDavid van Moolenbroek 
3095*83ee113eSDavid van Moolenbroek 		*out++ = len;
3096*83ee113eSDavid van Moolenbroek 		outlen++;
3097*83ee113eSDavid van Moolenbroek 
3098*83ee113eSDavid van Moolenbroek 		/* We only do one FQDN, ending in one root label. */
3099*83ee113eSDavid van Moolenbroek 		if (len == 0)
3100*83ee113eSDavid van Moolenbroek 			return outlen;
3101*83ee113eSDavid van Moolenbroek 
3102*83ee113eSDavid van Moolenbroek 		memcpy(out, src + i, len);
3103*83ee113eSDavid van Moolenbroek 		out += len;
3104*83ee113eSDavid van Moolenbroek 		outlen += len;
3105*83ee113eSDavid van Moolenbroek 
3106*83ee113eSDavid van Moolenbroek 		/* Advance past the root label. */
3107*83ee113eSDavid van Moolenbroek 		j++;
3108*83ee113eSDavid van Moolenbroek 	}
3109*83ee113eSDavid van Moolenbroek 
3110*83ee113eSDavid van Moolenbroek 	if ((outlen + 1) > dstlen)
3111*83ee113eSDavid van Moolenbroek 		return -1;
3112*83ee113eSDavid van Moolenbroek 
3113*83ee113eSDavid van Moolenbroek 	/* Place the root label. */
3114*83ee113eSDavid van Moolenbroek 	*out++ = 0;
3115*83ee113eSDavid van Moolenbroek 	outlen++;
3116*83ee113eSDavid van Moolenbroek 
3117*83ee113eSDavid van Moolenbroek 	return outlen;
3118*83ee113eSDavid van Moolenbroek }
3119*83ee113eSDavid van Moolenbroek 
fqdn_option_space_encapsulate(result,packet,lease,client_state,in_options,cfg_options,scope,universe)3120*83ee113eSDavid van Moolenbroek int fqdn_option_space_encapsulate (result, packet, lease, client_state,
3121*83ee113eSDavid van Moolenbroek 				   in_options, cfg_options, scope, universe)
3122*83ee113eSDavid van Moolenbroek 	struct data_string *result;
3123*83ee113eSDavid van Moolenbroek 	struct packet *packet;
3124*83ee113eSDavid van Moolenbroek 	struct lease *lease;
3125*83ee113eSDavid van Moolenbroek 	struct client_state *client_state;
3126*83ee113eSDavid van Moolenbroek 	struct option_state *in_options;
3127*83ee113eSDavid van Moolenbroek 	struct option_state *cfg_options;
3128*83ee113eSDavid van Moolenbroek 	struct binding_scope **scope;
3129*83ee113eSDavid van Moolenbroek 	struct universe *universe;
3130*83ee113eSDavid van Moolenbroek {
3131*83ee113eSDavid van Moolenbroek 	pair ocp;
3132*83ee113eSDavid van Moolenbroek 	struct data_string results [FQDN_SUBOPTION_COUNT + 1];
3133*83ee113eSDavid van Moolenbroek 	int status = 1;
3134*83ee113eSDavid van Moolenbroek 	int i;
3135*83ee113eSDavid van Moolenbroek 	unsigned len;
3136*83ee113eSDavid van Moolenbroek 	struct buffer *bp = (struct buffer *)0;
3137*83ee113eSDavid van Moolenbroek 	struct option_chain_head *head;
3138*83ee113eSDavid van Moolenbroek 
3139*83ee113eSDavid van Moolenbroek 	/* If there's no FQDN universe, don't encapsulate. */
3140*83ee113eSDavid van Moolenbroek 	if (fqdn_universe.index >= cfg_options -> universe_count)
3141*83ee113eSDavid van Moolenbroek 		return 0;
3142*83ee113eSDavid van Moolenbroek 	head = ((struct option_chain_head *)
3143*83ee113eSDavid van Moolenbroek 		cfg_options -> universes [fqdn_universe.index]);
3144*83ee113eSDavid van Moolenbroek 	if (!head)
3145*83ee113eSDavid van Moolenbroek 		return 0;
3146*83ee113eSDavid van Moolenbroek 
3147*83ee113eSDavid van Moolenbroek 	/* Figure out the values of all the suboptions. */
3148*83ee113eSDavid van Moolenbroek 	memset (results, 0, sizeof results);
3149*83ee113eSDavid van Moolenbroek 	for (ocp = head -> first; ocp; ocp = ocp -> cdr) {
3150*83ee113eSDavid van Moolenbroek 		struct option_cache *oc = (struct option_cache *)(ocp -> car);
3151*83ee113eSDavid van Moolenbroek 		if (oc -> option -> code > FQDN_SUBOPTION_COUNT)
3152*83ee113eSDavid van Moolenbroek 			continue;
3153*83ee113eSDavid van Moolenbroek 		/* No need to check the return code, we check the length later */
3154*83ee113eSDavid van Moolenbroek 		(void) evaluate_option_cache (&results[oc->option->code],
3155*83ee113eSDavid van Moolenbroek 					      packet, lease, client_state,
3156*83ee113eSDavid van Moolenbroek 					      in_options, cfg_options, scope,
3157*83ee113eSDavid van Moolenbroek 					      oc, MDL);
3158*83ee113eSDavid van Moolenbroek 	}
3159*83ee113eSDavid van Moolenbroek 	/* We add a byte for the flags field.
3160*83ee113eSDavid van Moolenbroek 	 * We add two bytes for the two RCODE fields.
3161*83ee113eSDavid van Moolenbroek 	 * We add a byte because we will prepend a label count.
3162*83ee113eSDavid van Moolenbroek 	 * We add a byte because the input len doesn't count null termination,
3163*83ee113eSDavid van Moolenbroek 	 * and we will add a root label.
3164*83ee113eSDavid van Moolenbroek 	 */
3165*83ee113eSDavid van Moolenbroek 	len = 5 + results [FQDN_FQDN].len;
3166*83ee113eSDavid van Moolenbroek 	/* Save the contents of the option in a buffer. */
3167*83ee113eSDavid van Moolenbroek 	if (!buffer_allocate (&bp, len, MDL)) {
3168*83ee113eSDavid van Moolenbroek 		log_error ("no memory for option buffer.");
3169*83ee113eSDavid van Moolenbroek 		status = 0;
3170*83ee113eSDavid van Moolenbroek 		goto exit;
3171*83ee113eSDavid van Moolenbroek 	}
3172*83ee113eSDavid van Moolenbroek 	buffer_reference (&result -> buffer, bp, MDL);
3173*83ee113eSDavid van Moolenbroek 	result -> len = 3;
3174*83ee113eSDavid van Moolenbroek 	result -> data = &bp -> data [0];
3175*83ee113eSDavid van Moolenbroek 
3176*83ee113eSDavid van Moolenbroek 	memset (&bp -> data [0], 0, len);
3177*83ee113eSDavid van Moolenbroek 	/* XXX: The server should set bit 4 (yes, 4, not 3) to 1 if it is
3178*83ee113eSDavid van Moolenbroek 	 * not going to perform any ddns updates.  The client should set the
3179*83ee113eSDavid van Moolenbroek 	 * bit if it doesn't want the server to perform any updates.
3180*83ee113eSDavid van Moolenbroek 	 * The problem is at this layer of abstraction we have no idea if
3181*83ee113eSDavid van Moolenbroek 	 * the caller is a client or server.
3182*83ee113eSDavid van Moolenbroek 	 *
3183*83ee113eSDavid van Moolenbroek 	 * See RFC4702, Section 3.1, 'The "N" bit'.
3184*83ee113eSDavid van Moolenbroek 	 *
3185*83ee113eSDavid van Moolenbroek 	 * if (?)
3186*83ee113eSDavid van Moolenbroek 	 *	bp->data[0] |= 8;
3187*83ee113eSDavid van Moolenbroek 	 */
3188*83ee113eSDavid van Moolenbroek 	if (results [FQDN_NO_CLIENT_UPDATE].len &&
3189*83ee113eSDavid van Moolenbroek 	    results [FQDN_NO_CLIENT_UPDATE].data [0])
3190*83ee113eSDavid van Moolenbroek 		bp -> data [0] |= 2;
3191*83ee113eSDavid van Moolenbroek 	if (results [FQDN_SERVER_UPDATE].len &&
3192*83ee113eSDavid van Moolenbroek 	    results [FQDN_SERVER_UPDATE].data [0])
3193*83ee113eSDavid van Moolenbroek 		bp -> data [0] |= 1;
3194*83ee113eSDavid van Moolenbroek 	if (results [FQDN_RCODE1].len)
3195*83ee113eSDavid van Moolenbroek 		bp -> data [1] = results [FQDN_RCODE1].data [0];
3196*83ee113eSDavid van Moolenbroek 	if (results [FQDN_RCODE2].len)
3197*83ee113eSDavid van Moolenbroek 		bp -> data [2] = results [FQDN_RCODE2].data [0];
3198*83ee113eSDavid van Moolenbroek 
3199*83ee113eSDavid van Moolenbroek 	if (results [FQDN_ENCODED].len &&
3200*83ee113eSDavid van Moolenbroek 	    results [FQDN_ENCODED].data [0]) {
3201*83ee113eSDavid van Moolenbroek 		bp->data[0] |= 4;
3202*83ee113eSDavid van Moolenbroek 		if (results [FQDN_FQDN].len) {
3203*83ee113eSDavid van Moolenbroek 			i = fqdn_encode(&bp->data[3], len - 3,
3204*83ee113eSDavid van Moolenbroek 					results[FQDN_FQDN].data,
3205*83ee113eSDavid van Moolenbroek 					results[FQDN_FQDN].len);
3206*83ee113eSDavid van Moolenbroek 
3207*83ee113eSDavid van Moolenbroek 			if (i < 0) {
3208*83ee113eSDavid van Moolenbroek 				status = 0;
3209*83ee113eSDavid van Moolenbroek 				goto exit;
3210*83ee113eSDavid van Moolenbroek 			}
3211*83ee113eSDavid van Moolenbroek 
3212*83ee113eSDavid van Moolenbroek 			result->len += i;
3213*83ee113eSDavid van Moolenbroek 			result->terminated = 0;
3214*83ee113eSDavid van Moolenbroek 		}
3215*83ee113eSDavid van Moolenbroek 	} else {
3216*83ee113eSDavid van Moolenbroek 		if (results [FQDN_FQDN].len) {
3217*83ee113eSDavid van Moolenbroek 			memcpy (&bp -> data [3], results [FQDN_FQDN].data,
3218*83ee113eSDavid van Moolenbroek 				results [FQDN_FQDN].len);
3219*83ee113eSDavid van Moolenbroek 			result -> len += results [FQDN_FQDN].len;
3220*83ee113eSDavid van Moolenbroek 			result -> terminated = 0;
3221*83ee113eSDavid van Moolenbroek 		}
3222*83ee113eSDavid van Moolenbroek 	}
3223*83ee113eSDavid van Moolenbroek       exit:
3224*83ee113eSDavid van Moolenbroek 	for (i = 1; i <= FQDN_SUBOPTION_COUNT; i++) {
3225*83ee113eSDavid van Moolenbroek 		if (results [i].len)
3226*83ee113eSDavid van Moolenbroek 			data_string_forget (&results [i], MDL);
3227*83ee113eSDavid van Moolenbroek 	}
3228*83ee113eSDavid van Moolenbroek 	buffer_dereference (&bp, MDL);
3229*83ee113eSDavid van Moolenbroek 	if (!status)
3230*83ee113eSDavid van Moolenbroek 		data_string_forget(result, MDL);
3231*83ee113eSDavid van Moolenbroek 	return status;
3232*83ee113eSDavid van Moolenbroek }
3233*83ee113eSDavid van Moolenbroek 
3234*83ee113eSDavid van Moolenbroek /*
3235*83ee113eSDavid van Moolenbroek  * Trap invalid attempts to inspect FQND6 contents.
3236*83ee113eSDavid van Moolenbroek  */
3237*83ee113eSDavid van Moolenbroek struct option_cache *
lookup_fqdn6_option(struct universe * universe,struct option_state * options,unsigned code)3238*83ee113eSDavid van Moolenbroek lookup_fqdn6_option(struct universe *universe, struct option_state *options,
3239*83ee113eSDavid van Moolenbroek 		    unsigned code)
3240*83ee113eSDavid van Moolenbroek {
3241*83ee113eSDavid van Moolenbroek 	log_fatal("Impossible condition at %s:%d.", MDL);
3242*83ee113eSDavid van Moolenbroek 	return NULL;
3243*83ee113eSDavid van Moolenbroek }
3244*83ee113eSDavid van Moolenbroek 
3245*83ee113eSDavid van Moolenbroek /*
3246*83ee113eSDavid van Moolenbroek  * Trap invalid attempts to save options directly to FQDN6 rather than FQDN.
3247*83ee113eSDavid van Moolenbroek  */
3248*83ee113eSDavid van Moolenbroek void
save_fqdn6_option(struct universe * universe,struct option_state * options,struct option_cache * oc,isc_boolean_t appendp)3249*83ee113eSDavid van Moolenbroek save_fqdn6_option(struct universe *universe, struct option_state *options,
3250*83ee113eSDavid van Moolenbroek 		  struct option_cache *oc, isc_boolean_t appendp)
3251*83ee113eSDavid van Moolenbroek {
3252*83ee113eSDavid van Moolenbroek 	log_fatal("Impossible condition at %s:%d.", MDL);
3253*83ee113eSDavid van Moolenbroek }
3254*83ee113eSDavid van Moolenbroek 
3255*83ee113eSDavid van Moolenbroek /*
3256*83ee113eSDavid van Moolenbroek  * Trap invalid attempts to delete an option out of the FQDN6 universe.
3257*83ee113eSDavid van Moolenbroek  */
3258*83ee113eSDavid van Moolenbroek void
delete_fqdn6_option(struct universe * universe,struct option_state * options,int code)3259*83ee113eSDavid van Moolenbroek delete_fqdn6_option(struct universe *universe, struct option_state *options,
3260*83ee113eSDavid van Moolenbroek 		    int code)
3261*83ee113eSDavid van Moolenbroek {
3262*83ee113eSDavid van Moolenbroek 	log_fatal("Impossible condition at %s:%d.", MDL);
3263*83ee113eSDavid van Moolenbroek }
3264*83ee113eSDavid van Moolenbroek 
3265*83ee113eSDavid van Moolenbroek /* Shill to the DHCPv4 fqdn option cache any attempts to traverse the
3266*83ee113eSDavid van Moolenbroek  * V6's option cache entry.
3267*83ee113eSDavid van Moolenbroek  *
3268*83ee113eSDavid van Moolenbroek  * This function is called speculatively by dhclient to setup
3269*83ee113eSDavid van Moolenbroek  * environment variables.  But it would have already called the
3270*83ee113eSDavid van Moolenbroek  * foreach on the normal fqdn universe, so this is superfluous.
3271*83ee113eSDavid van Moolenbroek  */
3272*83ee113eSDavid van Moolenbroek void
fqdn6_option_space_foreach(struct packet * packet,struct lease * lease,struct client_state * client_state,struct option_state * in_options,struct option_state * cfg_options,struct binding_scope ** scope,struct universe * u,void * stuff,void (* func)(struct option_cache *,struct packet *,struct lease *,struct client_state *,struct option_state *,struct option_state *,struct binding_scope **,struct universe *,void *))3273*83ee113eSDavid van Moolenbroek fqdn6_option_space_foreach(struct packet *packet, struct lease *lease,
3274*83ee113eSDavid van Moolenbroek 			   struct client_state *client_state,
3275*83ee113eSDavid van Moolenbroek 			   struct option_state *in_options,
3276*83ee113eSDavid van Moolenbroek 			   struct option_state *cfg_options,
3277*83ee113eSDavid van Moolenbroek 			   struct binding_scope **scope,
3278*83ee113eSDavid van Moolenbroek 			   struct universe *u, void *stuff,
3279*83ee113eSDavid van Moolenbroek 			   void (*func)(struct option_cache *,
3280*83ee113eSDavid van Moolenbroek 					struct packet *,
3281*83ee113eSDavid van Moolenbroek 					struct lease *,
3282*83ee113eSDavid van Moolenbroek 					struct client_state *,
3283*83ee113eSDavid van Moolenbroek 					struct option_state *,
3284*83ee113eSDavid van Moolenbroek 					struct option_state *,
3285*83ee113eSDavid van Moolenbroek 					struct binding_scope **,
3286*83ee113eSDavid van Moolenbroek 					struct universe *, void *))
3287*83ee113eSDavid van Moolenbroek {
3288*83ee113eSDavid van Moolenbroek 	/* Pretend it is empty. */
3289*83ee113eSDavid van Moolenbroek 	return;
3290*83ee113eSDavid van Moolenbroek }
3291*83ee113eSDavid van Moolenbroek 
3292*83ee113eSDavid van Moolenbroek /* Turn the FQDN option space into a DHCPv6 FQDN option buffer.
3293*83ee113eSDavid van Moolenbroek  */
3294*83ee113eSDavid van Moolenbroek int
fqdn6_option_space_encapsulate(struct data_string * result,struct packet * packet,struct lease * lease,struct client_state * client_state,struct option_state * in_options,struct option_state * cfg_options,struct binding_scope ** scope,struct universe * universe)3295*83ee113eSDavid van Moolenbroek fqdn6_option_space_encapsulate(struct data_string *result,
3296*83ee113eSDavid van Moolenbroek 			       struct packet *packet, struct lease *lease,
3297*83ee113eSDavid van Moolenbroek 			       struct client_state *client_state,
3298*83ee113eSDavid van Moolenbroek 			       struct option_state *in_options,
3299*83ee113eSDavid van Moolenbroek 			       struct option_state *cfg_options,
3300*83ee113eSDavid van Moolenbroek 			       struct binding_scope **scope,
3301*83ee113eSDavid van Moolenbroek 			       struct universe *universe)
3302*83ee113eSDavid van Moolenbroek {
3303*83ee113eSDavid van Moolenbroek 	pair ocp;
3304*83ee113eSDavid van Moolenbroek 	struct option_chain_head *head;
3305*83ee113eSDavid van Moolenbroek 	struct option_cache *oc;
3306*83ee113eSDavid van Moolenbroek 	unsigned char *data;
3307*83ee113eSDavid van Moolenbroek 	int i, len, rval = 0, count;
3308*83ee113eSDavid van Moolenbroek 	struct data_string results[FQDN_SUBOPTION_COUNT + 1];
3309*83ee113eSDavid van Moolenbroek 
3310*83ee113eSDavid van Moolenbroek 	if (fqdn_universe.index >= cfg_options->universe_count)
3311*83ee113eSDavid van Moolenbroek 		return 0;
3312*83ee113eSDavid van Moolenbroek 	head = ((struct option_chain_head *)
3313*83ee113eSDavid van Moolenbroek 		cfg_options->universes[fqdn_universe.index]);
3314*83ee113eSDavid van Moolenbroek 	if (head == NULL)
3315*83ee113eSDavid van Moolenbroek 		return 0;
3316*83ee113eSDavid van Moolenbroek 
3317*83ee113eSDavid van Moolenbroek 	memset(results, 0, sizeof(results));
3318*83ee113eSDavid van Moolenbroek 	for (ocp = head->first ; ocp != NULL ; ocp = ocp->cdr) {
3319*83ee113eSDavid van Moolenbroek 		oc = (struct option_cache *)(ocp->car);
3320*83ee113eSDavid van Moolenbroek 		if (oc->option->code > FQDN_SUBOPTION_COUNT)
3321*83ee113eSDavid van Moolenbroek 			log_fatal("Impossible condition at %s:%d.", MDL);
3322*83ee113eSDavid van Moolenbroek 		/* No need to check the return code, we check the length later */
3323*83ee113eSDavid van Moolenbroek 		(void) evaluate_option_cache(&results[oc->option->code], packet,
3324*83ee113eSDavid van Moolenbroek 					     lease, client_state, in_options,
3325*83ee113eSDavid van Moolenbroek 					     cfg_options, scope, oc, MDL);
3326*83ee113eSDavid van Moolenbroek 	}
3327*83ee113eSDavid van Moolenbroek 
3328*83ee113eSDavid van Moolenbroek 	/* We add a byte for the flags field at the start of the option.
3329*83ee113eSDavid van Moolenbroek 	 * We add a byte because we will prepend a label count.
3330*83ee113eSDavid van Moolenbroek 	 * We add a byte because the input length doesn't include a trailing
3331*83ee113eSDavid van Moolenbroek 	 * NULL, and we will add a root label.
3332*83ee113eSDavid van Moolenbroek 	 */
3333*83ee113eSDavid van Moolenbroek 	len = results[FQDN_FQDN].len + 3;
3334*83ee113eSDavid van Moolenbroek 	if (!buffer_allocate(&result->buffer, len, MDL)) {
3335*83ee113eSDavid van Moolenbroek 		log_error("No memory for virtual option buffer.");
3336*83ee113eSDavid van Moolenbroek 		goto exit;
3337*83ee113eSDavid van Moolenbroek 	}
3338*83ee113eSDavid van Moolenbroek 	data = result->buffer->data;
3339*83ee113eSDavid van Moolenbroek 	result->data = data;
3340*83ee113eSDavid van Moolenbroek 
3341*83ee113eSDavid van Moolenbroek 	/* The first byte is the flags field. */
3342*83ee113eSDavid van Moolenbroek 	result->len = 1;
3343*83ee113eSDavid van Moolenbroek 	data[0] = 0;
3344*83ee113eSDavid van Moolenbroek 	/* XXX: The server should set bit 3 (yes, 3, not 4) to 1 if we
3345*83ee113eSDavid van Moolenbroek 	 * are not going to perform any DNS updates.  The problem is
3346*83ee113eSDavid van Moolenbroek 	 * that at this layer of abstraction, we do not know if the caller
3347*83ee113eSDavid van Moolenbroek 	 * is the client or the server.
3348*83ee113eSDavid van Moolenbroek 	 *
3349*83ee113eSDavid van Moolenbroek 	 * See RFC4704 Section 4.1, 'The "N" bit'.
3350*83ee113eSDavid van Moolenbroek 	 *
3351*83ee113eSDavid van Moolenbroek 	 * if (?)
3352*83ee113eSDavid van Moolenbroek 	 *	data[0] |= 4;
3353*83ee113eSDavid van Moolenbroek 	 */
3354*83ee113eSDavid van Moolenbroek 	if (results[FQDN_NO_CLIENT_UPDATE].len &&
3355*83ee113eSDavid van Moolenbroek 	    results[FQDN_NO_CLIENT_UPDATE].data[0])
3356*83ee113eSDavid van Moolenbroek 		data[0] |= 2;
3357*83ee113eSDavid van Moolenbroek 	if (results[FQDN_SERVER_UPDATE].len &&
3358*83ee113eSDavid van Moolenbroek 	    results[FQDN_SERVER_UPDATE].data[0])
3359*83ee113eSDavid van Moolenbroek 		data[0] |= 1;
3360*83ee113eSDavid van Moolenbroek 
3361*83ee113eSDavid van Moolenbroek 	/* If there is no name, we're done. */
3362*83ee113eSDavid van Moolenbroek 	if (results[FQDN_FQDN].len == 0) {
3363*83ee113eSDavid van Moolenbroek 		rval = 1;
3364*83ee113eSDavid van Moolenbroek 		goto exit;
3365*83ee113eSDavid van Moolenbroek 	}
3366*83ee113eSDavid van Moolenbroek 
3367*83ee113eSDavid van Moolenbroek 	/* Convert textual representation to DNS format. */
3368*83ee113eSDavid van Moolenbroek 	count = fqdn_encode(data + 1, len - 1,
3369*83ee113eSDavid van Moolenbroek 			    results[FQDN_FQDN].data, results[FQDN_FQDN].len);
3370*83ee113eSDavid van Moolenbroek 
3371*83ee113eSDavid van Moolenbroek 	if (count < 0) {
3372*83ee113eSDavid van Moolenbroek 		rval = 0;
3373*83ee113eSDavid van Moolenbroek 		data_string_forget(result, MDL);
3374*83ee113eSDavid van Moolenbroek 		goto exit;
3375*83ee113eSDavid van Moolenbroek 	}
3376*83ee113eSDavid van Moolenbroek 
3377*83ee113eSDavid van Moolenbroek 	result->len += count;
3378*83ee113eSDavid van Moolenbroek 	result->terminated = 0;
3379*83ee113eSDavid van Moolenbroek 
3380*83ee113eSDavid van Moolenbroek 	/* Success! */
3381*83ee113eSDavid van Moolenbroek 	rval = 1;
3382*83ee113eSDavid van Moolenbroek 
3383*83ee113eSDavid van Moolenbroek       exit:
3384*83ee113eSDavid van Moolenbroek 	for (i = 1 ; i <= FQDN_SUBOPTION_COUNT ; i++) {
3385*83ee113eSDavid van Moolenbroek 		if (result[i].len)
3386*83ee113eSDavid van Moolenbroek 			data_string_forget(&results[i], MDL);
3387*83ee113eSDavid van Moolenbroek 	}
3388*83ee113eSDavid van Moolenbroek 
3389*83ee113eSDavid van Moolenbroek 	return rval;
3390*83ee113eSDavid van Moolenbroek }
3391*83ee113eSDavid van Moolenbroek 
3392*83ee113eSDavid van Moolenbroek /* Read the DHCPv6 FQDN option's contents into the FQDN virtual space.
3393*83ee113eSDavid van Moolenbroek  */
3394*83ee113eSDavid van Moolenbroek int
fqdn6_universe_decode(struct option_state * options,const unsigned char * buffer,unsigned length,struct universe * u)3395*83ee113eSDavid van Moolenbroek fqdn6_universe_decode(struct option_state *options,
3396*83ee113eSDavid van Moolenbroek 		      const unsigned char *buffer, unsigned length,
3397*83ee113eSDavid van Moolenbroek 		      struct universe *u)
3398*83ee113eSDavid van Moolenbroek {
3399*83ee113eSDavid van Moolenbroek 	struct buffer *bp = NULL;
3400*83ee113eSDavid van Moolenbroek 	unsigned char *first_dot;
3401*83ee113eSDavid van Moolenbroek 	int len, hlen, dlen;
3402*83ee113eSDavid van Moolenbroek 
3403*83ee113eSDavid van Moolenbroek 	/* The FQDN option has to be at least 1 byte long. */
3404*83ee113eSDavid van Moolenbroek 	if (length < 1)
3405*83ee113eSDavid van Moolenbroek 		return 0;
3406*83ee113eSDavid van Moolenbroek 
3407*83ee113eSDavid van Moolenbroek 	/* Save the contents of the option in a buffer.  There are 3
3408*83ee113eSDavid van Moolenbroek 	 * one-byte values we record from the packet, so we go ahead
3409*83ee113eSDavid van Moolenbroek 	 * and allocate a bigger buffer to accommodate them.  But the
3410*83ee113eSDavid van Moolenbroek 	 * 'length' we got (because it is a DNS encoded string) is
3411*83ee113eSDavid van Moolenbroek 	 * one longer than we need...so we only add two extra octets.
3412*83ee113eSDavid van Moolenbroek 	 */
3413*83ee113eSDavid van Moolenbroek 	if (!buffer_allocate(&bp, length + 2, MDL)) {
3414*83ee113eSDavid van Moolenbroek 		log_error("No memory for dhcp6.fqdn option buffer.");
3415*83ee113eSDavid van Moolenbroek 		return 0;
3416*83ee113eSDavid van Moolenbroek 	}
3417*83ee113eSDavid van Moolenbroek 
3418*83ee113eSDavid van Moolenbroek 	/* The v6 FQDN is always 'encoded' per DNS. */
3419*83ee113eSDavid van Moolenbroek 	bp->data[0] = 1;
3420*83ee113eSDavid van Moolenbroek 	if (!save_option_buffer(&fqdn_universe, options, bp,
3421*83ee113eSDavid van Moolenbroek 				bp->data, 1, FQDN_ENCODED, 0))
3422*83ee113eSDavid van Moolenbroek 		goto error;
3423*83ee113eSDavid van Moolenbroek 
3424*83ee113eSDavid van Moolenbroek 	/* XXX: We need to process 'The "N" bit'. */
3425*83ee113eSDavid van Moolenbroek 
3426*83ee113eSDavid van Moolenbroek 	if (buffer[0] & 1) /* server-update. */
3427*83ee113eSDavid van Moolenbroek 		bp->data[2] = 1;
3428*83ee113eSDavid van Moolenbroek 	else
3429*83ee113eSDavid van Moolenbroek 		bp->data[2] = 0;
3430*83ee113eSDavid van Moolenbroek 
3431*83ee113eSDavid van Moolenbroek 	if (!save_option_buffer(&fqdn_universe, options, bp, bp->data + 2, 1,
3432*83ee113eSDavid van Moolenbroek 				FQDN_SERVER_UPDATE, 0))
3433*83ee113eSDavid van Moolenbroek 		goto error;
3434*83ee113eSDavid van Moolenbroek 
3435*83ee113eSDavid van Moolenbroek 	if (buffer[0] & 2) /* no-client-update. */
3436*83ee113eSDavid van Moolenbroek 		bp->data[1] = 1;
3437*83ee113eSDavid van Moolenbroek 	else
3438*83ee113eSDavid van Moolenbroek 		bp->data[1] = 0;
3439*83ee113eSDavid van Moolenbroek 
3440*83ee113eSDavid van Moolenbroek 	if (!save_option_buffer(&fqdn_universe, options, bp, bp->data + 1, 1,
3441*83ee113eSDavid van Moolenbroek 				FQDN_NO_CLIENT_UPDATE, 0))
3442*83ee113eSDavid van Moolenbroek 		goto error;
3443*83ee113eSDavid van Moolenbroek 
3444*83ee113eSDavid van Moolenbroek 	/* Convert the domain name to textual representation for config. */
3445*83ee113eSDavid van Moolenbroek 	len = MRns_name_ntop(buffer + 1, (char *)bp->data + 3, length - 1);
3446*83ee113eSDavid van Moolenbroek 	if (len == -1) {
3447*83ee113eSDavid van Moolenbroek 		log_error("Unable to convert dhcp6.fqdn domain name to "
3448*83ee113eSDavid van Moolenbroek 			  "printable form.");
3449*83ee113eSDavid van Moolenbroek 		goto error;
3450*83ee113eSDavid van Moolenbroek 	}
3451*83ee113eSDavid van Moolenbroek 
3452*83ee113eSDavid van Moolenbroek 	/* Save the domain name. */
3453*83ee113eSDavid van Moolenbroek 	if (len > 0) {
3454*83ee113eSDavid van Moolenbroek 		unsigned char *fqdn_start = bp->data + 3;
3455*83ee113eSDavid van Moolenbroek 
3456*83ee113eSDavid van Moolenbroek 		if (!save_option_buffer(&fqdn_universe, options, bp,
3457*83ee113eSDavid van Moolenbroek 					fqdn_start, len, FQDN_FQDN, 1))
3458*83ee113eSDavid van Moolenbroek 			goto error;
3459*83ee113eSDavid van Moolenbroek 
3460*83ee113eSDavid van Moolenbroek 		first_dot = (unsigned char *)strchr((char *)fqdn_start, '.');
3461*83ee113eSDavid van Moolenbroek 
3462*83ee113eSDavid van Moolenbroek 		if (first_dot != NULL) {
3463*83ee113eSDavid van Moolenbroek 			hlen = first_dot - fqdn_start;
3464*83ee113eSDavid van Moolenbroek 			dlen = len - hlen;
3465*83ee113eSDavid van Moolenbroek 		} else {
3466*83ee113eSDavid van Moolenbroek 			hlen = len;
3467*83ee113eSDavid van Moolenbroek 			dlen = 0;
3468*83ee113eSDavid van Moolenbroek 		}
3469*83ee113eSDavid van Moolenbroek 
3470*83ee113eSDavid van Moolenbroek 		if (!save_option_buffer(&fqdn_universe, options, bp,
3471*83ee113eSDavid van Moolenbroek 					fqdn_start, len, FQDN_FQDN, 1) ||
3472*83ee113eSDavid van Moolenbroek 		    ((hlen > 0) &&
3473*83ee113eSDavid van Moolenbroek 		     !save_option_buffer(&fqdn_universe, options, bp,
3474*83ee113eSDavid van Moolenbroek 					 fqdn_start, hlen,
3475*83ee113eSDavid van Moolenbroek 					 FQDN_HOSTNAME, 0)) ||
3476*83ee113eSDavid van Moolenbroek 		    ((dlen > 0) &&
3477*83ee113eSDavid van Moolenbroek 		     !save_option_buffer(&fqdn_universe, options, bp,
3478*83ee113eSDavid van Moolenbroek 					 first_dot, dlen, FQDN_DOMAINNAME, 0)))
3479*83ee113eSDavid van Moolenbroek 				goto error;
3480*83ee113eSDavid van Moolenbroek 	}
3481*83ee113eSDavid van Moolenbroek 
3482*83ee113eSDavid van Moolenbroek 	buffer_dereference(&bp, MDL);
3483*83ee113eSDavid van Moolenbroek 	return 1;
3484*83ee113eSDavid van Moolenbroek 
3485*83ee113eSDavid van Moolenbroek       error:
3486*83ee113eSDavid van Moolenbroek 	buffer_dereference(&bp, MDL);
3487*83ee113eSDavid van Moolenbroek 	return 0;
3488*83ee113eSDavid van Moolenbroek }
3489*83ee113eSDavid van Moolenbroek 
option_space_foreach(struct packet * packet,struct lease * lease,struct client_state * client_state,struct option_state * in_options,struct option_state * cfg_options,struct binding_scope ** scope,struct universe * u,void * stuff,void (* func)(struct option_cache *,struct packet *,struct lease *,struct client_state *,struct option_state *,struct option_state *,struct binding_scope **,struct universe *,void *))3490*83ee113eSDavid van Moolenbroek void option_space_foreach (struct packet *packet, struct lease *lease,
3491*83ee113eSDavid van Moolenbroek 			   struct client_state *client_state,
3492*83ee113eSDavid van Moolenbroek 			   struct option_state *in_options,
3493*83ee113eSDavid van Moolenbroek 			   struct option_state *cfg_options,
3494*83ee113eSDavid van Moolenbroek 			   struct binding_scope **scope,
3495*83ee113eSDavid van Moolenbroek 			   struct universe *u, void *stuff,
3496*83ee113eSDavid van Moolenbroek 			   void (*func) (struct option_cache *,
3497*83ee113eSDavid van Moolenbroek 					 struct packet *,
3498*83ee113eSDavid van Moolenbroek 					 struct lease *, struct client_state *,
3499*83ee113eSDavid van Moolenbroek 					 struct option_state *,
3500*83ee113eSDavid van Moolenbroek 					 struct option_state *,
3501*83ee113eSDavid van Moolenbroek 					 struct binding_scope **,
3502*83ee113eSDavid van Moolenbroek 					 struct universe *, void *))
3503*83ee113eSDavid van Moolenbroek {
3504*83ee113eSDavid van Moolenbroek 	if (u -> foreach)
3505*83ee113eSDavid van Moolenbroek 		(*u -> foreach) (packet, lease, client_state, in_options,
3506*83ee113eSDavid van Moolenbroek 				 cfg_options, scope, u, stuff, func);
3507*83ee113eSDavid van Moolenbroek }
3508*83ee113eSDavid van Moolenbroek 
suboption_foreach(struct packet * packet,struct lease * lease,struct client_state * client_state,struct option_state * in_options,struct option_state * cfg_options,struct binding_scope ** scope,struct universe * u,void * stuff,void (* func)(struct option_cache *,struct packet *,struct lease *,struct client_state *,struct option_state *,struct option_state *,struct binding_scope **,struct universe *,void *),struct option_cache * oc,const char * vsname)3509*83ee113eSDavid van Moolenbroek void suboption_foreach (struct packet *packet, struct lease *lease,
3510*83ee113eSDavid van Moolenbroek 			struct client_state *client_state,
3511*83ee113eSDavid van Moolenbroek 			struct option_state *in_options,
3512*83ee113eSDavid van Moolenbroek 			struct option_state *cfg_options,
3513*83ee113eSDavid van Moolenbroek 			struct binding_scope **scope,
3514*83ee113eSDavid van Moolenbroek 			struct universe *u, void *stuff,
3515*83ee113eSDavid van Moolenbroek 			void (*func) (struct option_cache *,
3516*83ee113eSDavid van Moolenbroek 				      struct packet *,
3517*83ee113eSDavid van Moolenbroek 				      struct lease *, struct client_state *,
3518*83ee113eSDavid van Moolenbroek 				      struct option_state *,
3519*83ee113eSDavid van Moolenbroek 				      struct option_state *,
3520*83ee113eSDavid van Moolenbroek 				      struct binding_scope **,
3521*83ee113eSDavid van Moolenbroek 				      struct universe *, void *),
3522*83ee113eSDavid van Moolenbroek 			struct option_cache *oc,
3523*83ee113eSDavid van Moolenbroek 			const char *vsname)
3524*83ee113eSDavid van Moolenbroek {
3525*83ee113eSDavid van Moolenbroek 	struct universe *universe = find_option_universe (oc -> option,
3526*83ee113eSDavid van Moolenbroek 							  vsname);
3527*83ee113eSDavid van Moolenbroek 	if (universe -> foreach)
3528*83ee113eSDavid van Moolenbroek 		(*universe -> foreach) (packet, lease, client_state,
3529*83ee113eSDavid van Moolenbroek 					in_options, cfg_options,
3530*83ee113eSDavid van Moolenbroek 					scope, universe, stuff, func);
3531*83ee113eSDavid van Moolenbroek }
3532*83ee113eSDavid van Moolenbroek 
hashed_option_space_foreach(struct packet * packet,struct lease * lease,struct client_state * client_state,struct option_state * in_options,struct option_state * cfg_options,struct binding_scope ** scope,struct universe * u,void * stuff,void (* func)(struct option_cache *,struct packet *,struct lease *,struct client_state *,struct option_state *,struct option_state *,struct binding_scope **,struct universe *,void *))3533*83ee113eSDavid van Moolenbroek void hashed_option_space_foreach (struct packet *packet, struct lease *lease,
3534*83ee113eSDavid van Moolenbroek 				  struct client_state *client_state,
3535*83ee113eSDavid van Moolenbroek 				  struct option_state *in_options,
3536*83ee113eSDavid van Moolenbroek 				  struct option_state *cfg_options,
3537*83ee113eSDavid van Moolenbroek 				  struct binding_scope **scope,
3538*83ee113eSDavid van Moolenbroek 				  struct universe *u, void *stuff,
3539*83ee113eSDavid van Moolenbroek 				  void (*func) (struct option_cache *,
3540*83ee113eSDavid van Moolenbroek 						struct packet *,
3541*83ee113eSDavid van Moolenbroek 						struct lease *,
3542*83ee113eSDavid van Moolenbroek 						struct client_state *,
3543*83ee113eSDavid van Moolenbroek 						struct option_state *,
3544*83ee113eSDavid van Moolenbroek 						struct option_state *,
3545*83ee113eSDavid van Moolenbroek 						struct binding_scope **,
3546*83ee113eSDavid van Moolenbroek 						struct universe *, void *))
3547*83ee113eSDavid van Moolenbroek {
3548*83ee113eSDavid van Moolenbroek 	pair *hash;
3549*83ee113eSDavid van Moolenbroek 	int i;
3550*83ee113eSDavid van Moolenbroek 	struct option_cache *oc;
3551*83ee113eSDavid van Moolenbroek 
3552*83ee113eSDavid van Moolenbroek 	if (cfg_options -> universe_count <= u -> index)
3553*83ee113eSDavid van Moolenbroek 		return;
3554*83ee113eSDavid van Moolenbroek 
3555*83ee113eSDavid van Moolenbroek 	hash = cfg_options -> universes [u -> index];
3556*83ee113eSDavid van Moolenbroek 	if (!hash)
3557*83ee113eSDavid van Moolenbroek 		return;
3558*83ee113eSDavid van Moolenbroek 	for (i = 0; i < OPTION_HASH_SIZE; i++) {
3559*83ee113eSDavid van Moolenbroek 		pair p;
3560*83ee113eSDavid van Moolenbroek 		/* XXX save _all_ options! XXX */
3561*83ee113eSDavid van Moolenbroek 		for (p = hash [i]; p; p = p -> cdr) {
3562*83ee113eSDavid van Moolenbroek 			oc = (struct option_cache *)p -> car;
3563*83ee113eSDavid van Moolenbroek 			(*func) (oc, packet, lease, client_state,
3564*83ee113eSDavid van Moolenbroek 				 in_options, cfg_options, scope, u, stuff);
3565*83ee113eSDavid van Moolenbroek 		}
3566*83ee113eSDavid van Moolenbroek 	}
3567*83ee113eSDavid van Moolenbroek }
3568*83ee113eSDavid van Moolenbroek 
3569*83ee113eSDavid van Moolenbroek void
save_linked_option(struct universe * universe,struct option_state * options,struct option_cache * oc,isc_boolean_t appendp)3570*83ee113eSDavid van Moolenbroek save_linked_option(struct universe *universe, struct option_state *options,
3571*83ee113eSDavid van Moolenbroek 		   struct option_cache *oc, isc_boolean_t appendp)
3572*83ee113eSDavid van Moolenbroek {
3573*83ee113eSDavid van Moolenbroek 	pair *tail;
3574*83ee113eSDavid van Moolenbroek 	struct option_chain_head *head;
3575*83ee113eSDavid van Moolenbroek 	struct option_cache **ocloc;
3576*83ee113eSDavid van Moolenbroek 
3577*83ee113eSDavid van Moolenbroek 	if (universe -> index >= options -> universe_count)
3578*83ee113eSDavid van Moolenbroek 		return;
3579*83ee113eSDavid van Moolenbroek 	head = ((struct option_chain_head *)
3580*83ee113eSDavid van Moolenbroek 		options -> universes [universe -> index]);
3581*83ee113eSDavid van Moolenbroek 	if (!head) {
3582*83ee113eSDavid van Moolenbroek 		if (!option_chain_head_allocate (((struct option_chain_head **)
3583*83ee113eSDavid van Moolenbroek 						  &options -> universes
3584*83ee113eSDavid van Moolenbroek 						  [universe -> index]), MDL))
3585*83ee113eSDavid van Moolenbroek 			return;
3586*83ee113eSDavid van Moolenbroek 		head = ((struct option_chain_head *)
3587*83ee113eSDavid van Moolenbroek 			options -> universes [universe -> index]);
3588*83ee113eSDavid van Moolenbroek 	}
3589*83ee113eSDavid van Moolenbroek 
3590*83ee113eSDavid van Moolenbroek 	/* Find the tail of the list. */
3591*83ee113eSDavid van Moolenbroek 	for (tail = &head -> first; *tail; tail = &((*tail) -> cdr)) {
3592*83ee113eSDavid van Moolenbroek 		ocloc = (struct option_cache **)&(*tail)->car;
3593*83ee113eSDavid van Moolenbroek 
3594*83ee113eSDavid van Moolenbroek 		if (oc->option->code == (*ocloc)->option->code) {
3595*83ee113eSDavid van Moolenbroek 			if (appendp) {
3596*83ee113eSDavid van Moolenbroek 				do {
3597*83ee113eSDavid van Moolenbroek 					ocloc = &(*ocloc)->next;
3598*83ee113eSDavid van Moolenbroek 				} while (*ocloc != NULL);
3599*83ee113eSDavid van Moolenbroek 			} else {
3600*83ee113eSDavid van Moolenbroek 				option_cache_dereference(ocloc, MDL);
3601*83ee113eSDavid van Moolenbroek 			}
3602*83ee113eSDavid van Moolenbroek 			option_cache_reference(ocloc, oc, MDL);
3603*83ee113eSDavid van Moolenbroek 			return;
3604*83ee113eSDavid van Moolenbroek 		}
3605*83ee113eSDavid van Moolenbroek 	}
3606*83ee113eSDavid van Moolenbroek 
3607*83ee113eSDavid van Moolenbroek 	*tail = cons (0, 0);
3608*83ee113eSDavid van Moolenbroek 	if (*tail) {
3609*83ee113eSDavid van Moolenbroek 		option_cache_reference ((struct option_cache **)
3610*83ee113eSDavid van Moolenbroek 					(&(*tail) -> car), oc, MDL);
3611*83ee113eSDavid van Moolenbroek 	}
3612*83ee113eSDavid van Moolenbroek }
3613*83ee113eSDavid van Moolenbroek 
linked_option_space_encapsulate(result,packet,lease,client_state,in_options,cfg_options,scope,universe)3614*83ee113eSDavid van Moolenbroek int linked_option_space_encapsulate (result, packet, lease, client_state,
3615*83ee113eSDavid van Moolenbroek 				    in_options, cfg_options, scope, universe)
3616*83ee113eSDavid van Moolenbroek 	struct data_string *result;
3617*83ee113eSDavid van Moolenbroek 	struct packet *packet;
3618*83ee113eSDavid van Moolenbroek 	struct lease *lease;
3619*83ee113eSDavid van Moolenbroek 	struct client_state *client_state;
3620*83ee113eSDavid van Moolenbroek 	struct option_state *in_options;
3621*83ee113eSDavid van Moolenbroek 	struct option_state *cfg_options;
3622*83ee113eSDavid van Moolenbroek 	struct binding_scope **scope;
3623*83ee113eSDavid van Moolenbroek 	struct universe *universe;
3624*83ee113eSDavid van Moolenbroek {
3625*83ee113eSDavid van Moolenbroek 	int status = 0;
3626*83ee113eSDavid van Moolenbroek 	pair oc;
3627*83ee113eSDavid van Moolenbroek 	struct option_chain_head *head;
3628*83ee113eSDavid van Moolenbroek 
3629*83ee113eSDavid van Moolenbroek 	if (universe -> index >= cfg_options -> universe_count)
3630*83ee113eSDavid van Moolenbroek 		return status;
3631*83ee113eSDavid van Moolenbroek 	head = ((struct option_chain_head *)
3632*83ee113eSDavid van Moolenbroek 		cfg_options -> universes [universe -> index]);
3633*83ee113eSDavid van Moolenbroek 	if (!head)
3634*83ee113eSDavid van Moolenbroek 		return status;
3635*83ee113eSDavid van Moolenbroek 
3636*83ee113eSDavid van Moolenbroek 	for (oc = head -> first; oc; oc = oc -> cdr) {
3637*83ee113eSDavid van Moolenbroek 		if (store_option (result, universe, packet,
3638*83ee113eSDavid van Moolenbroek 				  lease, client_state, in_options, cfg_options,
3639*83ee113eSDavid van Moolenbroek 				  scope, (struct option_cache *)(oc -> car)))
3640*83ee113eSDavid van Moolenbroek 			status = 1;
3641*83ee113eSDavid van Moolenbroek 	}
3642*83ee113eSDavid van Moolenbroek 
3643*83ee113eSDavid van Moolenbroek 	if (search_subencapsulation(result, packet, lease, client_state,
3644*83ee113eSDavid van Moolenbroek 				    in_options, cfg_options, scope, universe))
3645*83ee113eSDavid van Moolenbroek 		status = 1;
3646*83ee113eSDavid van Moolenbroek 
3647*83ee113eSDavid van Moolenbroek 	return status;
3648*83ee113eSDavid van Moolenbroek }
3649*83ee113eSDavid van Moolenbroek 
delete_linked_option(universe,options,code)3650*83ee113eSDavid van Moolenbroek void delete_linked_option (universe, options, code)
3651*83ee113eSDavid van Moolenbroek 	struct universe *universe;
3652*83ee113eSDavid van Moolenbroek 	struct option_state *options;
3653*83ee113eSDavid van Moolenbroek 	int code;
3654*83ee113eSDavid van Moolenbroek {
3655*83ee113eSDavid van Moolenbroek 	pair *tail, tmp = (pair)0;
3656*83ee113eSDavid van Moolenbroek 	struct option_chain_head *head;
3657*83ee113eSDavid van Moolenbroek 
3658*83ee113eSDavid van Moolenbroek 	if (universe -> index >= options -> universe_count)
3659*83ee113eSDavid van Moolenbroek 		return;
3660*83ee113eSDavid van Moolenbroek 	head = ((struct option_chain_head *)
3661*83ee113eSDavid van Moolenbroek 		options -> universes [universe -> index]);
3662*83ee113eSDavid van Moolenbroek 	if (!head)
3663*83ee113eSDavid van Moolenbroek 		return;
3664*83ee113eSDavid van Moolenbroek 
3665*83ee113eSDavid van Moolenbroek 	for (tail = &head -> first; *tail; tail = &((*tail) -> cdr)) {
3666*83ee113eSDavid van Moolenbroek 		if (code ==
3667*83ee113eSDavid van Moolenbroek 		    ((struct option_cache *)(*tail) -> car) -> option -> code)
3668*83ee113eSDavid van Moolenbroek 		{
3669*83ee113eSDavid van Moolenbroek 			tmp = (*tail) -> cdr;
3670*83ee113eSDavid van Moolenbroek 			option_cache_dereference ((struct option_cache **)
3671*83ee113eSDavid van Moolenbroek 						  (&(*tail) -> car), MDL);
3672*83ee113eSDavid van Moolenbroek 			dfree (*tail, MDL);
3673*83ee113eSDavid van Moolenbroek 			(*tail) = tmp;
3674*83ee113eSDavid van Moolenbroek 			break;
3675*83ee113eSDavid van Moolenbroek 		}
3676*83ee113eSDavid van Moolenbroek 	}
3677*83ee113eSDavid van Moolenbroek }
3678*83ee113eSDavid van Moolenbroek 
lookup_linked_option(universe,options,code)3679*83ee113eSDavid van Moolenbroek struct option_cache *lookup_linked_option (universe, options, code)
3680*83ee113eSDavid van Moolenbroek 	struct universe *universe;
3681*83ee113eSDavid van Moolenbroek 	struct option_state *options;
3682*83ee113eSDavid van Moolenbroek 	unsigned code;
3683*83ee113eSDavid van Moolenbroek {
3684*83ee113eSDavid van Moolenbroek 	pair oc;
3685*83ee113eSDavid van Moolenbroek 	struct option_chain_head *head;
3686*83ee113eSDavid van Moolenbroek 
3687*83ee113eSDavid van Moolenbroek 	if (universe -> index >= options -> universe_count)
3688*83ee113eSDavid van Moolenbroek 		return 0;
3689*83ee113eSDavid van Moolenbroek 	head = ((struct option_chain_head *)
3690*83ee113eSDavid van Moolenbroek 		options -> universes [universe -> index]);
3691*83ee113eSDavid van Moolenbroek 	if (!head)
3692*83ee113eSDavid van Moolenbroek 		return 0;
3693*83ee113eSDavid van Moolenbroek 
3694*83ee113eSDavid van Moolenbroek 	for (oc = head -> first; oc; oc = oc -> cdr) {
3695*83ee113eSDavid van Moolenbroek 		if (code ==
3696*83ee113eSDavid van Moolenbroek 		    ((struct option_cache *)(oc -> car)) -> option -> code) {
3697*83ee113eSDavid van Moolenbroek 			return (struct option_cache *)(oc -> car);
3698*83ee113eSDavid van Moolenbroek 		}
3699*83ee113eSDavid van Moolenbroek 	}
3700*83ee113eSDavid van Moolenbroek 
3701*83ee113eSDavid van Moolenbroek 	return (struct option_cache *)0;
3702*83ee113eSDavid van Moolenbroek }
3703*83ee113eSDavid van Moolenbroek 
linked_option_state_dereference(universe,state,file,line)3704*83ee113eSDavid van Moolenbroek int linked_option_state_dereference (universe, state, file, line)
3705*83ee113eSDavid van Moolenbroek 	struct universe *universe;
3706*83ee113eSDavid van Moolenbroek 	struct option_state *state;
3707*83ee113eSDavid van Moolenbroek 	const char *file;
3708*83ee113eSDavid van Moolenbroek 	int line;
3709*83ee113eSDavid van Moolenbroek {
3710*83ee113eSDavid van Moolenbroek 	return (option_chain_head_dereference
3711*83ee113eSDavid van Moolenbroek 		((struct option_chain_head **)
3712*83ee113eSDavid van Moolenbroek 		 (&state -> universes [universe -> index]), MDL));
3713*83ee113eSDavid van Moolenbroek }
3714*83ee113eSDavid van Moolenbroek 
linked_option_space_foreach(struct packet * packet,struct lease * lease,struct client_state * client_state,struct option_state * in_options,struct option_state * cfg_options,struct binding_scope ** scope,struct universe * u,void * stuff,void (* func)(struct option_cache *,struct packet *,struct lease *,struct client_state *,struct option_state *,struct option_state *,struct binding_scope **,struct universe *,void *))3715*83ee113eSDavid van Moolenbroek void linked_option_space_foreach (struct packet *packet, struct lease *lease,
3716*83ee113eSDavid van Moolenbroek 				  struct client_state *client_state,
3717*83ee113eSDavid van Moolenbroek 				  struct option_state *in_options,
3718*83ee113eSDavid van Moolenbroek 				  struct option_state *cfg_options,
3719*83ee113eSDavid van Moolenbroek 				  struct binding_scope **scope,
3720*83ee113eSDavid van Moolenbroek 				  struct universe *u, void *stuff,
3721*83ee113eSDavid van Moolenbroek 				  void (*func) (struct option_cache *,
3722*83ee113eSDavid van Moolenbroek 						struct packet *,
3723*83ee113eSDavid van Moolenbroek 						struct lease *,
3724*83ee113eSDavid van Moolenbroek 						struct client_state *,
3725*83ee113eSDavid van Moolenbroek 						struct option_state *,
3726*83ee113eSDavid van Moolenbroek 						struct option_state *,
3727*83ee113eSDavid van Moolenbroek 						struct binding_scope **,
3728*83ee113eSDavid van Moolenbroek 						struct universe *, void *))
3729*83ee113eSDavid van Moolenbroek {
3730*83ee113eSDavid van Moolenbroek 	pair car;
3731*83ee113eSDavid van Moolenbroek 	struct option_chain_head *head;
3732*83ee113eSDavid van Moolenbroek 
3733*83ee113eSDavid van Moolenbroek 	if (u -> index >= cfg_options -> universe_count)
3734*83ee113eSDavid van Moolenbroek 		return;
3735*83ee113eSDavid van Moolenbroek 	head = ((struct option_chain_head *)
3736*83ee113eSDavid van Moolenbroek 		cfg_options -> universes [u -> index]);
3737*83ee113eSDavid van Moolenbroek 	if (!head)
3738*83ee113eSDavid van Moolenbroek 		return;
3739*83ee113eSDavid van Moolenbroek 	for (car = head -> first; car; car = car -> cdr) {
3740*83ee113eSDavid van Moolenbroek 		(*func) ((struct option_cache *)(car -> car),
3741*83ee113eSDavid van Moolenbroek 			 packet, lease, client_state,
3742*83ee113eSDavid van Moolenbroek 			 in_options, cfg_options, scope, u, stuff);
3743*83ee113eSDavid van Moolenbroek 	}
3744*83ee113eSDavid van Moolenbroek }
3745*83ee113eSDavid van Moolenbroek 
do_packet(interface,packet,len,from_port,from,hfrom)3746*83ee113eSDavid van Moolenbroek void do_packet (interface, packet, len, from_port, from, hfrom)
3747*83ee113eSDavid van Moolenbroek 	struct interface_info *interface;
3748*83ee113eSDavid van Moolenbroek 	struct dhcp_packet *packet;
3749*83ee113eSDavid van Moolenbroek 	unsigned len;
3750*83ee113eSDavid van Moolenbroek 	unsigned int from_port;
3751*83ee113eSDavid van Moolenbroek 	struct iaddr from;
3752*83ee113eSDavid van Moolenbroek 	struct hardware *hfrom;
3753*83ee113eSDavid van Moolenbroek {
3754*83ee113eSDavid van Moolenbroek 	struct option_cache *op;
3755*83ee113eSDavid van Moolenbroek 	struct packet *decoded_packet;
3756*83ee113eSDavid van Moolenbroek #if defined (DEBUG_MEMORY_LEAKAGE)
3757*83ee113eSDavid van Moolenbroek 	unsigned long previous_outstanding = dmalloc_outstanding;
3758*83ee113eSDavid van Moolenbroek #endif
3759*83ee113eSDavid van Moolenbroek 
3760*83ee113eSDavid van Moolenbroek #if defined (TRACING)
3761*83ee113eSDavid van Moolenbroek 	trace_inpacket_stash(interface, packet, len, from_port, from, hfrom);
3762*83ee113eSDavid van Moolenbroek #endif
3763*83ee113eSDavid van Moolenbroek 
3764*83ee113eSDavid van Moolenbroek 	decoded_packet = NULL;
3765*83ee113eSDavid van Moolenbroek 	if (!packet_allocate(&decoded_packet, MDL)) {
3766*83ee113eSDavid van Moolenbroek 		log_error("do_packet: no memory for incoming packet!");
3767*83ee113eSDavid van Moolenbroek 		return;
3768*83ee113eSDavid van Moolenbroek 	}
3769*83ee113eSDavid van Moolenbroek 	decoded_packet->raw = packet;
3770*83ee113eSDavid van Moolenbroek 	decoded_packet->packet_length = len;
3771*83ee113eSDavid van Moolenbroek 	decoded_packet->client_port = from_port;
3772*83ee113eSDavid van Moolenbroek 	decoded_packet->client_addr = from;
3773*83ee113eSDavid van Moolenbroek 	interface_reference(&decoded_packet->interface, interface, MDL);
3774*83ee113eSDavid van Moolenbroek 	decoded_packet->haddr = hfrom;
3775*83ee113eSDavid van Moolenbroek 
3776*83ee113eSDavid van Moolenbroek 	if (packet->hlen > sizeof packet->chaddr) {
3777*83ee113eSDavid van Moolenbroek 		packet_dereference(&decoded_packet, MDL);
3778*83ee113eSDavid van Moolenbroek 		log_info("Discarding packet with bogus hlen.");
3779*83ee113eSDavid van Moolenbroek 		return;
3780*83ee113eSDavid van Moolenbroek 	}
3781*83ee113eSDavid van Moolenbroek 
3782*83ee113eSDavid van Moolenbroek 	/* If there's an option buffer, try to parse it. */
3783*83ee113eSDavid van Moolenbroek 	if (decoded_packet->packet_length >= DHCP_FIXED_NON_UDP + 4) {
3784*83ee113eSDavid van Moolenbroek 		if (!parse_options(decoded_packet)) {
3785*83ee113eSDavid van Moolenbroek 			if (decoded_packet->options)
3786*83ee113eSDavid van Moolenbroek 				option_state_dereference
3787*83ee113eSDavid van Moolenbroek 					(&decoded_packet->options, MDL);
3788*83ee113eSDavid van Moolenbroek 			packet_dereference (&decoded_packet, MDL);
3789*83ee113eSDavid van Moolenbroek 			return;
3790*83ee113eSDavid van Moolenbroek 		}
3791*83ee113eSDavid van Moolenbroek 
3792*83ee113eSDavid van Moolenbroek 		if (decoded_packet->options_valid &&
3793*83ee113eSDavid van Moolenbroek 		    (op = lookup_option(&dhcp_universe,
3794*83ee113eSDavid van Moolenbroek 					decoded_packet->options,
3795*83ee113eSDavid van Moolenbroek 					DHO_DHCP_MESSAGE_TYPE))) {
3796*83ee113eSDavid van Moolenbroek 			struct data_string dp;
3797*83ee113eSDavid van Moolenbroek 			memset(&dp, 0, sizeof dp);
3798*83ee113eSDavid van Moolenbroek 			evaluate_option_cache(&dp, decoded_packet, NULL, NULL,
3799*83ee113eSDavid van Moolenbroek 					      decoded_packet->options, NULL,
3800*83ee113eSDavid van Moolenbroek 					      NULL, op, MDL);
3801*83ee113eSDavid van Moolenbroek 			if (dp.len > 0)
3802*83ee113eSDavid van Moolenbroek 				decoded_packet->packet_type = dp.data[0];
3803*83ee113eSDavid van Moolenbroek 			else
3804*83ee113eSDavid van Moolenbroek 				decoded_packet->packet_type = 0;
3805*83ee113eSDavid van Moolenbroek 			data_string_forget(&dp, MDL);
3806*83ee113eSDavid van Moolenbroek 		}
3807*83ee113eSDavid van Moolenbroek 	}
3808*83ee113eSDavid van Moolenbroek 
3809*83ee113eSDavid van Moolenbroek 	if (validate_packet(decoded_packet) != 0) {
3810*83ee113eSDavid van Moolenbroek 		if (decoded_packet->packet_type)
3811*83ee113eSDavid van Moolenbroek 			dhcp(decoded_packet);
3812*83ee113eSDavid van Moolenbroek 		else
3813*83ee113eSDavid van Moolenbroek 			bootp(decoded_packet);
3814*83ee113eSDavid van Moolenbroek 	}
3815*83ee113eSDavid van Moolenbroek 
3816*83ee113eSDavid van Moolenbroek 	/* If the caller kept the packet, they'll have upped the refcnt. */
3817*83ee113eSDavid van Moolenbroek 	packet_dereference(&decoded_packet, MDL);
3818*83ee113eSDavid van Moolenbroek 
3819*83ee113eSDavid van Moolenbroek #if defined (DEBUG_MEMORY_LEAKAGE)
3820*83ee113eSDavid van Moolenbroek 	log_info("generation %ld: %ld new, %ld outstanding, %ld long-term",
3821*83ee113eSDavid van Moolenbroek 		 dmalloc_generation,
3822*83ee113eSDavid van Moolenbroek 		 dmalloc_outstanding - previous_outstanding,
3823*83ee113eSDavid van Moolenbroek 		 dmalloc_outstanding, dmalloc_longterm);
3824*83ee113eSDavid van Moolenbroek 	dmalloc_dump_outstanding();
3825*83ee113eSDavid van Moolenbroek #endif
3826*83ee113eSDavid van Moolenbroek #if defined (DEBUG_RC_HISTORY_EXHAUSTIVELY)
3827*83ee113eSDavid van Moolenbroek 	dump_rc_history(0);
3828*83ee113eSDavid van Moolenbroek #endif
3829*83ee113eSDavid van Moolenbroek }
3830*83ee113eSDavid van Moolenbroek 
3831*83ee113eSDavid van Moolenbroek int
packet6_len_okay(const char * packet,int len)3832*83ee113eSDavid van Moolenbroek packet6_len_okay(const char *packet, int len) {
3833*83ee113eSDavid van Moolenbroek 	if (len < 1) {
3834*83ee113eSDavid van Moolenbroek 		return 0;
3835*83ee113eSDavid van Moolenbroek 	}
3836*83ee113eSDavid van Moolenbroek 	if ((packet[0] == DHCPV6_RELAY_FORW) ||
3837*83ee113eSDavid van Moolenbroek 	    (packet[0] == DHCPV6_RELAY_REPL)) {
3838*83ee113eSDavid van Moolenbroek 		if (len >= offsetof(struct dhcpv6_relay_packet, options)) {
3839*83ee113eSDavid van Moolenbroek 			return 1;
3840*83ee113eSDavid van Moolenbroek 		} else {
3841*83ee113eSDavid van Moolenbroek 			return 0;
3842*83ee113eSDavid van Moolenbroek 		}
3843*83ee113eSDavid van Moolenbroek 	} else {
3844*83ee113eSDavid van Moolenbroek 		if (len >= offsetof(struct dhcpv6_packet, options)) {
3845*83ee113eSDavid van Moolenbroek 			return 1;
3846*83ee113eSDavid van Moolenbroek 		} else {
3847*83ee113eSDavid van Moolenbroek 			return 0;
3848*83ee113eSDavid van Moolenbroek 		}
3849*83ee113eSDavid van Moolenbroek 	}
3850*83ee113eSDavid van Moolenbroek }
3851*83ee113eSDavid van Moolenbroek 
3852*83ee113eSDavid van Moolenbroek #ifdef DHCPv6
3853*83ee113eSDavid van Moolenbroek void
do_packet6(struct interface_info * interface,const char * packet,int len,int from_port,const struct iaddr * from,isc_boolean_t was_unicast)3854*83ee113eSDavid van Moolenbroek do_packet6(struct interface_info *interface, const char *packet,
3855*83ee113eSDavid van Moolenbroek 	   int len, int from_port, const struct iaddr *from,
3856*83ee113eSDavid van Moolenbroek 	   isc_boolean_t was_unicast) {
3857*83ee113eSDavid van Moolenbroek 	unsigned char msg_type;
3858*83ee113eSDavid van Moolenbroek 	const struct dhcpv6_packet *msg;
3859*83ee113eSDavid van Moolenbroek 	const struct dhcpv6_relay_packet *relay;
3860*83ee113eSDavid van Moolenbroek 	struct packet *decoded_packet;
3861*83ee113eSDavid van Moolenbroek #if defined (DEBUG_MEMORY_LEAKAGE)
3862*83ee113eSDavid van Moolenbroek 	unsigned long previous_outstanding = dmalloc_outstanding;
3863*83ee113eSDavid van Moolenbroek #endif
3864*83ee113eSDavid van Moolenbroek 
3865*83ee113eSDavid van Moolenbroek 	if (!packet6_len_okay(packet, len)) {
3866*83ee113eSDavid van Moolenbroek 		log_info("do_packet6: "
3867*83ee113eSDavid van Moolenbroek 			 "short packet from %s port %d, len %d, dropped",
3868*83ee113eSDavid van Moolenbroek 			 piaddr(*from), from_port, len);
3869*83ee113eSDavid van Moolenbroek 		return;
3870*83ee113eSDavid van Moolenbroek 	}
3871*83ee113eSDavid van Moolenbroek 
3872*83ee113eSDavid van Moolenbroek 	decoded_packet = NULL;
3873*83ee113eSDavid van Moolenbroek 	if (!packet_allocate(&decoded_packet, MDL)) {
3874*83ee113eSDavid van Moolenbroek 		log_error("do_packet6: no memory for incoming packet.");
3875*83ee113eSDavid van Moolenbroek 		return;
3876*83ee113eSDavid van Moolenbroek 	}
3877*83ee113eSDavid van Moolenbroek 
3878*83ee113eSDavid van Moolenbroek 	if (!option_state_allocate(&decoded_packet->options, MDL)) {
3879*83ee113eSDavid van Moolenbroek 		log_error("do_packet6: no memory for options.");
3880*83ee113eSDavid van Moolenbroek 		packet_dereference(&decoded_packet, MDL);
3881*83ee113eSDavid van Moolenbroek 		return;
3882*83ee113eSDavid van Moolenbroek 	}
3883*83ee113eSDavid van Moolenbroek 
3884*83ee113eSDavid van Moolenbroek 	/* IPv4 information, already set to 0 */
3885*83ee113eSDavid van Moolenbroek 	/* decoded_packet->packet_type = 0; */
3886*83ee113eSDavid van Moolenbroek 	/* memset(&decoded_packet->haddr, 0, sizeof(decoded_packet->haddr)); */
3887*83ee113eSDavid van Moolenbroek 	/* decoded_packet->circuit_id = NULL; */
3888*83ee113eSDavid van Moolenbroek 	/* decoded_packet->circuit_id_len = 0; */
3889*83ee113eSDavid van Moolenbroek 	/* decoded_packet->remote_id = NULL; */
3890*83ee113eSDavid van Moolenbroek 	/* decoded_packet->remote_id_len = 0; */
3891*83ee113eSDavid van Moolenbroek 	decoded_packet->raw = (struct dhcp_packet *)packet;
3892*83ee113eSDavid van Moolenbroek 	decoded_packet->packet_length = (unsigned)len;
3893*83ee113eSDavid van Moolenbroek 	decoded_packet->client_port = from_port;
3894*83ee113eSDavid van Moolenbroek 	decoded_packet->client_addr = *from;
3895*83ee113eSDavid van Moolenbroek 	interface_reference(&decoded_packet->interface, interface, MDL);
3896*83ee113eSDavid van Moolenbroek 
3897*83ee113eSDavid van Moolenbroek 	decoded_packet->unicast = was_unicast;
3898*83ee113eSDavid van Moolenbroek 
3899*83ee113eSDavid van Moolenbroek 	msg_type = packet[0];
3900*83ee113eSDavid van Moolenbroek 	if ((msg_type == DHCPV6_RELAY_FORW) ||
3901*83ee113eSDavid van Moolenbroek 	    (msg_type == DHCPV6_RELAY_REPL)) {
3902*83ee113eSDavid van Moolenbroek 		int relaylen = (int)(offsetof(struct dhcpv6_relay_packet, options));
3903*83ee113eSDavid van Moolenbroek 		relay = (const struct dhcpv6_relay_packet *)packet;
3904*83ee113eSDavid van Moolenbroek 		decoded_packet->dhcpv6_msg_type = relay->msg_type;
3905*83ee113eSDavid van Moolenbroek 
3906*83ee113eSDavid van Moolenbroek 		/* relay-specific data */
3907*83ee113eSDavid van Moolenbroek 		decoded_packet->dhcpv6_hop_count = relay->hop_count;
3908*83ee113eSDavid van Moolenbroek 		memcpy(&decoded_packet->dhcpv6_link_address,
3909*83ee113eSDavid van Moolenbroek 		       relay->link_address, sizeof(relay->link_address));
3910*83ee113eSDavid van Moolenbroek 		memcpy(&decoded_packet->dhcpv6_peer_address,
3911*83ee113eSDavid van Moolenbroek 		       relay->peer_address, sizeof(relay->peer_address));
3912*83ee113eSDavid van Moolenbroek 
3913*83ee113eSDavid van Moolenbroek 		if (!parse_option_buffer(decoded_packet->options,
3914*83ee113eSDavid van Moolenbroek 					 relay->options, len - relaylen,
3915*83ee113eSDavid van Moolenbroek 					 &dhcpv6_universe)) {
3916*83ee113eSDavid van Moolenbroek 			/* no logging here, as parse_option_buffer() logs all
3917*83ee113eSDavid van Moolenbroek 			   cases where it fails */
3918*83ee113eSDavid van Moolenbroek 			packet_dereference(&decoded_packet, MDL);
3919*83ee113eSDavid van Moolenbroek 			return;
3920*83ee113eSDavid van Moolenbroek 		}
3921*83ee113eSDavid van Moolenbroek 	} else {
3922*83ee113eSDavid van Moolenbroek 		int msglen = (int)(offsetof(struct dhcpv6_packet, options));
3923*83ee113eSDavid van Moolenbroek 		msg = (const struct dhcpv6_packet *)packet;
3924*83ee113eSDavid van Moolenbroek 		decoded_packet->dhcpv6_msg_type = msg->msg_type;
3925*83ee113eSDavid van Moolenbroek 
3926*83ee113eSDavid van Moolenbroek 		/* message-specific data */
3927*83ee113eSDavid van Moolenbroek 		memcpy(decoded_packet->dhcpv6_transaction_id,
3928*83ee113eSDavid van Moolenbroek 		       msg->transaction_id,
3929*83ee113eSDavid van Moolenbroek 		       sizeof(decoded_packet->dhcpv6_transaction_id));
3930*83ee113eSDavid van Moolenbroek 
3931*83ee113eSDavid van Moolenbroek 		if (!parse_option_buffer(decoded_packet->options,
3932*83ee113eSDavid van Moolenbroek 					 msg->options, len - msglen,
3933*83ee113eSDavid van Moolenbroek 					 &dhcpv6_universe)) {
3934*83ee113eSDavid van Moolenbroek 			/* no logging here, as parse_option_buffer() logs all
3935*83ee113eSDavid van Moolenbroek 			   cases where it fails */
3936*83ee113eSDavid van Moolenbroek 			packet_dereference(&decoded_packet, MDL);
3937*83ee113eSDavid van Moolenbroek 			return;
3938*83ee113eSDavid van Moolenbroek 		}
3939*83ee113eSDavid van Moolenbroek 	}
3940*83ee113eSDavid van Moolenbroek 
3941*83ee113eSDavid van Moolenbroek 	dhcpv6(decoded_packet);
3942*83ee113eSDavid van Moolenbroek 
3943*83ee113eSDavid van Moolenbroek 	packet_dereference(&decoded_packet, MDL);
3944*83ee113eSDavid van Moolenbroek 
3945*83ee113eSDavid van Moolenbroek #if defined (DEBUG_MEMORY_LEAKAGE)
3946*83ee113eSDavid van Moolenbroek 	log_info("generation %ld: %ld new, %ld outstanding, %ld long-term",
3947*83ee113eSDavid van Moolenbroek 		 dmalloc_generation,
3948*83ee113eSDavid van Moolenbroek 		 dmalloc_outstanding - previous_outstanding,
3949*83ee113eSDavid van Moolenbroek 		 dmalloc_outstanding, dmalloc_longterm);
3950*83ee113eSDavid van Moolenbroek 	dmalloc_dump_outstanding();
3951*83ee113eSDavid van Moolenbroek #endif
3952*83ee113eSDavid van Moolenbroek #if defined (DEBUG_RC_HISTORY_EXHAUSTIVELY)
3953*83ee113eSDavid van Moolenbroek 	dump_rc_history(0);
3954*83ee113eSDavid van Moolenbroek #endif
3955*83ee113eSDavid van Moolenbroek }
3956*83ee113eSDavid van Moolenbroek #endif /* DHCPv6 */
3957*83ee113eSDavid van Moolenbroek 
3958*83ee113eSDavid van Moolenbroek int
pretty_escape(char ** dst,char * dend,const unsigned char ** src,const unsigned char * send)3959*83ee113eSDavid van Moolenbroek pretty_escape(char **dst, char *dend, const unsigned char **src,
3960*83ee113eSDavid van Moolenbroek 	      const unsigned char *send)
3961*83ee113eSDavid van Moolenbroek {
3962*83ee113eSDavid van Moolenbroek 	int count = 0;
3963*83ee113eSDavid van Moolenbroek 
3964*83ee113eSDavid van Moolenbroek 	/* If there aren't as many bytes left as there are in the source
3965*83ee113eSDavid van Moolenbroek 	 * buffer, don't even bother entering the loop.
3966*83ee113eSDavid van Moolenbroek 	 */
3967*83ee113eSDavid van Moolenbroek 	if (dst == NULL || dend == NULL || src == NULL || send == NULL ||
3968*83ee113eSDavid van Moolenbroek 	    *dst == NULL || *src == NULL || (*dst >= dend) || (*src > send) ||
3969*83ee113eSDavid van Moolenbroek 	    ((send - *src) > (dend - *dst)))
3970*83ee113eSDavid van Moolenbroek 		return -1;
3971*83ee113eSDavid van Moolenbroek 
3972*83ee113eSDavid van Moolenbroek 	for ( ; *src < send ; (*src)++) {
3973*83ee113eSDavid van Moolenbroek 		if (!isascii (**src) || !isprint (**src)) {
3974*83ee113eSDavid van Moolenbroek 			/* Skip trailing NUL. */
3975*83ee113eSDavid van Moolenbroek 			if ((*src + 1) != send || **src != '\0') {
3976*83ee113eSDavid van Moolenbroek 				if (*dst + 4 > dend)
3977*83ee113eSDavid van Moolenbroek 					return -1;
3978*83ee113eSDavid van Moolenbroek 
3979*83ee113eSDavid van Moolenbroek 				sprintf(*dst, "\\%03o",
3980*83ee113eSDavid van Moolenbroek 					**src);
3981*83ee113eSDavid van Moolenbroek 				(*dst) += 4;
3982*83ee113eSDavid van Moolenbroek 				count += 4;
3983*83ee113eSDavid van Moolenbroek 			}
3984*83ee113eSDavid van Moolenbroek 		} else if (**src == '"' || **src == '\'' || **src == '$' ||
3985*83ee113eSDavid van Moolenbroek 			   **src == '`' || **src == '\\' || **src == '|' ||
3986*83ee113eSDavid van Moolenbroek 			   **src == '&') {
3987*83ee113eSDavid van Moolenbroek 			if (*dst + 2 > dend)
3988*83ee113eSDavid van Moolenbroek 				return -1;
3989*83ee113eSDavid van Moolenbroek 
3990*83ee113eSDavid van Moolenbroek 			**dst = '\\';
3991*83ee113eSDavid van Moolenbroek 			(*dst)++;
3992*83ee113eSDavid van Moolenbroek 			**dst = **src;
3993*83ee113eSDavid van Moolenbroek 			(*dst)++;
3994*83ee113eSDavid van Moolenbroek 			count += 2;
3995*83ee113eSDavid van Moolenbroek 		} else {
3996*83ee113eSDavid van Moolenbroek 			if (*dst + 1 > dend)
3997*83ee113eSDavid van Moolenbroek 				return -1;
3998*83ee113eSDavid van Moolenbroek 
3999*83ee113eSDavid van Moolenbroek 			**dst = **src;
4000*83ee113eSDavid van Moolenbroek 			(*dst)++;
4001*83ee113eSDavid van Moolenbroek 			count++;
4002*83ee113eSDavid van Moolenbroek 		}
4003*83ee113eSDavid van Moolenbroek 	}
4004*83ee113eSDavid van Moolenbroek 
4005*83ee113eSDavid van Moolenbroek 	return count;
4006*83ee113eSDavid van Moolenbroek }
4007*83ee113eSDavid van Moolenbroek 
4008*83ee113eSDavid van Moolenbroek static int
pretty_text(char ** dst,char * dend,const unsigned char ** src,const unsigned char * send,int emit_quotes)4009*83ee113eSDavid van Moolenbroek pretty_text(char **dst, char *dend, const unsigned char **src,
4010*83ee113eSDavid van Moolenbroek 	    const unsigned char *send, int emit_quotes)
4011*83ee113eSDavid van Moolenbroek {
4012*83ee113eSDavid van Moolenbroek 	int count;
4013*83ee113eSDavid van Moolenbroek 
4014*83ee113eSDavid van Moolenbroek 	if (dst == NULL || dend == NULL || src == NULL || send == NULL ||
4015*83ee113eSDavid van Moolenbroek 	    *dst == NULL || *src == NULL ||
4016*83ee113eSDavid van Moolenbroek 	    ((*dst + (emit_quotes ? 2 : 0)) > dend) || (*src > send))
4017*83ee113eSDavid van Moolenbroek 		return -1;
4018*83ee113eSDavid van Moolenbroek 
4019*83ee113eSDavid van Moolenbroek 	if (emit_quotes) {
4020*83ee113eSDavid van Moolenbroek 		**dst = '"';
4021*83ee113eSDavid van Moolenbroek 		(*dst)++;
4022*83ee113eSDavid van Moolenbroek 	}
4023*83ee113eSDavid van Moolenbroek 
4024*83ee113eSDavid van Moolenbroek 	/* dend-1 leaves 1 byte for the closing quote. */
4025*83ee113eSDavid van Moolenbroek 	count = pretty_escape(dst, dend - (emit_quotes ? 1 : 0), src, send);
4026*83ee113eSDavid van Moolenbroek 
4027*83ee113eSDavid van Moolenbroek 	if (count == -1)
4028*83ee113eSDavid van Moolenbroek 		return -1;
4029*83ee113eSDavid van Moolenbroek 
4030*83ee113eSDavid van Moolenbroek 	if (emit_quotes && (*dst < dend)) {
4031*83ee113eSDavid van Moolenbroek 		**dst = '"';
4032*83ee113eSDavid van Moolenbroek 		(*dst)++;
4033*83ee113eSDavid van Moolenbroek 
4034*83ee113eSDavid van Moolenbroek 		/* Includes quote prior to pretty_escape(); */
4035*83ee113eSDavid van Moolenbroek 		count += 2;
4036*83ee113eSDavid van Moolenbroek 	}
4037*83ee113eSDavid van Moolenbroek 
4038*83ee113eSDavid van Moolenbroek 	return count;
4039*83ee113eSDavid van Moolenbroek }
4040*83ee113eSDavid van Moolenbroek 
4041*83ee113eSDavid van Moolenbroek static int
pretty_domain(char ** dst,char * dend,const unsigned char ** src,const unsigned char * send)4042*83ee113eSDavid van Moolenbroek pretty_domain(char **dst, char *dend, const unsigned char **src,
4043*83ee113eSDavid van Moolenbroek 	      const unsigned char *send)
4044*83ee113eSDavid van Moolenbroek {
4045*83ee113eSDavid van Moolenbroek 	const unsigned char *tend;
4046*83ee113eSDavid van Moolenbroek 	int count = 2;
4047*83ee113eSDavid van Moolenbroek 	int tsiz, status;
4048*83ee113eSDavid van Moolenbroek 
4049*83ee113eSDavid van Moolenbroek 	if (dst == NULL || dend == NULL || src == NULL || send == NULL ||
4050*83ee113eSDavid van Moolenbroek 	    *dst == NULL || *src == NULL ||
4051*83ee113eSDavid van Moolenbroek 	    ((*dst + 2) > dend) || (*src >= send))
4052*83ee113eSDavid van Moolenbroek 		return -1;
4053*83ee113eSDavid van Moolenbroek 
4054*83ee113eSDavid van Moolenbroek 	**dst = '"';
4055*83ee113eSDavid van Moolenbroek 	(*dst)++;
4056*83ee113eSDavid van Moolenbroek 
4057*83ee113eSDavid van Moolenbroek 	do {
4058*83ee113eSDavid van Moolenbroek 		/* Continue loop until end of src buffer. */
4059*83ee113eSDavid van Moolenbroek 		if (*src >= send)
4060*83ee113eSDavid van Moolenbroek 			break;
4061*83ee113eSDavid van Moolenbroek 
4062*83ee113eSDavid van Moolenbroek 		/* Consume tag size. */
4063*83ee113eSDavid van Moolenbroek 		tsiz = **src;
4064*83ee113eSDavid van Moolenbroek 		(*src)++;
4065*83ee113eSDavid van Moolenbroek 
4066*83ee113eSDavid van Moolenbroek 		/* At root, finis. */
4067*83ee113eSDavid van Moolenbroek 		if (tsiz == 0)
4068*83ee113eSDavid van Moolenbroek 			break;
4069*83ee113eSDavid van Moolenbroek 
4070*83ee113eSDavid van Moolenbroek 		tend = (*src) + tsiz;
4071*83ee113eSDavid van Moolenbroek 
4072*83ee113eSDavid van Moolenbroek 		/* If the tag exceeds the source buffer, it's illegal.
4073*83ee113eSDavid van Moolenbroek 		 * This should also trap compression pointers (which should
4074*83ee113eSDavid van Moolenbroek 		 * not be in these buffers).
4075*83ee113eSDavid van Moolenbroek 		 */
4076*83ee113eSDavid van Moolenbroek 		if (tend > send)
4077*83ee113eSDavid van Moolenbroek 			return -1;
4078*83ee113eSDavid van Moolenbroek 
4079*83ee113eSDavid van Moolenbroek 		/* dend-2 leaves room for a trailing dot and quote. */
4080*83ee113eSDavid van Moolenbroek 		status = pretty_escape(dst, dend-2, src, tend);
4081*83ee113eSDavid van Moolenbroek 
4082*83ee113eSDavid van Moolenbroek 		if ((status == -1) || ((*dst + 2) > dend))
4083*83ee113eSDavid van Moolenbroek 			return -1;
4084*83ee113eSDavid van Moolenbroek 
4085*83ee113eSDavid van Moolenbroek 		**dst = '.';
4086*83ee113eSDavid van Moolenbroek 		(*dst)++;
4087*83ee113eSDavid van Moolenbroek 		count += status + 1;
4088*83ee113eSDavid van Moolenbroek 	}
4089*83ee113eSDavid van Moolenbroek 	while(1);
4090*83ee113eSDavid van Moolenbroek 
4091*83ee113eSDavid van Moolenbroek 	**dst = '"';
4092*83ee113eSDavid van Moolenbroek 	(*dst)++;
4093*83ee113eSDavid van Moolenbroek 
4094*83ee113eSDavid van Moolenbroek 	return count;
4095*83ee113eSDavid van Moolenbroek }
4096*83ee113eSDavid van Moolenbroek 
4097*83ee113eSDavid van Moolenbroek /*
4098*83ee113eSDavid van Moolenbroek  * Add the option identified with the option number and data to the
4099*83ee113eSDavid van Moolenbroek  * options state.
4100*83ee113eSDavid van Moolenbroek  */
4101*83ee113eSDavid van Moolenbroek int
add_option(struct option_state * options,unsigned int option_num,void * data,unsigned int data_len)4102*83ee113eSDavid van Moolenbroek add_option(struct option_state *options,
4103*83ee113eSDavid van Moolenbroek 	   unsigned int option_num,
4104*83ee113eSDavid van Moolenbroek 	   void *data,
4105*83ee113eSDavid van Moolenbroek 	   unsigned int data_len)
4106*83ee113eSDavid van Moolenbroek {
4107*83ee113eSDavid van Moolenbroek 	struct option_cache *oc;
4108*83ee113eSDavid van Moolenbroek 	struct option *option;
4109*83ee113eSDavid van Moolenbroek 
4110*83ee113eSDavid van Moolenbroek 	/* INSIST(options != NULL); */
4111*83ee113eSDavid van Moolenbroek 	/* INSIST(data != NULL); */
4112*83ee113eSDavid van Moolenbroek 
4113*83ee113eSDavid van Moolenbroek 	option = NULL;
4114*83ee113eSDavid van Moolenbroek 	if (!option_code_hash_lookup(&option, dhcp_universe.code_hash,
4115*83ee113eSDavid van Moolenbroek 				     &option_num, 0, MDL)) {
4116*83ee113eSDavid van Moolenbroek 		log_error("Attempting to add unknown option %d.", option_num);
4117*83ee113eSDavid van Moolenbroek 		return 0;
4118*83ee113eSDavid van Moolenbroek 	}
4119*83ee113eSDavid van Moolenbroek 
4120*83ee113eSDavid van Moolenbroek 	oc = NULL;
4121*83ee113eSDavid van Moolenbroek 	if (!option_cache_allocate(&oc, MDL)) {
4122*83ee113eSDavid van Moolenbroek 		log_error("No memory for option cache adding %s (option %d).",
4123*83ee113eSDavid van Moolenbroek 			  option->name, option_num);
4124*83ee113eSDavid van Moolenbroek 		return 0;
4125*83ee113eSDavid van Moolenbroek 	}
4126*83ee113eSDavid van Moolenbroek 
4127*83ee113eSDavid van Moolenbroek 	if (!make_const_data(&oc->expression,
4128*83ee113eSDavid van Moolenbroek 			     data,
4129*83ee113eSDavid van Moolenbroek 			     data_len,
4130*83ee113eSDavid van Moolenbroek 			     0,
4131*83ee113eSDavid van Moolenbroek 			     0,
4132*83ee113eSDavid van Moolenbroek 			     MDL)) {
4133*83ee113eSDavid van Moolenbroek 		log_error("No memory for constant data adding %s (option %d).",
4134*83ee113eSDavid van Moolenbroek 			  option->name, option_num);
4135*83ee113eSDavid van Moolenbroek 		option_cache_dereference(&oc, MDL);
4136*83ee113eSDavid van Moolenbroek 		return 0;
4137*83ee113eSDavid van Moolenbroek 	}
4138*83ee113eSDavid van Moolenbroek 
4139*83ee113eSDavid van Moolenbroek 	option_reference(&(oc->option), option, MDL);
4140*83ee113eSDavid van Moolenbroek 	save_option(&dhcp_universe, options, oc);
4141*83ee113eSDavid van Moolenbroek 	option_cache_dereference(&oc, MDL);
4142*83ee113eSDavid van Moolenbroek 
4143*83ee113eSDavid van Moolenbroek 	return 1;
4144*83ee113eSDavid van Moolenbroek }
4145*83ee113eSDavid van Moolenbroek 
4146*83ee113eSDavid van Moolenbroek /**
4147*83ee113eSDavid van Moolenbroek  *  Checks if received BOOTP/DHCPv4 packet is sane
4148*83ee113eSDavid van Moolenbroek  *
4149*83ee113eSDavid van Moolenbroek  * @param packet received, decoded packet
4150*83ee113eSDavid van Moolenbroek  *
4151*83ee113eSDavid van Moolenbroek  * @return 1 if packet is sane, 0 if it is not
4152*83ee113eSDavid van Moolenbroek  */
validate_packet(struct packet * packet)4153*83ee113eSDavid van Moolenbroek int validate_packet(struct packet *packet)
4154*83ee113eSDavid van Moolenbroek {
4155*83ee113eSDavid van Moolenbroek 	struct option_cache *oc = NULL;
4156*83ee113eSDavid van Moolenbroek 
4157*83ee113eSDavid van Moolenbroek 	oc = lookup_option (&dhcp_universe, packet->options,
4158*83ee113eSDavid van Moolenbroek 			    DHO_DHCP_CLIENT_IDENTIFIER);
4159*83ee113eSDavid van Moolenbroek 	if (oc) {
4160*83ee113eSDavid van Moolenbroek 		/* Let's check if client-identifier is sane */
4161*83ee113eSDavid van Moolenbroek 		if (oc->data.len == 0) {
4162*83ee113eSDavid van Moolenbroek 			log_debug("Dropped DHCPv4 packet with zero-length client-id");
4163*83ee113eSDavid van Moolenbroek 			return (0);
4164*83ee113eSDavid van Moolenbroek 
4165*83ee113eSDavid van Moolenbroek 		} else if (oc->data.len == 1) {
4166*83ee113eSDavid van Moolenbroek 			/*
4167*83ee113eSDavid van Moolenbroek 			 * RFC2132, section 9.14 states that minimum length of client-id
4168*83ee113eSDavid van Moolenbroek 			 * is 2.  We will allow single-character client-ids for now (for
4169*83ee113eSDavid van Moolenbroek 			 * backwards compatibility), but warn the user that support for
4170*83ee113eSDavid van Moolenbroek 			 * this is against the standard.
4171*83ee113eSDavid van Moolenbroek 			 */
4172*83ee113eSDavid van Moolenbroek 			log_debug("Accepted DHCPv4 packet with one-character client-id - "
4173*83ee113eSDavid van Moolenbroek 				"a future version of ISC DHCP will reject this");
4174*83ee113eSDavid van Moolenbroek 		}
4175*83ee113eSDavid van Moolenbroek 	} else {
4176*83ee113eSDavid van Moolenbroek 		/*
4177*83ee113eSDavid van Moolenbroek 		 * If hlen is 0 we don't have any identifier, we warn the user
4178*83ee113eSDavid van Moolenbroek 		 * but continue processing the packet as we can.
4179*83ee113eSDavid van Moolenbroek 		 */
4180*83ee113eSDavid van Moolenbroek 		if (packet->raw->hlen == 0) {
4181*83ee113eSDavid van Moolenbroek 			log_debug("Received DHCPv4 packet without client-id"
4182*83ee113eSDavid van Moolenbroek 				  " option and empty hlen field.");
4183*83ee113eSDavid van Moolenbroek 		}
4184*83ee113eSDavid van Moolenbroek 	}
4185*83ee113eSDavid van Moolenbroek 
4186*83ee113eSDavid van Moolenbroek 	/* @todo: Add checks for other received options */
4187*83ee113eSDavid van Moolenbroek 
4188*83ee113eSDavid van Moolenbroek 	return (1);
4189*83ee113eSDavid van Moolenbroek }
4190