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