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