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