options.c 104 KB
Newer Older
Ted Lemon's avatar
Ted Lemon committed
1
2
3
4
5
/* options.c

   DHCP options parsing and reassembly. */

/*
6
 * Copyright (c) 2004-2007 by Internet Systems Consortium, Inc. ("ISC")
7
 * Copyright (c) 1995-2003 by Internet Software Consortium
Ted Lemon's avatar
Ted Lemon committed
8
 *
9
10
11
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
Ted Lemon's avatar
Ted Lemon committed
12
 *
13
14
15
16
17
18
19
 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
Ted Lemon's avatar
Ted Lemon committed
20
 *
21
22
23
24
25
 *   Internet Systems Consortium, Inc.
 *   950 Charter Street
 *   Redwood City, CA 94063
 *   <info@isc.org>
 *   http://www.isc.org/
Ted Lemon's avatar
Ted Lemon committed
26
 *
27
 * This software has been written for Internet Systems Consortium
Ted Lemon's avatar
Ted Lemon committed
28
 * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
29
 * To learn more about Internet Systems Consortium, see
Ted Lemon's avatar
Ted Lemon committed
30
31
32
 * ``http://www.isc.org/''.  To learn more about Vixie Enterprises,
 * see ``http://www.vix.com''.   To learn more about Nominum, Inc., see
 * ``http://www.nominum.com''.
Ted Lemon's avatar
Ted Lemon committed
33
34
35
36
 */

#define DHCP_OPTION_DATA
#include "dhcpd.h"
37
#include <omapip/omapip_p.h>
38
#include <limits.h>
Ted Lemon's avatar
Ted Lemon committed
39

40
41
struct option *vendor_cfg_option;

42
43
44
45
static int pretty_text(char **, char *, const unsigned char **,
			 const unsigned char *, int);
static int pretty_domain(char **, char *, const unsigned char **,
			 const unsigned char *);
46

Ted Lemon's avatar
Ted Lemon committed
47
48
/* Parse all available options out of the specified packet. */

49
int parse_options (packet)
Ted Lemon's avatar
Ted Lemon committed
50
51
	struct packet *packet;
{
52
53
	struct option_cache *op = (struct option_cache *)0;

54
	/* Allocate a new option state. */
55
	if (!option_state_allocate (&packet -> options, MDL)) {
56
57
58
		packet -> options_valid = 0;
		return 0;
	}
Ted Lemon's avatar
Ted Lemon committed
59
60
61
62

	/* If we don't see the magic cookie, there's nothing to parse. */
	if (memcmp (packet -> raw -> options, DHCP_OPTIONS_COOKIE, 4)) {
		packet -> options_valid = 0;
63
		return 1;
Ted Lemon's avatar
Ted Lemon committed
64
65
66
67
	}

	/* Go through the options field, up to the end of the packet
	   or the End field. */
68
69
	if (!parse_option_buffer (packet -> options,
				  &packet -> raw -> options [4],
70
				  (packet -> packet_length -
71
				   DHCP_FIXED_NON_UDP - 4),
Ted Lemon's avatar
Ted Lemon committed
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
				  &dhcp_universe)) {

		/* STSN servers have a bug where they send a mangled
		   domain-name option, and whatever is beyond that in
		   the packet is junk.   Microsoft clients accept this,
		   which is probably why whoever implemented the STSN
		   server isn't aware of the problem yet.   To work around
		   this, we will accept corrupt packets from the server if
		   they contain a valid DHCP_MESSAGE_TYPE option, but
		   will not accept any corrupt client packets (the ISC DHCP
		   server is sufficiently widely used that it is probably
		   beneficial for it to be picky) and will not accept
		   packets whose type can't be determined. */

		if ((op = lookup_option (&dhcp_universe, packet -> options,
					 DHO_DHCP_MESSAGE_TYPE))) {
			if (!op -> data.data ||
			    (op -> data.data [0] != DHCPOFFER &&
			     op -> data.data [0] != DHCPACK &&
			     op -> data.data [0] != DHCPNAK))
				return 0;
		} else
			return 0;
	}
96

Ted Lemon's avatar
Ted Lemon committed
97
98
	/* If we parsed a DHCP Option Overload option, parse more
	   options out of the buffer(s) containing them. */
Ted Lemon's avatar
Ted Lemon committed
99
	if ((op = lookup_option (&dhcp_universe, packet -> options,
100
101
102
				 DHO_DHCP_OPTION_OVERLOAD))) {
		if (op -> data.data [0] & 1) {
			if (!parse_option_buffer
103
104
105
106
			    (packet -> options,
			     (unsigned char *)packet -> raw -> file,
			     sizeof packet -> raw -> file,
			     &dhcp_universe))
107
108
109
110
				return 0;
		}
		if (op -> data.data [0] & 2) {
			if (!parse_option_buffer
111
			    (packet -> options,
112
			     (unsigned char *)packet -> raw -> sname,
113
114
			     sizeof packet -> raw -> sname,
			     &dhcp_universe))
115
116
				return 0;
		}
Ted Lemon's avatar
Ted Lemon committed
117
	}
118
	packet -> options_valid = 1;
119
	return 1;
Ted Lemon's avatar
Ted Lemon committed
120
121
122
}

/* Parse options out of the specified buffer, storing addresses of option
David Hankins's avatar
David Hankins committed
123
124
 * values in packet->options.
 */
125
126
int parse_option_buffer (options, buffer, length, universe)
	struct option_state *options;
127
	const unsigned char *buffer;
128
	unsigned length;
129
	struct universe *universe;
Ted Lemon's avatar
Ted Lemon committed
130
{
131
	unsigned len, offset;
132
	unsigned code;
David Hankins's avatar
David Hankins committed
133
	struct option_cache *op = NULL, *nop = NULL;
134
	struct buffer *bp = (struct buffer *)0;
135
	struct option *option = NULL;
Ted Lemon's avatar
Ted Lemon committed
136

137
	if (!buffer_allocate (&bp, length, MDL)) {
138
		log_error ("no memory for option buffer.");
139
140
141
		return 0;
	}
	memcpy (bp -> data, buffer, length);
142
143

	for (offset = 0;
144
145
	     (offset + universe->tag_size) <= length &&
	     (code = universe->get_tag(buffer + offset)) != universe->end; ) {
146
147
		offset += universe->tag_size;

Ted Lemon's avatar
Ted Lemon committed
148
		/* Pad options don't have a length - just skip them. */
149
		if (code == DHO_PAD)
Ted Lemon's avatar
Ted Lemon committed
150
			continue;
151

152
		/* Don't look for length if the buffer isn't that big. */
153
		if ((offset + universe->length_size) > length) {
154
155
156
157
			len = 65536;
			goto bogus;
		}

158
		/* All other fields (except PAD and END handled above)
David Hankins's avatar
David Hankins committed
159
160
161
162
163
		 * have a length field, unless it's a DHCPv6 zero-length
		 * options space (eg any of the enterprise-id'd options).
		 *
		 * Zero-length-size option spaces basicaly consume the
		 * entire options buffer, so have at it.
164
		 */
David Hankins's avatar
David Hankins committed
165
166
167
168
		if (universe->get_length != NULL)
			len = universe->get_length(buffer + offset);
		else if (universe->length_size == 0)
			len = length - universe->tag_size;
169
		else {
David Hankins's avatar
David Hankins committed
170
171
172
173
			log_fatal("Improperly configured option space(%s): "
				  "may not have a nonzero length size "
				  "AND a NULL get_length function.",
				  universe->name);
174

175
176
177
178
			/* Silence compiler warnings. */
			return 0;
		}

179
180
181
182
		offset += universe->length_size;

		option_code_hash_lookup(&option, universe->code_hash, &code,
					0, MDL);
183

Ted Lemon's avatar
Ted Lemon committed
184
		/* If the length is outrageous, the options are bad. */
185
		if (offset + len > length) {
186
		      bogus:
187
188
189
			log_error ("parse_option_buffer: option %s (%u:%u) %s.",
				   option ? option->name : "<unknown>",
				   code, len, "larger than buffer");
190
			buffer_dereference (&bp, MDL);
191
			return 0;
Ted Lemon's avatar
Ted Lemon committed
192
		}
193

194
195
196
197
		/* If the option contains an encapsulation, parse it.   If
		   the parse fails, or the option isn't an encapsulation (by
		   far the most common case), or the option isn't entirely
		   an encapsulation, keep the raw data as well. */
198
199
200
		if (!(option &&
		      (option->format[0] == 'e' ||
		       option->format[0] == 'E') &&
David Hankins's avatar
David Hankins committed
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
		      (parse_encapsulated_suboptions(options, option,
						     bp->data + offset, len,
						     universe, NULL)))) {
			op = lookup_option(universe, options, code);

			if (op != NULL && universe->concat_duplicates) {
				struct data_string new;
				memset(&new, 0, sizeof new);
				if (!buffer_allocate(&new.buffer,
						     op->data.len + len,
						     MDL)) {
					log_error("parse_option_buffer: "
						  "No memory.");
					buffer_dereference(&bp, MDL);
					return 0;
				}
				/* Copy old option to new data object. */
				memcpy(new.buffer->data, op->data.data,
					op->data.len);
				/* Concat new option behind old. */
				memcpy(new.buffer->data + op->data.len,
					bp->data + offset, len);
				new.len = op->data.len + len;
				new.data = new.buffer->data;
				/* Save new concat'd object. */
				data_string_forget(&op->data, MDL);
				data_string_copy(&op->data, &new, MDL);
				data_string_forget(&new, MDL);
			} else if (op != NULL) {
				/* We must append this statement onto the
				 * end of the list.
				 */
				while (op->next != NULL)
					op = op->next;

				if (!option_cache_allocate(&nop, MDL)) {
					log_error("parse_option_buffer: "
						  "No memory.");
					buffer_dereference(&bp, MDL);
					return 0;
				}

				option_reference(&nop->option, op->option, MDL);

				nop->data.buffer = NULL;
				buffer_reference(&nop->data.buffer, bp, MDL);
247
				nop->data.data = bp->data + offset;
David Hankins's avatar
David Hankins committed
248
249
250
251
252
253
254
255
				nop->data.len = len;

				option_cache_reference(&op->next, nop, MDL);
				option_cache_dereference(&nop, MDL);
			} else {
				save_option_buffer(universe, options, bp,
						   bp->data + offset, len,
						   code, 1);
256
			}
257
		}
258
259
		option_dereference(&option, MDL);
		offset += len;
260
261
262
263
264
	}
	buffer_dereference (&bp, MDL);
	return 1;
}

265
266
267
268
269
/* If an option in an option buffer turns out to be an encapsulation,
   figure out what to do.   If we don't know how to de-encapsulate it,
   or it's not well-formed, return zero; otherwise, return 1, indicating
   that we succeeded in de-encapsulating it. */

270
struct universe *find_option_universe (struct option *eopt, const char *uname)
271
272
273
{
	int i;
	char *s, *t;
274
	struct universe *universe = (struct universe *)0;
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289

	/* Look for the E option in the option format. */
	s = strchr (eopt -> format, 'E');
	if (!s) {
		log_error ("internal encapsulation format error 1.");
		return 0;
	}
	/* Look for the universe name in the option format. */
	t = strchr (++s, '.');
	/* If there was no trailing '.', or there's something after the
	   trailing '.', the option is bogus and we can't use it. */
	if (!t || t [1]) {
		log_error ("internal encapsulation format error 2.");
		return 0;
	}
290
291
292
293
294
295
296
297
	if (t == s && uname) {
		for (i = 0; i < universe_count; i++) {
			if (!strcmp (universes [i] -> name, uname)) {
				universe = universes [i];
				break;
			}
		}
	} else if (t != s) {
298
299
300
301
302
303
304
305
306
		for (i = 0; i < universe_count; i++) {
			if (strlen (universes [i] -> name) == t - s &&
			    !memcmp (universes [i] -> name,
				     s, (unsigned)(t - s))) {
				universe = universes [i];
				break;
			}
		}
	}
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
	return universe;
}

/* If an option in an option buffer turns out to be an encapsulation,
   figure out what to do.   If we don't know how to de-encapsulate it,
   or it's not well-formed, return zero; otherwise, return 1, indicating
   that we succeeded in de-encapsulating it. */

int parse_encapsulated_suboptions (struct option_state *options,
				   struct option *eopt,
				   const unsigned char *buffer,
				   unsigned len, struct universe *eu,
				   const char *uname)
{
	int i;
	struct universe *universe = find_option_universe (eopt, uname);
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337

	/* If we didn't find the universe, we can't do anything with it
	   right now (e.g., we can't decode vendor options until we've
	   decoded the packet and executed the scopes that it matches). */
	if (!universe)
		return 0;
		
	/* If we don't have a decoding function for it, we can't decode
	   it. */
	if (!universe -> decode)
		return 0;

	i = (*universe -> decode) (options, buffer, len, universe);

	/* If there is stuff before the suboptions, we have to keep it. */
338
	if (eopt -> format [0] != 'E')
339
340
341
342
343
344
		return 0;
	/* Otherwise, return the status of the decode function. */
	return i;
}

int fqdn_universe_decode (struct option_state *options,
345
			  const unsigned char *buffer,
346
347
348
349
350
			  unsigned length, struct universe *u)
{
	struct buffer *bp = (struct buffer *)0;

	/* FQDN options have to be at least four bytes long. */
351
	if (length < 3)
352
353
354
355
356
357
358
359
360
361
362
363
364
		return 0;

	/* Save the contents of the option in a buffer. */
	if (!buffer_allocate (&bp, length + 4, MDL)) {
		log_error ("no memory for option buffer.");
		return 0;
	}
	memcpy (&bp -> data [3], buffer + 1, length - 1);

	if (buffer [0] & 4)	/* encoded */
		bp -> data [0] = 1;
	else
		bp -> data [0] = 0;
365
366
	if (!save_option_buffer(&fqdn_universe, options, bp,
				bp->data, 1, FQDN_ENCODED, 0)) {
367
368
369
370
371
372
373
374
375
	      bad:
		buffer_dereference (&bp, MDL);
		return 0;
	}

	if (buffer [0] & 1)	/* server-update */
		bp -> data [2] = 1;
	else
		bp -> data [2] = 0;
376
377
378
379
	if (buffer [0] & 2)	/* no-client-update */
		bp -> data [1] = 1;
	else
		bp -> data [1] = 0;
380
381
382
383
384
385
386
387
388

	/* XXX Ideally we should store the name in DNS format, so if the
	   XXX label isn't in DNS format, we convert it to DNS format,
	   XXX rather than converting labels specified in DNS format to
	   XXX the plain ASCII representation.   But that's hard, so
	   XXX not now. */

	/* Not encoded using DNS format? */
	if (!bp -> data [0]) {
389
390
		unsigned i;

391
392
393
394
395
396
		/* Some broken clients NUL-terminate this option. */
		if (buffer [length - 1] == 0) {
			--length;
			bp -> data [1] = 1;
		}

397
398
399
400
401
402
403
404
		/* Determine the length of the hostname component of the
		   name.  If the name contains no '.' character, it
		   represents a non-qualified label. */
		for (i = 3; i < length && buffer [i] != '.'; i++);
		i -= 3;

		/* Note: If the client sends a FQDN, the first '.' will
		   be used as a NUL terminator for the hostname. */
405
406
407
		if (i && (!save_option_buffer(&fqdn_universe, options, bp,
					      &bp->data[5], i,
					      FQDN_HOSTNAME, 0)))
408
409
			goto bad;
		/* Note: If the client sends a single label, the
410
		   FQDN_DOMAINNAME option won't be set. */
411
		if (length > 4 + i &&
412
		    (!save_option_buffer(&fqdn_universe, options, bp,
413
					 &bp -> data[6 + i], length - 4 - i,
414
					 FQDN_DOMAINNAME, 1)))
415
			goto bad;
416
		/* Also save the whole name. */
417
418
419
420
		if (length > 3) {
			if (!save_option_buffer(&fqdn_universe, options, bp,
						&bp -> data [5], length - 3,
						FQDN_FQDN, 1))
421
				goto bad;
422
		}
423
	} else {
424
425
426
427
428
429
430
		unsigned len;
		unsigned total_len = 0;
		unsigned first_len = 0;
		int terminated = 0;
		unsigned char *s;

		s = &bp -> data[5];
431

432
		while (s < &bp -> data[0] + length + 2) {
433
			len = *s;
434
435
			if (len > 63) {
				log_info ("fancy bits in fqdn option");
436
				return 0;
437
438
			}	
			if (len == 0) {
439
				terminated = 1;
440
				break;
441
			}
442
443
			if (s + len > &bp -> data [0] + length + 3) {
				log_info ("fqdn tag longer than buffer");
444
445
				return 0;
			}
446
447
448
449
450
451
452

			if (first_len == 0) {
				first_len = len;
			}

			*s = '.';
			s += len + 1;
453
			total_len += len + 1;
454
		}
455

456
457
458
459
460
461
462
		/* We wind up with a length that's one too many because
		   we shouldn't increment for the last label, but there's
		   no way to tell we're at the last label until we exit
		   the loop.   :'*/
		if (total_len > 0)
			total_len--;

463
464
465
466
		if (!terminated) {
			first_len = total_len;
		}

467
		if (first_len > 0 &&
468
469
470
		    !save_option_buffer(&fqdn_universe, options, bp,
					&bp -> data[6], first_len,
					FQDN_HOSTNAME, 0))
471
			goto bad;
472
		if (total_len > 0 && first_len != total_len) {
473
474
475
476
			if (!save_option_buffer(&fqdn_universe, options, bp,
						&bp->data[6 + first_len],
						total_len - first_len,
						FQDN_DOMAINNAME, 1))
477
				goto bad;
478
		}
479
480
481
		if (total_len > 0)
			if (!save_option_buffer (&fqdn_universe, options, bp,
						 &bp -> data [6], total_len,
482
						 FQDN_FQDN, 1))
483
				goto bad;
Ted Lemon's avatar
Ted Lemon committed
484
	}
485
486
487

	if (!save_option_buffer (&fqdn_universe, options, bp,
				 &bp -> data [1], 1,
488
				 FQDN_NO_CLIENT_UPDATE, 0))
489
490
491
	    goto bad;
	if (!save_option_buffer (&fqdn_universe, options, bp,
				 &bp -> data [2], 1,
492
				 FQDN_SERVER_UPDATE, 0))
493
494
495
496
		goto bad;

	if (!save_option_buffer (&fqdn_universe, options, bp,
				 &bp -> data [3], 1,
497
				 FQDN_RCODE1, 0))
498
499
500
		goto bad;
	if (!save_option_buffer (&fqdn_universe, options, bp,
				 &bp -> data [4], 1,
501
				 FQDN_RCODE2, 0))
502
503
		goto bad;

504
	buffer_dereference (&bp, MDL);
505
	return 1;
Ted Lemon's avatar
Ted Lemon committed
506
507
}

508
/* cons options into a big buffer, and then split them out into the
509
   three separate buffers if needed.  This allows us to cons up a set
510
511
   of vendor options using the same routine. */

512
513
int cons_options (inpacket, outpacket, lease, client_state,
		  mms, in_options, cfg_options,
514
		  scope, overload, terminate, bootpp, prl, vuname)
515
	struct packet *inpacket;
516
	struct dhcp_packet *outpacket;
517
	struct lease *lease;
518
	struct client_state *client_state;
519
	int mms;
520
521
	struct option_state *in_options;
	struct option_state *cfg_options;
522
	struct binding_scope **scope;
523
	int overload;	/* Overload flags that may be set. */
524
	int terminate;
525
	int bootpp;
526
	struct data_string *prl;
527
	const char *vuname;
528
{
529
#define PRIORITY_COUNT 300
530
	unsigned priority_list [PRIORITY_COUNT];
531
532
	int priority_len;
	unsigned char buffer [4096];	/* Really big buffer... */
533
	unsigned main_buffer_size, mb_max;
534
	unsigned mainbufix, agentix;
535
536
	unsigned option_size;
	unsigned length;
537
538
539
	int i;
	struct option_cache *op;
	struct data_string ds;
540
	pair pp, *hash;
541
	int need_endopt = 0;
542
543
	int ocount = 0;
	int ofbuf1=0, ofbuf2=0;
544
545

	memset (&ds, 0, sizeof ds);
546

547
548
549
550
	/* If there's a Maximum Message Size option in the incoming packet
	   and no alternate maximum message size has been specified, take the
	   one in the packet. */

551
	if (inpacket &&
552
	    (op = lookup_option (&dhcp_universe, inpacket -> options,
553
				 DHO_DHCP_MAX_MESSAGE_SIZE))) {
554
555
		evaluate_option_cache (&ds, inpacket,
				       lease, client_state, in_options,
556
				       cfg_options, scope, op, MDL);
557
558
559
560
561
562
		if (ds.len >= sizeof (u_int16_t)) {
			i = getUShort (ds.data);

			if(!mms || (i < mms))
				mms = i;
		}
563
		data_string_forget (&ds, MDL);
564
565
	}

566
	/* If the client has provided a maximum DHCP message size,
567
568
569
570
	   use that; otherwise, if it's BOOTP, only 64 bytes; otherwise
	   use up to the minimum IP MTU size (576 bytes). */
	/* XXX if a BOOTP client specifies a max message size, we will
	   honor it. */
571
572
573
574

	if (mms) {
		main_buffer_size = mms - DHCP_FIXED_LEN;

Ted Lemon's avatar
Ted Lemon committed
575
576
577
		/* Enforce a minimum packet size... */
		if (main_buffer_size < (576 - DHCP_FIXED_LEN))
			main_buffer_size = 576 - DHCP_FIXED_LEN;
578
579
580
581
582
583
584
585
586
	} else if (bootpp) {
		if (inpacket) {
			main_buffer_size =
				inpacket -> packet_length - DHCP_FIXED_LEN;
			if (main_buffer_size < 64)
				main_buffer_size = 64;
		} else
			main_buffer_size = 64;
	} else
587
588
		main_buffer_size = 576 - DHCP_FIXED_LEN;

589
	/* Set a hard limit at the size of the output buffer. */
590
591
592
593
	mb_max = sizeof(buffer) - (((overload & 1) ? DHCP_FILE_LEN : 0) +
				   ((overload & 2) ? DHCP_SNAME_LEN : 0));
	if (main_buffer_size > mb_max)
		main_buffer_size = mb_max;
594

595
596
597
	/* Preload the option priority list with protocol-mandatory options.
	 * This effectively gives these options the highest priority.
	 */
598
	priority_len = 0;
599
600
601
602
603
604
	priority_list[priority_len++] = DHO_DHCP_MESSAGE_TYPE;
	priority_list[priority_len++] = DHO_DHCP_SERVER_IDENTIFIER;
	priority_list[priority_len++] = DHO_DHCP_LEASE_TIME;
	priority_list[priority_len++] = DHO_DHCP_MESSAGE;
	priority_list[priority_len++] = DHO_DHCP_REQUESTED_ADDRESS;
	priority_list[priority_len++] = DHO_ASSOCIATED_IP;
605

606
	if (prl && prl -> len > 0) {
607
608
609
610
611
612
		if ((op = lookup_option (&dhcp_universe, cfg_options,
					 DHO_SUBNET_SELECTION))) {
			if (priority_len < PRIORITY_COUNT)
				priority_list [priority_len++] =
					DHO_SUBNET_SELECTION;
		}
613

614
		data_string_truncate (prl, (PRIORITY_COUNT - priority_len));
615

616
617
618
619
620
621
622
		for (i = 0; i < prl -> len; i++) {
			/* Prevent client from changing order of delivery
			   of relay agent information option. */
			if (prl -> data [i] != DHO_DHCP_AGENT_OPTIONS)
				priority_list [priority_len++] =
					prl -> data [i];
		}
623

624
		/* If the client doesn't request the FQDN option explicitly,
625
		 * to indicate priority, consider it lowest priority.  Fit
626
627
		 * in the packet if there is space.  Note that the option
		 * may only be included if the client supplied one.
628
		 */
629
630
631
		if ((priority_len < PRIORITY_COUNT) &&
		    (lookup_option(&dhcp_universe, inpacket->options,
				   DHO_FQDN) != NULL))
632
633
634
635
636
637
			priority_list[priority_len++] = DHO_FQDN;

		/* Some DHCP Servers will give the subnet-mask option if
		 * it is not on the parameter request list - so some client
		 * implementations have come to rely on this - so we will
		 * also make sure we supply this, at lowest priority.
638
639
640
641
642
		 *
		 * This is only done in response to DHCPDISCOVER or
		 * DHCPREQUEST messages, to avoid providing the option on
		 * DHCPINFORM or DHCPLEASEQUERY responses (if the client
		 * didn't request it).
643
		 */
644
645
646
		if ((priority_len < PRIORITY_COUNT) &&
		    ((inpacket->packet_type == DHCPDISCOVER) ||
		     (inpacket->packet_type == DHCPREQUEST)))
647
648
			priority_list[priority_len++] = DHO_SUBNET_MASK;

649
	} else {
650
		/* First, hardcode some more options that ought to be
651
652
653
654
655
656
657
658
		 * sent first...these are high priority to have in the
		 * packet.
		 */
		priority_list[priority_len++] = DHO_SUBNET_MASK;
		priority_list[priority_len++] = DHO_ROUTERS;
		priority_list[priority_len++] = DHO_DOMAIN_NAME_SERVERS;
		priority_list[priority_len++] = DHO_HOST_NAME;
		priority_list[priority_len++] = DHO_FQDN;
659

660
661
662
663
664
665
666
		/* Append a list of the standard DHCP options from the
		   standard DHCP option space.  Actually, if a site
		   option space hasn't been specified, we wind up
		   treating the dhcp option space as the site option
		   space, and the first for loop is skipped, because
		   it's slightly more general to do it this way,
		   taking the 1Q99 DHCP futures work into account. */
667
		if (cfg_options -> site_code_min) {
668
		    for (i = 0; i < OPTION_HASH_SIZE; i++) {
669
			hash = cfg_options -> universes [dhcp_universe.index];
670
671
			if (hash) {
			    for (pp = hash [i]; pp; pp = pp -> cdr) {
672
				op = (struct option_cache *)(pp -> car);
673
				if (op -> option -> code <
674
				    cfg_options -> site_code_min &&
675
676
677
				    priority_len < PRIORITY_COUNT &&
				    (op -> option -> code !=
				     DHO_DHCP_AGENT_OPTIONS))
678
679
					priority_list [priority_len++] =
						op -> option -> code;
680
			    }
681
			}
682
		    }
683
		}
684
685
686
687
688

		/* Now cycle through the site option space, or if there
		   is no site option space, we'll be cycling through the
		   dhcp option space. */
		for (i = 0; i < OPTION_HASH_SIZE; i++) {
689
690
691
		    hash = (cfg_options -> universes
			    [cfg_options -> site_universe]);
		    if (hash)
692
693
694
			for (pp = hash [i]; pp; pp = pp -> cdr) {
				op = (struct option_cache *)(pp -> car);
				if (op -> option -> code >=
695
				    cfg_options -> site_code_min &&
696
697
698
				    priority_len < PRIORITY_COUNT &&
				    (op -> option -> code !=
				     DHO_DHCP_AGENT_OPTIONS))
699
700
701
					priority_list [priority_len++] =
						op -> option -> code;
			}
702
703
		}

704
		/* Put any spaces that are encapsulated on the list,
David Hankins's avatar
David Hankins committed
705
		 * sort out whether they contain values later.
706
		 */
707
		for (i = 0; i < cfg_options -> universe_count; i++) {
708
		    if (universes[i]->enc_opt &&
709
710
711
712
713
714
715
			priority_len < PRIORITY_COUNT &&
			universes [i] -> enc_opt -> universe == &dhcp_universe)
		    {
			    if (universes [i] -> enc_opt -> code !=
				DHO_DHCP_AGENT_OPTIONS)
				    priority_list [priority_len++] =
					    universes [i] -> enc_opt -> code;
716
		    }
717
718
719
720
721
722
723
		}

		/* The vendor option space can't stand on its own, so always
		   add it to the list. */
		if (priority_len < PRIORITY_COUNT)
			priority_list [priority_len++] =
				DHO_VENDOR_ENCAPSULATED_OPTIONS;
724
725
	}

726
727
728
729
730
731
732
	/* Figure out the overload buffer offset(s). */
	if (overload) {
		ofbuf1 = main_buffer_size - 4;
		if (overload == 3)
			ofbuf2 = main_buffer_size - 4 + DHCP_FILE_LEN;
	}

733
	/* Copy the options into the big buffer... */
734
735
	option_size = store_options (&ocount, buffer,
				     (main_buffer_size - 4 +
736
737
				      ((overload & 1) ? DHCP_FILE_LEN : 0) +
				      ((overload & 2) ? DHCP_SNAME_LEN : 0)),
738
				     inpacket, lease, client_state,
739
				     in_options, cfg_options, scope,
740
				     priority_list, priority_len,
741
742
743
744
745
746
747
748
749
750
751
752
753
754
				     ofbuf1, ofbuf2, terminate, vuname);
	/* If store_options failed. */
	if (option_size == 0)
		return 0;
	if (overload) {
		if (ocount == 1 && (overload & 1))
			overload = 1;
		else if (ocount == 1 && (overload & 2))
			overload = 2;
		else if (ocount == 3)
			overload = 3;
		else
			overload = 0;
	}
755

756
	/* Put the cookie up front... */
757
	memcpy (outpacket -> options, DHCP_OPTIONS_COOKIE, 4);
758
759
760
761
762
763
	mainbufix = 4;

	/* If we're going to have to overload, store the overload
	   option at the beginning.  If we can, though, just store the
	   whole thing in the packet's option buffer and leave it at
	   that. */
764
765
766
767
	memcpy (&outpacket -> options [mainbufix],
		buffer, option_size);
	mainbufix += option_size;
	if (overload) {
768
		outpacket -> options [mainbufix++] = DHO_DHCP_OPTION_OVERLOAD;
769
		outpacket -> options [mainbufix++] = 1;
770
		outpacket -> options [mainbufix++] = overload;
771

772
		if (overload & 1) {
773
774
775
776
777
778
779
			memcpy (outpacket -> file,
				&buffer [ofbuf1], DHCP_FILE_LEN);
		}
		if (overload & 2) {
			if (ofbuf2) {
				memcpy (outpacket -> sname, &buffer [ofbuf2],
					DHCP_SNAME_LEN);
780
			} else {
781
782
				memcpy (outpacket -> sname, &buffer [ofbuf1],
					DHCP_SNAME_LEN);
783
784
785
			}
		}
	}
786
787
788
789
	agentix = mainbufix;
	if (mainbufix < main_buffer_size)
		need_endopt = 1;
	length = DHCP_FIXED_NON_UDP + mainbufix;
790

791
792
793
	/* Now hack in the agent options if there are any. */
	priority_list [0] = DHO_DHCP_AGENT_OPTIONS;
	priority_len = 1;
794
	agentix +=
795
		store_options (0, &outpacket -> options [agentix],
796
			       DHCP_OPTION_LEN - agentix,
797
			       inpacket, lease, client_state,
798
799
			       in_options, cfg_options, scope,
			       priority_list, priority_len,
800
			       0, 0, 0, (char *)0);
801
802

	/* Tack a DHO_END option onto the packet if we need to. */
803
	if (agentix < DHCP_OPTION_LEN && need_endopt)
804
805
806
807
		outpacket -> options [agentix++] = DHO_END;

	/* Figure out the length. */
	length = DHCP_FIXED_NON_UDP + agentix;
808
	return length;
809
810
}

David Hankins's avatar
David Hankins committed
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
/*
 * XXX: We currently special case collecting VSIO options.
 *      We should be able to handle this in a more generic fashion, by
 *      including any encapsulated options that are present and desired.
 *      This will look something like the VSIO handling VSIO code.
 *      We may also consider handling the ORO-like options within
 *      encapsulated spaces.
 */

struct vsio_state {
	char *buf;
	int buflen;
	int bufpos;
};

static void
vsio_options(struct option_cache *oc,
	     struct packet *packet,
	     struct lease *dummy_lease, 
	     struct client_state *dummy_client_state,
	     struct option_state *dummy_opt_state,
	     struct option_state *opt_state,
	     struct binding_scope **dummy_binding_scope,
	     struct universe *universe, 
	     void *void_vsio_state) {
	struct vsio_state *vs = (struct vsio_state *)void_vsio_state;
	struct data_string ds;
	int total_len;

	memset(&ds, 0, sizeof(ds));
	if (evaluate_option_cache(&ds, packet, NULL,
				  NULL, opt_state, NULL, 
				  &global_scope, oc, MDL)) {
		total_len = ds.len + universe->tag_size + universe->length_size;
		if (total_len <= (vs->buflen - vs->bufpos)) {
			if (universe->tag_size == 1) {
				vs->buf[vs->bufpos++] = oc->option->code;
			} else if (universe->tag_size == 2) {
849
850
				putUShort((unsigned char *)vs->buf+vs->bufpos,
					  oc->option->code);
David Hankins's avatar
David Hankins committed
851
852
				vs->bufpos += 2;
			} else if (universe->tag_size == 4) {
853
854
				putULong((unsigned char *)vs->buf+vs->bufpos,
					 oc->option->code);
David Hankins's avatar
David Hankins committed
855
856
857
858
859
				vs->bufpos += 4;
			}
			if (universe->length_size == 1) {
				vs->buf[vs->bufpos++] = ds.len;
			} else if (universe->length_size == 2) {
860
861
				putUShort((unsigned char *)vs->buf+vs->bufpos, 
					  ds.len);
David Hankins's avatar
David Hankins committed
862
863
				vs->bufpos += 2;
			} else if (universe->length_size == 4) {
864
865
				putULong((unsigned char *)vs->buf+vs->bufpos, 
					 ds.len);
David Hankins's avatar
David Hankins committed
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
				vs->bufpos += 4;
			}
			memcpy(vs->buf + vs->bufpos, ds.data, ds.len);
			vs->bufpos += ds.len;
		} else {
			log_debug("No space for option %d in VSIO space %s.",
		  		oc->option->code, universe->name);
		}
		data_string_forget(&ds, MDL);
	} else {
		log_error("Error evaluating option %d in VSIO space %s.",
		  	oc->option->code, universe->name);
	}
}

/*
 * Stores the options from the DHCPv6 universe into the buffer given.
 *
 * Required options are given as a 0-terminated list of option codes.
 * Once those are added, the ORO is consulted.
 */

int
store_options6(char *buf, int buflen, 
	       struct option_state *opt_state, 
	       struct packet *packet,
	       const int *required_opts,
	       struct data_string *oro) {
	int i, j;
	struct option_cache *oc;
	struct option *o;
	struct data_string ds;
	int bufpos;
	int oro_size;
	u_int16_t code;
	int in_required_opts;
	int vsio_option_code;
	int vsio_wanted;
	struct vsio_state vs;
905
	unsigned char *tmp;
David Hankins's avatar
David Hankins committed
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942

	bufpos = 0;
	vsio_wanted = 0;

	/*
	 * Find the option code for the VSIO universe.
	 */
	vsio_option_code = 0;
	o = vsio_universe.enc_opt;
	while (o != NULL) { 
		if (o->universe == &dhcpv6_universe) {
			vsio_option_code = o->code;
			break;
		} 
		o = o->universe->enc_opt;
	}
	if (vsio_option_code == 0) {
		log_fatal("No VSIO option code found.");
	}

	if (required_opts != NULL) {
		for (i=0; required_opts[i] != 0; i++) {
			if (required_opts[i] == vsio_option_code) {
				vsio_wanted = 1;
			}

			oc = lookup_option(&dhcpv6_universe, 
					   opt_state, required_opts[i]);
			if (oc == NULL) {
				continue;
			}
			memset(&ds, 0, sizeof(ds));

			if (evaluate_option_cache(&ds, packet, NULL,
						  NULL, opt_state, NULL, 
						  &global_scope, oc, MDL)) {
				if ((ds.len + 4) <= (buflen - bufpos)) {
943
					tmp = (unsigned char *)buf + bufpos;
David Hankins's avatar
David Hankins committed
944
					/* option tag */
945
					putUShort(tmp, required_opts[i]);
David Hankins's avatar
David Hankins committed
946
					/* option length */
947
					putUShort(tmp+2, ds.len);
David Hankins's avatar
David Hankins committed
948
					/* option data */
949
					memcpy(tmp+4, ds.data, ds.len);
David Hankins's avatar
David Hankins committed
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
					/* update position */
					bufpos += (4 + ds.len);
				} else {
					log_debug("No space for option %d",
						  required_opts[i]);
				}
				data_string_forget(&ds, MDL);
			} else {
				log_error("Error evaluating option %d",
				  	required_opts[i]);
			}
		}
	}

	if (oro == NULL) {
		oro_size = 0;
	} else {
		oro_size = oro->len / 2;
	}
	for (i=0; i<oro_size; i++) {
		memcpy(&code, oro->data+(i*2), 2);
		code = ntohs(code);

		/* 
		 * See if we've already included this option because
		 * it is required.
		 */
		in_required_opts = 0;
		if (required_opts != NULL) {
			for (j=0; required_opts[j] != 0; j++) {
				if (required_opts[j] == code) {
					in_required_opts = 1;
					break;
				}
			}
		}
		if (in_required_opts) {
			continue;
		}

		/*
		 * See if this is the VSIO option.
		 */
		if (code == vsio_option_code) {
			vsio_wanted = 1;
		}

		/* 
		 * Not already added, find this option.
		 */
		oc = lookup_option(&dhcpv6_universe, opt_state, code);
		if (oc == NULL) {
			continue;
		}
		memset(&ds, 0, sizeof(ds));
		if (evaluate_option_cache(&ds, packet, NULL, NULL, opt_state,
					  NULL, &global_scope, oc, MDL)) {
			if ((ds.len + 4) <= (buflen - bufpos)) {
1008
				tmp = (unsigned char *)buf + bufpos;
David Hankins's avatar
David Hankins committed
1009
				/* option tag */
1010
				putUShort(tmp, code);
David Hankins's avatar
David Hankins committed
1011
				/* option length */
1012
				putUShort(tmp+2, ds.len);
David Hankins's avatar
David Hankins committed
1013
				/* option data */
1014
				memcpy(tmp+4, ds.data, ds.len);
David Hankins's avatar
David Hankins committed
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
				/* update position */
				bufpos += (4 + ds.len);
			} else {
				log_debug("No space for option %d", code);
			}
			data_string_forget(&ds, MDL);
		} else {
			log_error("Error evaluating option %d", code);
		}
	}

	if (vsio_wanted) {
		for (i=0; i < opt_state->universe_count; i++) {
			if (opt_state->universes[i] != NULL) {
		    		o = universes[i]->enc_opt;
				if ((o != NULL) && 
				    (o->universe == &vsio_universe)) {
					/*
					 * Add the data from this VSIO option.
					 */
					vs.buf = buf;
					vs.buflen = buflen;
					vs.bufpos = bufpos+8;
					option_space_foreach(packet, NULL,
							     NULL, 
							     NULL, opt_state,
			     				     NULL, 
							     universes[i], 
							     (void *)&vs,
			     				     vsio_options);

					/* 
					 * If there was actually data here,
					 * add the "header".
					 */
					if (vs.bufpos > bufpos+8) {
1051
1052
1053
						tmp = (unsigned char *)buf +
						      bufpos;
						putUShort(tmp,
David Hankins's avatar
David Hankins committed
1054
							  vsio_option_code);
1055
						putUShort(tmp+2,
David Hankins's avatar
David Hankins committed
1056
							  vs.bufpos-bufpos-4);
1057
						putULong(tmp+4, o->code);
David Hankins's avatar
David Hankins committed
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068

						bufpos = vs.bufpos;
					}
				}
			}
		}
	}

	return bufpos;
}

1069
1070
/* Store all the requested options into the requested buffer. */

1071
int store_options (ocount, buffer, buflen, packet, lease, client_state,
1072
1073
		   in_options, cfg_options, scope, priority_list, priority_len,
		   first_cutoff, second_cutoff, terminate, vuname)
1074
	int *ocount;
1075
	unsigned char *buffer;
1076
	unsigned buflen;
1077
	struct packet *packet;
1078
	struct lease *lease;
1079
	struct client_state *client_state;
1080
1081
	struct option_state *in_options;
	struct option_state *cfg_options;
1082
	struct binding_scope **scope;
1083
	unsigned *priority_list;
1084
	int priority_len;
1085
	unsigned first_cutoff, second_cutoff;
1086
	int terminate;
1087
	const char *vuname;
1088
{
1089
	int bufix = 0, six = 0, tix = 0;
1090
1091
	int i;
	int ix;
1092
	int tto;
1093
	int bufend, sbufend;
1094
	struct data_string od;
1095
	struct option_cache *oc;
1096
	struct option *option=NULL;
1097
	unsigned code;
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113

	if (first_cutoff) {
	    if (first_cutoff >= buflen)
		log_fatal("%s:%d:store_options: Invalid first cutoff.", MDL);

	    bufend = first_cutoff;
	} else
	    bufend = buflen;

	if (second_cutoff) {
	    if (second_cutoff >= buflen)
		log_fatal("%s:%d:store_options: Invalid second cutoff.", MDL);

	    sbufend = second_cutoff;
	} else
	    sbufend = buflen;
1114

1115
1116
	memset (&od, 0, sizeof od);

1117
1118
1119
	/* Eliminate duplicate options from the parameter request list.
	 * Enforce RFC-mandated ordering of options that are present.
	 */
1120
	for (i = 0; i < priority_len - 1; i++) {
1121
		/* Eliminate duplicates. */
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
		tto = 0;
		for (ix = i + 1; ix < priority_len + tto; ix++) {
			if (tto)
				priority_list [ix - tto] =
					priority_list [ix];
			if (priority_list [i] == priority_list [ix]) {
				tto++;
				priority_len--;
			}
		}
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158

		/* Enforce ordering of SUBNET_MASK options, according to
		 * RFC2132 Section 3.3:
		 *
		 *   If both the subnet mask and the router option are
		 *   specified in a DHCP reply, the subnet mask option MUST
		 *   be first.
		 *
		 * This guidance does not specify what to do if the client
		 * PRL explicitly requests the options out of order, it is
		 * a general statement.
		 */
		if (priority_list[i] == DHO_SUBNET_MASK) {
			for (ix = i - 1 ; ix >= 0 ; ix--) {
				/* We know that anything before 'i' can only
				 * appear once.  So shovel the options to make
				 * room to bubble the subnet mask ahead, and
				 * then break out of the loop, we're done.
				 */
				if (priority_list[ix] == DHO_ROUTERS) {
					memmove(priority_list + ix + 1,
					        priority_list + ix, i - ix);
					priority_list[ix] = DHO_SUBNET_MASK;
					break;
				}
			}
		}
1159
	}
1160
1161
1162
1163

	/* Copy out the options in the order that they appear in the
	   priority list... */
	for (i = 0; i < priority_len; i++) {
1164
1165
1166
	    /* Number of bytes left to store (some may already
	       have been stored by a previous pass). */
	    unsigned length;
1167
	    int optstart, soptstart, toptstart;
1168
1169
1170
	    struct universe *u;
	    int have_encapsulation = 0;
	    struct data_string encapsulation;
1171
	    int splitup;
1172

1173
	    memset (&encapsulation, 0, sizeof encapsulation);
1174
1175
1176
1177
	    have_encapsulation = 0;

	    if (option != NULL)