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