Deleted Added
full compact
clparse.c (166602) clparse.c (228259)
1/* $OpenBSD: clparse.c,v 1.18 2004/09/15 18:15:18 henning Exp $ */
2
3/* Parser for dhclient config and lease files... */
4
5/*
6 * Copyright (c) 1997 The Internet Software Consortium.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 *
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of The Internet Software Consortium nor the names
19 * of its contributors may be used to endorse or promote products derived
20 * from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
23 * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
24 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26 * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
27 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
29 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
30 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
31 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
33 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 *
36 * This software has been written for the Internet Software Consortium
37 * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
38 * Enterprises. To learn more about the Internet Software Consortium,
39 * see ``http://www.vix.com/isc''. To learn more about Vixie
40 * Enterprises, see ``http://www.vix.com''.
41 */
42
43#include <sys/cdefs.h>
1/* $OpenBSD: clparse.c,v 1.18 2004/09/15 18:15:18 henning Exp $ */
2
3/* Parser for dhclient config and lease files... */
4
5/*
6 * Copyright (c) 1997 The Internet Software Consortium.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 *
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of The Internet Software Consortium nor the names
19 * of its contributors may be used to endorse or promote products derived
20 * from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
23 * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
24 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26 * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
27 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
29 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
30 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
31 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
33 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 *
36 * This software has been written for the Internet Software Consortium
37 * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
38 * Enterprises. To learn more about the Internet Software Consortium,
39 * see ``http://www.vix.com/isc''. To learn more about Vixie
40 * Enterprises, see ``http://www.vix.com''.
41 */
42
43#include <sys/cdefs.h>
44__FBSDID("$FreeBSD: head/sbin/dhclient/clparse.c 166602 2007-02-09 17:50:26Z emaste $");
44__FBSDID("$FreeBSD: head/sbin/dhclient/clparse.c 228259 2011-12-04 14:44:31Z dumbbell $");
45
46#include "dhcpd.h"
47#include "dhctoken.h"
48
49struct client_config top_level_config;
50struct interface_info *dummy_interfaces;
51extern struct interface_info *ifi;
52
53char client_script_name[] = "/sbin/dhclient-script";
54
55/*
56 * client-conf-file :== client-declarations EOF
57 * client-declarations :== <nil>
58 * | client-declaration
59 * | client-declarations client-declaration
60 */
61int
62read_client_conf(void)
63{
64 FILE *cfile;
65 char *val;
66 int token;
67 struct client_config *config;
68
69 new_parse(path_dhclient_conf);
70
71 /* Set up the initial dhcp option universe. */
72 initialize_universes();
73
74 /* Initialize the top level client configuration. */
75 memset(&top_level_config, 0, sizeof(top_level_config));
76
77 /* Set some defaults... */
78 top_level_config.timeout = 60;
79 top_level_config.select_interval = 0;
80 top_level_config.reboot_timeout = 10;
81 top_level_config.retry_interval = 300;
82 top_level_config.backoff_cutoff = 15;
83 top_level_config.initial_interval = 3;
84 top_level_config.bootp_policy = ACCEPT;
85 top_level_config.script_name = client_script_name;
86 top_level_config.requested_options
87 [top_level_config.requested_option_count++] = DHO_SUBNET_MASK;
88 top_level_config.requested_options
89 [top_level_config.requested_option_count++] = DHO_BROADCAST_ADDRESS;
90 top_level_config.requested_options
91 [top_level_config.requested_option_count++] = DHO_TIME_OFFSET;
92 top_level_config.requested_options
93 [top_level_config.requested_option_count++] = DHO_CLASSLESS_ROUTES;
94 top_level_config.requested_options
95 [top_level_config.requested_option_count++] = DHO_ROUTERS;
96 top_level_config.requested_options
97 [top_level_config.requested_option_count++] = DHO_DOMAIN_NAME;
98 top_level_config.requested_options
99 [top_level_config.requested_option_count++] =
100 DHO_DOMAIN_NAME_SERVERS;
101 top_level_config.requested_options
102 [top_level_config.requested_option_count++] = DHO_HOST_NAME;
45
46#include "dhcpd.h"
47#include "dhctoken.h"
48
49struct client_config top_level_config;
50struct interface_info *dummy_interfaces;
51extern struct interface_info *ifi;
52
53char client_script_name[] = "/sbin/dhclient-script";
54
55/*
56 * client-conf-file :== client-declarations EOF
57 * client-declarations :== <nil>
58 * | client-declaration
59 * | client-declarations client-declaration
60 */
61int
62read_client_conf(void)
63{
64 FILE *cfile;
65 char *val;
66 int token;
67 struct client_config *config;
68
69 new_parse(path_dhclient_conf);
70
71 /* Set up the initial dhcp option universe. */
72 initialize_universes();
73
74 /* Initialize the top level client configuration. */
75 memset(&top_level_config, 0, sizeof(top_level_config));
76
77 /* Set some defaults... */
78 top_level_config.timeout = 60;
79 top_level_config.select_interval = 0;
80 top_level_config.reboot_timeout = 10;
81 top_level_config.retry_interval = 300;
82 top_level_config.backoff_cutoff = 15;
83 top_level_config.initial_interval = 3;
84 top_level_config.bootp_policy = ACCEPT;
85 top_level_config.script_name = client_script_name;
86 top_level_config.requested_options
87 [top_level_config.requested_option_count++] = DHO_SUBNET_MASK;
88 top_level_config.requested_options
89 [top_level_config.requested_option_count++] = DHO_BROADCAST_ADDRESS;
90 top_level_config.requested_options
91 [top_level_config.requested_option_count++] = DHO_TIME_OFFSET;
92 top_level_config.requested_options
93 [top_level_config.requested_option_count++] = DHO_CLASSLESS_ROUTES;
94 top_level_config.requested_options
95 [top_level_config.requested_option_count++] = DHO_ROUTERS;
96 top_level_config.requested_options
97 [top_level_config.requested_option_count++] = DHO_DOMAIN_NAME;
98 top_level_config.requested_options
99 [top_level_config.requested_option_count++] =
100 DHO_DOMAIN_NAME_SERVERS;
101 top_level_config.requested_options
102 [top_level_config.requested_option_count++] = DHO_HOST_NAME;
103 top_level_config.requested_options
104 [top_level_config.requested_option_count++] = DHO_DOMAIN_SEARCH;
103
104 if ((cfile = fopen(path_dhclient_conf, "r")) != NULL) {
105 do {
106 token = peek_token(&val, cfile);
107 if (token == EOF)
108 break;
109 parse_client_statement(cfile, NULL, &top_level_config);
110 } while (1);
111 token = next_token(&val, cfile); /* Clear the peek buffer */
112 fclose(cfile);
113 }
114
115 /*
116 * Set up state and config structures for clients that don't
117 * have per-interface configuration declarations.
118 */
119 config = NULL;
120 if (!ifi->client) {
121 ifi->client = malloc(sizeof(struct client_state));
122 if (!ifi->client)
123 error("no memory for client state.");
124 memset(ifi->client, 0, sizeof(*(ifi->client)));
125 }
126 if (!ifi->client->config) {
127 if (!config) {
128 config = malloc(sizeof(struct client_config));
129 if (!config)
130 error("no memory for client config.");
131 memcpy(config, &top_level_config,
132 sizeof(top_level_config));
133 }
134 ifi->client->config = config;
135 }
136
137 return (!warnings_occurred);
138}
139
140/*
141 * lease-file :== client-lease-statements EOF
142 * client-lease-statements :== <nil>
143 * | client-lease-statements LEASE client-lease-statement
144 */
145void
146read_client_leases(void)
147{
148 FILE *cfile;
149 char *val;
150 int token;
151
152 new_parse(path_dhclient_db);
153
154 /* Open the lease file. If we can't open it, just return -
155 we can safely trust the server to remember our state. */
156 if ((cfile = fopen(path_dhclient_db, "r")) == NULL)
157 return;
158 do {
159 token = next_token(&val, cfile);
160 if (token == EOF)
161 break;
162 if (token != LEASE) {
163 warning("Corrupt lease file - possible data loss!");
164 skip_to_semi(cfile);
165 break;
166 } else
167 parse_client_lease_statement(cfile, 0);
168
169 } while (1);
170 fclose(cfile);
171}
172
173/*
174 * client-declaration :==
175 * SEND option-decl |
176 * DEFAULT option-decl |
177 * SUPERSEDE option-decl |
178 * PREPEND option-decl |
179 * APPEND option-decl |
180 * hardware-declaration |
181 * REQUEST option-list |
182 * REQUIRE option-list |
183 * TIMEOUT number |
184 * RETRY number |
185 * REBOOT number |
186 * SELECT_TIMEOUT number |
187 * SCRIPT string |
188 * interface-declaration |
189 * LEASE client-lease-statement |
190 * ALIAS client-lease-statement
191 */
192void
193parse_client_statement(FILE *cfile, struct interface_info *ip,
194 struct client_config *config)
195{
196 int token;
197 char *val;
198 struct option *option;
199
200 switch (next_token(&val, cfile)) {
201 case SEND:
202 parse_option_decl(cfile, &config->send_options[0]);
203 return;
204 case DEFAULT:
205 option = parse_option_decl(cfile, &config->defaults[0]);
206 if (option)
207 config->default_actions[option->code] = ACTION_DEFAULT;
208 return;
209 case SUPERSEDE:
210 option = parse_option_decl(cfile, &config->defaults[0]);
211 if (option)
212 config->default_actions[option->code] =
213 ACTION_SUPERSEDE;
214 return;
215 case APPEND:
216 option = parse_option_decl(cfile, &config->defaults[0]);
217 if (option)
218 config->default_actions[option->code] = ACTION_APPEND;
219 return;
220 case PREPEND:
221 option = parse_option_decl(cfile, &config->defaults[0]);
222 if (option)
223 config->default_actions[option->code] = ACTION_PREPEND;
224 return;
225 case MEDIA:
226 parse_string_list(cfile, &config->media, 1);
227 return;
228 case HARDWARE:
229 if (ip)
230 parse_hardware_param(cfile, &ip->hw_address);
231 else {
232 parse_warn("hardware address parameter %s",
233 "not allowed here.");
234 skip_to_semi(cfile);
235 }
236 return;
237 case REQUEST:
238 config->requested_option_count =
239 parse_option_list(cfile, config->requested_options);
240 return;
241 case REQUIRE:
242 memset(config->required_options, 0,
243 sizeof(config->required_options));
244 parse_option_list(cfile, config->required_options);
245 return;
246 case TIMEOUT:
247 parse_lease_time(cfile, &config->timeout);
248 return;
249 case RETRY:
250 parse_lease_time(cfile, &config->retry_interval);
251 return;
252 case SELECT_TIMEOUT:
253 parse_lease_time(cfile, &config->select_interval);
254 return;
255 case REBOOT:
256 parse_lease_time(cfile, &config->reboot_timeout);
257 return;
258 case BACKOFF_CUTOFF:
259 parse_lease_time(cfile, &config->backoff_cutoff);
260 return;
261 case INITIAL_INTERVAL:
262 parse_lease_time(cfile, &config->initial_interval);
263 return;
264 case SCRIPT:
265 config->script_name = parse_string(cfile);
266 return;
267 case INTERFACE:
268 if (ip)
269 parse_warn("nested interface declaration.");
270 parse_interface_declaration(cfile, config);
271 return;
272 case LEASE:
273 parse_client_lease_statement(cfile, 1);
274 return;
275 case ALIAS:
276 parse_client_lease_statement(cfile, 2);
277 return;
278 case REJECT:
279 parse_reject_statement(cfile, config);
280 return;
281 default:
282 parse_warn("expecting a statement.");
283 skip_to_semi(cfile);
284 break;
285 }
286 token = next_token(&val, cfile);
287 if (token != SEMI) {
288 parse_warn("semicolon expected.");
289 skip_to_semi(cfile);
290 }
291}
292
293int
294parse_X(FILE *cfile, u_int8_t *buf, int max)
295{
296 int token;
297 char *val;
298 int len;
299
300 token = peek_token(&val, cfile);
301 if (token == NUMBER_OR_NAME || token == NUMBER) {
302 len = 0;
303 do {
304 token = next_token(&val, cfile);
305 if (token != NUMBER && token != NUMBER_OR_NAME) {
306 parse_warn("expecting hexadecimal constant.");
307 skip_to_semi(cfile);
308 return (0);
309 }
310 convert_num(&buf[len], val, 16, 8);
311 if (len++ > max) {
312 parse_warn("hexadecimal constant too long.");
313 skip_to_semi(cfile);
314 return (0);
315 }
316 token = peek_token(&val, cfile);
317 if (token == COLON)
318 token = next_token(&val, cfile);
319 } while (token == COLON);
320 val = (char *)buf;
321 } else if (token == STRING) {
322 token = next_token(&val, cfile);
323 len = strlen(val);
324 if (len + 1 > max) {
325 parse_warn("string constant too long.");
326 skip_to_semi(cfile);
327 return (0);
328 }
329 memcpy(buf, val, len + 1);
330 } else {
331 parse_warn("expecting string or hexadecimal data");
332 skip_to_semi(cfile);
333 return (0);
334 }
335 return (len);
336}
337
338/*
339 * option-list :== option_name |
340 * option_list COMMA option_name
341 */
342int
343parse_option_list(FILE *cfile, u_int8_t *list)
344{
345 int ix, i;
346 int token;
347 char *val;
348
349 ix = 0;
350 do {
351 token = next_token(&val, cfile);
352 if (!is_identifier(token)) {
353 parse_warn("expected option name.");
354 skip_to_semi(cfile);
355 return (0);
356 }
357 for (i = 0; i < 256; i++)
358 if (!strcasecmp(dhcp_options[i].name, val))
359 break;
360
361 if (i == 256) {
362 parse_warn("%s: unexpected option name.", val);
363 skip_to_semi(cfile);
364 return (0);
365 }
366 list[ix++] = i;
367 if (ix == 256) {
368 parse_warn("%s: too many options.", val);
369 skip_to_semi(cfile);
370 return (0);
371 }
372 token = next_token(&val, cfile);
373 } while (token == COMMA);
374 if (token != SEMI) {
375 parse_warn("expecting semicolon.");
376 skip_to_semi(cfile);
377 return (0);
378 }
379 return (ix);
380}
381
382/*
383 * interface-declaration :==
384 * INTERFACE string LBRACE client-declarations RBRACE
385 */
386void
387parse_interface_declaration(FILE *cfile, struct client_config *outer_config)
388{
389 int token;
390 char *val;
391 struct interface_info *ip;
392
393 token = next_token(&val, cfile);
394 if (token != STRING) {
395 parse_warn("expecting interface name (in quotes).");
396 skip_to_semi(cfile);
397 return;
398 }
399
400 ip = interface_or_dummy(val);
401
402 if (!ip->client)
403 make_client_state(ip);
404
405 if (!ip->client->config)
406 make_client_config(ip, outer_config);
407
408 token = next_token(&val, cfile);
409 if (token != LBRACE) {
410 parse_warn("expecting left brace.");
411 skip_to_semi(cfile);
412 return;
413 }
414
415 do {
416 token = peek_token(&val, cfile);
417 if (token == EOF) {
418 parse_warn("unterminated interface declaration.");
419 return;
420 }
421 if (token == RBRACE)
422 break;
423 parse_client_statement(cfile, ip, ip->client->config);
424 } while (1);
425 token = next_token(&val, cfile);
426}
427
428struct interface_info *
429interface_or_dummy(char *name)
430{
431 struct interface_info *ip;
432
433 /* Find the interface (if any) that matches the name. */
434 if (!strcmp(ifi->name, name))
435 return (ifi);
436
437 /* If it's not a real interface, see if it's on the dummy list. */
438 for (ip = dummy_interfaces; ip; ip = ip->next)
439 if (!strcmp(ip->name, name))
440 return (ip);
441
442 /*
443 * If we didn't find an interface, make a dummy interface as a
444 * placeholder.
445 */
446 ip = malloc(sizeof(*ip));
447 if (!ip)
448 error("Insufficient memory to record interface %s", name);
449 memset(ip, 0, sizeof(*ip));
450 strlcpy(ip->name, name, IFNAMSIZ);
451 ip->next = dummy_interfaces;
452 dummy_interfaces = ip;
453 return (ip);
454}
455
456void
457make_client_state(struct interface_info *ip)
458{
459 ip->client = malloc(sizeof(*(ip->client)));
460 if (!ip->client)
461 error("no memory for state on %s", ip->name);
462 memset(ip->client, 0, sizeof(*(ip->client)));
463}
464
465void
466make_client_config(struct interface_info *ip, struct client_config *config)
467{
468 ip->client->config = malloc(sizeof(struct client_config));
469 if (!ip->client->config)
470 error("no memory for config for %s", ip->name);
471 memset(ip->client->config, 0, sizeof(*(ip->client->config)));
472 memcpy(ip->client->config, config, sizeof(*config));
473}
474
475/*
476 * client-lease-statement :==
477 * RBRACE client-lease-declarations LBRACE
478 *
479 * client-lease-declarations :==
480 * <nil> |
481 * client-lease-declaration |
482 * client-lease-declarations client-lease-declaration
483 */
484void
485parse_client_lease_statement(FILE *cfile, int is_static)
486{
487 struct client_lease *lease, *lp, *pl;
488 struct interface_info *ip;
489 int token;
490 char *val;
491
492 token = next_token(&val, cfile);
493 if (token != LBRACE) {
494 parse_warn("expecting left brace.");
495 skip_to_semi(cfile);
496 return;
497 }
498
499 lease = malloc(sizeof(struct client_lease));
500 if (!lease)
501 error("no memory for lease.");
502 memset(lease, 0, sizeof(*lease));
503 lease->is_static = is_static;
504
505 ip = NULL;
506
507 do {
508 token = peek_token(&val, cfile);
509 if (token == EOF) {
510 parse_warn("unterminated lease declaration.");
511 return;
512 }
513 if (token == RBRACE)
514 break;
515 parse_client_lease_declaration(cfile, lease, &ip);
516 } while (1);
517 token = next_token(&val, cfile);
518
519 /* If the lease declaration didn't include an interface
520 * declaration that we recognized, it's of no use to us.
521 */
522 if (!ip) {
523 free_client_lease(lease);
524 return;
525 }
526
527 /* Make sure there's a client state structure... */
528 if (!ip->client)
529 make_client_state(ip);
530
531 /* If this is an alias lease, it doesn't need to be sorted in. */
532 if (is_static == 2) {
533 ip->client->alias = lease;
534 return;
535 }
536
537 /*
538 * The new lease may supersede a lease that's not the active
539 * lease but is still on the lease list, so scan the lease list
540 * looking for a lease with the same address, and if we find it,
541 * toss it.
542 */
543 pl = NULL;
544 for (lp = ip->client->leases; lp; lp = lp->next) {
545 if (lp->address.len == lease->address.len &&
546 !memcmp(lp->address.iabuf, lease->address.iabuf,
547 lease->address.len)) {
548 if (pl)
549 pl->next = lp->next;
550 else
551 ip->client->leases = lp->next;
552 free_client_lease(lp);
553 break;
554 }
555 }
556
557 /*
558 * If this is a preloaded lease, just put it on the list of
559 * recorded leases - don't make it the active lease.
560 */
561 if (is_static) {
562 lease->next = ip->client->leases;
563 ip->client->leases = lease;
564 return;
565 }
566
567 /*
568 * The last lease in the lease file on a particular interface is
569 * the active lease for that interface. Of course, we don't
570 * know what the last lease in the file is until we've parsed
571 * the whole file, so at this point, we assume that the lease we
572 * just parsed is the active lease for its interface. If
573 * there's already an active lease for the interface, and this
574 * lease is for the same ip address, then we just toss the old
575 * active lease and replace it with this one. If this lease is
576 * for a different address, then if the old active lease has
577 * expired, we dump it; if not, we put it on the list of leases
578 * for this interface which are still valid but no longer
579 * active.
580 */
581 if (ip->client->active) {
582 if (ip->client->active->expiry < cur_time)
583 free_client_lease(ip->client->active);
584 else if (ip->client->active->address.len ==
585 lease->address.len &&
586 !memcmp(ip->client->active->address.iabuf,
587 lease->address.iabuf, lease->address.len))
588 free_client_lease(ip->client->active);
589 else {
590 ip->client->active->next = ip->client->leases;
591 ip->client->leases = ip->client->active;
592 }
593 }
594 ip->client->active = lease;
595
596 /* Phew. */
597}
598
599/*
600 * client-lease-declaration :==
601 * BOOTP |
602 * INTERFACE string |
603 * FIXED_ADDR ip_address |
604 * FILENAME string |
605 * SERVER_NAME string |
606 * OPTION option-decl |
607 * RENEW time-decl |
608 * REBIND time-decl |
609 * EXPIRE time-decl
610 */
611void
612parse_client_lease_declaration(FILE *cfile, struct client_lease *lease,
613 struct interface_info **ipp)
614{
615 int token;
616 char *val;
617 struct interface_info *ip;
618
619 switch (next_token(&val, cfile)) {
620 case BOOTP:
621 lease->is_bootp = 1;
622 break;
623 case INTERFACE:
624 token = next_token(&val, cfile);
625 if (token != STRING) {
626 parse_warn("expecting interface name (in quotes).");
627 skip_to_semi(cfile);
628 break;
629 }
630 ip = interface_or_dummy(val);
631 *ipp = ip;
632 break;
633 case FIXED_ADDR:
634 if (!parse_ip_addr(cfile, &lease->address))
635 return;
636 break;
637 case MEDIUM:
638 parse_string_list(cfile, &lease->medium, 0);
639 return;
640 case FILENAME:
641 lease->filename = parse_string(cfile);
642 return;
643 case SERVER_NAME:
644 lease->server_name = parse_string(cfile);
645 return;
646 case RENEW:
647 lease->renewal = parse_date(cfile);
648 return;
649 case REBIND:
650 lease->rebind = parse_date(cfile);
651 return;
652 case EXPIRE:
653 lease->expiry = parse_date(cfile);
654 return;
655 case OPTION:
656 parse_option_decl(cfile, lease->options);
657 return;
658 default:
659 parse_warn("expecting lease declaration.");
660 skip_to_semi(cfile);
661 break;
662 }
663 token = next_token(&val, cfile);
664 if (token != SEMI) {
665 parse_warn("expecting semicolon.");
666 skip_to_semi(cfile);
667 }
668}
669
670struct option *
671parse_option_decl(FILE *cfile, struct option_data *options)
672{
673 char *val;
674 int token;
675 u_int8_t buf[4];
676 u_int8_t hunkbuf[1024];
677 int hunkix = 0;
678 char *vendor;
679 char *fmt;
680 struct universe *universe;
681 struct option *option;
682 struct iaddr ip_addr;
683 u_int8_t *dp;
684 int len;
685 int nul_term = 0;
686
687 token = next_token(&val, cfile);
688 if (!is_identifier(token)) {
689 parse_warn("expecting identifier after option keyword.");
690 if (token != SEMI)
691 skip_to_semi(cfile);
692 return (NULL);
693 }
694 if ((vendor = strdup(val)) == NULL)
695 error("no memory for vendor information.");
696
697 token = peek_token(&val, cfile);
698 if (token == DOT) {
699 /* Go ahead and take the DOT token... */
700 token = next_token(&val, cfile);
701
702 /* The next token should be an identifier... */
703 token = next_token(&val, cfile);
704 if (!is_identifier(token)) {
705 parse_warn("expecting identifier after '.'");
706 if (token != SEMI)
707 skip_to_semi(cfile);
708 return (NULL);
709 }
710
711 /* Look up the option name hash table for the specified
712 vendor. */
713 universe = ((struct universe *)hash_lookup(&universe_hash,
714 (unsigned char *)vendor, 0));
715 /* If it's not there, we can't parse the rest of the
716 declaration. */
717 if (!universe) {
718 parse_warn("no vendor named %s.", vendor);
719 skip_to_semi(cfile);
720 return (NULL);
721 }
722 } else {
723 /* Use the default hash table, which contains all the
724 standard dhcp option names. */
725 val = vendor;
726 universe = &dhcp_universe;
727 }
728
729 /* Look up the actual option info... */
730 option = (struct option *)hash_lookup(universe->hash,
731 (unsigned char *)val, 0);
732
733 /* If we didn't get an option structure, it's an undefined option. */
734 if (!option) {
735 if (val == vendor)
736 parse_warn("no option named %s", val);
737 else
738 parse_warn("no option named %s for vendor %s",
739 val, vendor);
740 skip_to_semi(cfile);
741 return (NULL);
742 }
743
744 /* Free the initial identifier token. */
745 free(vendor);
746
747 /* Parse the option data... */
748 do {
749 for (fmt = option->format; *fmt; fmt++) {
750 if (*fmt == 'A')
751 break;
752 switch (*fmt) {
753 case 'X':
754 len = parse_X(cfile, &hunkbuf[hunkix],
755 sizeof(hunkbuf) - hunkix);
756 hunkix += len;
757 break;
758 case 't': /* Text string... */
759 token = next_token(&val, cfile);
760 if (token != STRING) {
761 parse_warn("expecting string.");
762 skip_to_semi(cfile);
763 return (NULL);
764 }
765 len = strlen(val);
766 if (hunkix + len + 1 > sizeof(hunkbuf)) {
767 parse_warn("option data buffer %s",
768 "overflow");
769 skip_to_semi(cfile);
770 return (NULL);
771 }
772 memcpy(&hunkbuf[hunkix], val, len + 1);
773 nul_term = 1;
774 hunkix += len;
775 break;
776 case 'I': /* IP address. */
777 if (!parse_ip_addr(cfile, &ip_addr))
778 return (NULL);
779 len = ip_addr.len;
780 dp = ip_addr.iabuf;
781alloc:
782 if (hunkix + len > sizeof(hunkbuf)) {
783 parse_warn("option data buffer "
784 "overflow");
785 skip_to_semi(cfile);
786 return (NULL);
787 }
788 memcpy(&hunkbuf[hunkix], dp, len);
789 hunkix += len;
790 break;
791 case 'L': /* Unsigned 32-bit integer... */
792 case 'l': /* Signed 32-bit integer... */
793 token = next_token(&val, cfile);
794 if (token != NUMBER) {
795need_number:
796 parse_warn("expecting number.");
797 if (token != SEMI)
798 skip_to_semi(cfile);
799 return (NULL);
800 }
801 convert_num(buf, val, 0, 32);
802 len = 4;
803 dp = buf;
804 goto alloc;
805 case 's': /* Signed 16-bit integer. */
806 case 'S': /* Unsigned 16-bit integer. */
807 token = next_token(&val, cfile);
808 if (token != NUMBER)
809 goto need_number;
810 convert_num(buf, val, 0, 16);
811 len = 2;
812 dp = buf;
813 goto alloc;
814 case 'b': /* Signed 8-bit integer. */
815 case 'B': /* Unsigned 8-bit integer. */
816 token = next_token(&val, cfile);
817 if (token != NUMBER)
818 goto need_number;
819 convert_num(buf, val, 0, 8);
820 len = 1;
821 dp = buf;
822 goto alloc;
823 case 'f': /* Boolean flag. */
824 token = next_token(&val, cfile);
825 if (!is_identifier(token)) {
826 parse_warn("expecting identifier.");
827bad_flag:
828 if (token != SEMI)
829 skip_to_semi(cfile);
830 return (NULL);
831 }
832 if (!strcasecmp(val, "true") ||
833 !strcasecmp(val, "on"))
834 buf[0] = 1;
835 else if (!strcasecmp(val, "false") ||
836 !strcasecmp(val, "off"))
837 buf[0] = 0;
838 else {
839 parse_warn("expecting boolean.");
840 goto bad_flag;
841 }
842 len = 1;
843 dp = buf;
844 goto alloc;
845 default:
846 warning("Bad format %c in parse_option_param.",
847 *fmt);
848 skip_to_semi(cfile);
849 return (NULL);
850 }
851 }
852 token = next_token(&val, cfile);
853 } while (*fmt == 'A' && token == COMMA);
854
855 if (token != SEMI) {
856 parse_warn("semicolon expected.");
857 skip_to_semi(cfile);
858 return (NULL);
859 }
860
861 options[option->code].data = malloc(hunkix + nul_term);
862 if (!options[option->code].data)
863 error("out of memory allocating option data.");
864 memcpy(options[option->code].data, hunkbuf, hunkix + nul_term);
865 options[option->code].len = hunkix;
866 return (option);
867}
868
869void
870parse_string_list(FILE *cfile, struct string_list **lp, int multiple)
871{
872 int token;
873 char *val;
874 struct string_list *cur, *tmp;
875
876 /* Find the last medium in the media list. */
877 if (*lp)
878 for (cur = *lp; cur->next; cur = cur->next)
879 ; /* nothing */
880 else
881 cur = NULL;
882
883 do {
884 token = next_token(&val, cfile);
885 if (token != STRING) {
886 parse_warn("Expecting media options.");
887 skip_to_semi(cfile);
888 return;
889 }
890
891 tmp = new_string_list(strlen(val) + 1);
892 if (tmp == NULL)
893 error("no memory for string list entry.");
894 strlcpy(tmp->string, val, strlen(val) + 1);
895 tmp->next = NULL;
896
897 /* Store this medium at the end of the media list. */
898 if (cur)
899 cur->next = tmp;
900 else
901 *lp = tmp;
902 cur = tmp;
903
904 token = next_token(&val, cfile);
905 } while (multiple && token == COMMA);
906
907 if (token != SEMI) {
908 parse_warn("expecting semicolon.");
909 skip_to_semi(cfile);
910 }
911}
912
913void
914parse_reject_statement(FILE *cfile, struct client_config *config)
915{
916 int token;
917 char *val;
918 struct iaddr addr;
919 struct iaddrlist *list;
920
921 do {
922 if (!parse_ip_addr(cfile, &addr)) {
923 parse_warn("expecting IP address.");
924 skip_to_semi(cfile);
925 return;
926 }
927
928 list = malloc(sizeof(struct iaddrlist));
929 if (!list)
930 error("no memory for reject list!");
931
932 list->addr = addr;
933 list->next = config->reject_list;
934 config->reject_list = list;
935
936 token = next_token(&val, cfile);
937 } while (token == COMMA);
938
939 if (token != SEMI) {
940 parse_warn("expecting semicolon.");
941 skip_to_semi(cfile);
942 }
943}
105
106 if ((cfile = fopen(path_dhclient_conf, "r")) != NULL) {
107 do {
108 token = peek_token(&val, cfile);
109 if (token == EOF)
110 break;
111 parse_client_statement(cfile, NULL, &top_level_config);
112 } while (1);
113 token = next_token(&val, cfile); /* Clear the peek buffer */
114 fclose(cfile);
115 }
116
117 /*
118 * Set up state and config structures for clients that don't
119 * have per-interface configuration declarations.
120 */
121 config = NULL;
122 if (!ifi->client) {
123 ifi->client = malloc(sizeof(struct client_state));
124 if (!ifi->client)
125 error("no memory for client state.");
126 memset(ifi->client, 0, sizeof(*(ifi->client)));
127 }
128 if (!ifi->client->config) {
129 if (!config) {
130 config = malloc(sizeof(struct client_config));
131 if (!config)
132 error("no memory for client config.");
133 memcpy(config, &top_level_config,
134 sizeof(top_level_config));
135 }
136 ifi->client->config = config;
137 }
138
139 return (!warnings_occurred);
140}
141
142/*
143 * lease-file :== client-lease-statements EOF
144 * client-lease-statements :== <nil>
145 * | client-lease-statements LEASE client-lease-statement
146 */
147void
148read_client_leases(void)
149{
150 FILE *cfile;
151 char *val;
152 int token;
153
154 new_parse(path_dhclient_db);
155
156 /* Open the lease file. If we can't open it, just return -
157 we can safely trust the server to remember our state. */
158 if ((cfile = fopen(path_dhclient_db, "r")) == NULL)
159 return;
160 do {
161 token = next_token(&val, cfile);
162 if (token == EOF)
163 break;
164 if (token != LEASE) {
165 warning("Corrupt lease file - possible data loss!");
166 skip_to_semi(cfile);
167 break;
168 } else
169 parse_client_lease_statement(cfile, 0);
170
171 } while (1);
172 fclose(cfile);
173}
174
175/*
176 * client-declaration :==
177 * SEND option-decl |
178 * DEFAULT option-decl |
179 * SUPERSEDE option-decl |
180 * PREPEND option-decl |
181 * APPEND option-decl |
182 * hardware-declaration |
183 * REQUEST option-list |
184 * REQUIRE option-list |
185 * TIMEOUT number |
186 * RETRY number |
187 * REBOOT number |
188 * SELECT_TIMEOUT number |
189 * SCRIPT string |
190 * interface-declaration |
191 * LEASE client-lease-statement |
192 * ALIAS client-lease-statement
193 */
194void
195parse_client_statement(FILE *cfile, struct interface_info *ip,
196 struct client_config *config)
197{
198 int token;
199 char *val;
200 struct option *option;
201
202 switch (next_token(&val, cfile)) {
203 case SEND:
204 parse_option_decl(cfile, &config->send_options[0]);
205 return;
206 case DEFAULT:
207 option = parse_option_decl(cfile, &config->defaults[0]);
208 if (option)
209 config->default_actions[option->code] = ACTION_DEFAULT;
210 return;
211 case SUPERSEDE:
212 option = parse_option_decl(cfile, &config->defaults[0]);
213 if (option)
214 config->default_actions[option->code] =
215 ACTION_SUPERSEDE;
216 return;
217 case APPEND:
218 option = parse_option_decl(cfile, &config->defaults[0]);
219 if (option)
220 config->default_actions[option->code] = ACTION_APPEND;
221 return;
222 case PREPEND:
223 option = parse_option_decl(cfile, &config->defaults[0]);
224 if (option)
225 config->default_actions[option->code] = ACTION_PREPEND;
226 return;
227 case MEDIA:
228 parse_string_list(cfile, &config->media, 1);
229 return;
230 case HARDWARE:
231 if (ip)
232 parse_hardware_param(cfile, &ip->hw_address);
233 else {
234 parse_warn("hardware address parameter %s",
235 "not allowed here.");
236 skip_to_semi(cfile);
237 }
238 return;
239 case REQUEST:
240 config->requested_option_count =
241 parse_option_list(cfile, config->requested_options);
242 return;
243 case REQUIRE:
244 memset(config->required_options, 0,
245 sizeof(config->required_options));
246 parse_option_list(cfile, config->required_options);
247 return;
248 case TIMEOUT:
249 parse_lease_time(cfile, &config->timeout);
250 return;
251 case RETRY:
252 parse_lease_time(cfile, &config->retry_interval);
253 return;
254 case SELECT_TIMEOUT:
255 parse_lease_time(cfile, &config->select_interval);
256 return;
257 case REBOOT:
258 parse_lease_time(cfile, &config->reboot_timeout);
259 return;
260 case BACKOFF_CUTOFF:
261 parse_lease_time(cfile, &config->backoff_cutoff);
262 return;
263 case INITIAL_INTERVAL:
264 parse_lease_time(cfile, &config->initial_interval);
265 return;
266 case SCRIPT:
267 config->script_name = parse_string(cfile);
268 return;
269 case INTERFACE:
270 if (ip)
271 parse_warn("nested interface declaration.");
272 parse_interface_declaration(cfile, config);
273 return;
274 case LEASE:
275 parse_client_lease_statement(cfile, 1);
276 return;
277 case ALIAS:
278 parse_client_lease_statement(cfile, 2);
279 return;
280 case REJECT:
281 parse_reject_statement(cfile, config);
282 return;
283 default:
284 parse_warn("expecting a statement.");
285 skip_to_semi(cfile);
286 break;
287 }
288 token = next_token(&val, cfile);
289 if (token != SEMI) {
290 parse_warn("semicolon expected.");
291 skip_to_semi(cfile);
292 }
293}
294
295int
296parse_X(FILE *cfile, u_int8_t *buf, int max)
297{
298 int token;
299 char *val;
300 int len;
301
302 token = peek_token(&val, cfile);
303 if (token == NUMBER_OR_NAME || token == NUMBER) {
304 len = 0;
305 do {
306 token = next_token(&val, cfile);
307 if (token != NUMBER && token != NUMBER_OR_NAME) {
308 parse_warn("expecting hexadecimal constant.");
309 skip_to_semi(cfile);
310 return (0);
311 }
312 convert_num(&buf[len], val, 16, 8);
313 if (len++ > max) {
314 parse_warn("hexadecimal constant too long.");
315 skip_to_semi(cfile);
316 return (0);
317 }
318 token = peek_token(&val, cfile);
319 if (token == COLON)
320 token = next_token(&val, cfile);
321 } while (token == COLON);
322 val = (char *)buf;
323 } else if (token == STRING) {
324 token = next_token(&val, cfile);
325 len = strlen(val);
326 if (len + 1 > max) {
327 parse_warn("string constant too long.");
328 skip_to_semi(cfile);
329 return (0);
330 }
331 memcpy(buf, val, len + 1);
332 } else {
333 parse_warn("expecting string or hexadecimal data");
334 skip_to_semi(cfile);
335 return (0);
336 }
337 return (len);
338}
339
340/*
341 * option-list :== option_name |
342 * option_list COMMA option_name
343 */
344int
345parse_option_list(FILE *cfile, u_int8_t *list)
346{
347 int ix, i;
348 int token;
349 char *val;
350
351 ix = 0;
352 do {
353 token = next_token(&val, cfile);
354 if (!is_identifier(token)) {
355 parse_warn("expected option name.");
356 skip_to_semi(cfile);
357 return (0);
358 }
359 for (i = 0; i < 256; i++)
360 if (!strcasecmp(dhcp_options[i].name, val))
361 break;
362
363 if (i == 256) {
364 parse_warn("%s: unexpected option name.", val);
365 skip_to_semi(cfile);
366 return (0);
367 }
368 list[ix++] = i;
369 if (ix == 256) {
370 parse_warn("%s: too many options.", val);
371 skip_to_semi(cfile);
372 return (0);
373 }
374 token = next_token(&val, cfile);
375 } while (token == COMMA);
376 if (token != SEMI) {
377 parse_warn("expecting semicolon.");
378 skip_to_semi(cfile);
379 return (0);
380 }
381 return (ix);
382}
383
384/*
385 * interface-declaration :==
386 * INTERFACE string LBRACE client-declarations RBRACE
387 */
388void
389parse_interface_declaration(FILE *cfile, struct client_config *outer_config)
390{
391 int token;
392 char *val;
393 struct interface_info *ip;
394
395 token = next_token(&val, cfile);
396 if (token != STRING) {
397 parse_warn("expecting interface name (in quotes).");
398 skip_to_semi(cfile);
399 return;
400 }
401
402 ip = interface_or_dummy(val);
403
404 if (!ip->client)
405 make_client_state(ip);
406
407 if (!ip->client->config)
408 make_client_config(ip, outer_config);
409
410 token = next_token(&val, cfile);
411 if (token != LBRACE) {
412 parse_warn("expecting left brace.");
413 skip_to_semi(cfile);
414 return;
415 }
416
417 do {
418 token = peek_token(&val, cfile);
419 if (token == EOF) {
420 parse_warn("unterminated interface declaration.");
421 return;
422 }
423 if (token == RBRACE)
424 break;
425 parse_client_statement(cfile, ip, ip->client->config);
426 } while (1);
427 token = next_token(&val, cfile);
428}
429
430struct interface_info *
431interface_or_dummy(char *name)
432{
433 struct interface_info *ip;
434
435 /* Find the interface (if any) that matches the name. */
436 if (!strcmp(ifi->name, name))
437 return (ifi);
438
439 /* If it's not a real interface, see if it's on the dummy list. */
440 for (ip = dummy_interfaces; ip; ip = ip->next)
441 if (!strcmp(ip->name, name))
442 return (ip);
443
444 /*
445 * If we didn't find an interface, make a dummy interface as a
446 * placeholder.
447 */
448 ip = malloc(sizeof(*ip));
449 if (!ip)
450 error("Insufficient memory to record interface %s", name);
451 memset(ip, 0, sizeof(*ip));
452 strlcpy(ip->name, name, IFNAMSIZ);
453 ip->next = dummy_interfaces;
454 dummy_interfaces = ip;
455 return (ip);
456}
457
458void
459make_client_state(struct interface_info *ip)
460{
461 ip->client = malloc(sizeof(*(ip->client)));
462 if (!ip->client)
463 error("no memory for state on %s", ip->name);
464 memset(ip->client, 0, sizeof(*(ip->client)));
465}
466
467void
468make_client_config(struct interface_info *ip, struct client_config *config)
469{
470 ip->client->config = malloc(sizeof(struct client_config));
471 if (!ip->client->config)
472 error("no memory for config for %s", ip->name);
473 memset(ip->client->config, 0, sizeof(*(ip->client->config)));
474 memcpy(ip->client->config, config, sizeof(*config));
475}
476
477/*
478 * client-lease-statement :==
479 * RBRACE client-lease-declarations LBRACE
480 *
481 * client-lease-declarations :==
482 * <nil> |
483 * client-lease-declaration |
484 * client-lease-declarations client-lease-declaration
485 */
486void
487parse_client_lease_statement(FILE *cfile, int is_static)
488{
489 struct client_lease *lease, *lp, *pl;
490 struct interface_info *ip;
491 int token;
492 char *val;
493
494 token = next_token(&val, cfile);
495 if (token != LBRACE) {
496 parse_warn("expecting left brace.");
497 skip_to_semi(cfile);
498 return;
499 }
500
501 lease = malloc(sizeof(struct client_lease));
502 if (!lease)
503 error("no memory for lease.");
504 memset(lease, 0, sizeof(*lease));
505 lease->is_static = is_static;
506
507 ip = NULL;
508
509 do {
510 token = peek_token(&val, cfile);
511 if (token == EOF) {
512 parse_warn("unterminated lease declaration.");
513 return;
514 }
515 if (token == RBRACE)
516 break;
517 parse_client_lease_declaration(cfile, lease, &ip);
518 } while (1);
519 token = next_token(&val, cfile);
520
521 /* If the lease declaration didn't include an interface
522 * declaration that we recognized, it's of no use to us.
523 */
524 if (!ip) {
525 free_client_lease(lease);
526 return;
527 }
528
529 /* Make sure there's a client state structure... */
530 if (!ip->client)
531 make_client_state(ip);
532
533 /* If this is an alias lease, it doesn't need to be sorted in. */
534 if (is_static == 2) {
535 ip->client->alias = lease;
536 return;
537 }
538
539 /*
540 * The new lease may supersede a lease that's not the active
541 * lease but is still on the lease list, so scan the lease list
542 * looking for a lease with the same address, and if we find it,
543 * toss it.
544 */
545 pl = NULL;
546 for (lp = ip->client->leases; lp; lp = lp->next) {
547 if (lp->address.len == lease->address.len &&
548 !memcmp(lp->address.iabuf, lease->address.iabuf,
549 lease->address.len)) {
550 if (pl)
551 pl->next = lp->next;
552 else
553 ip->client->leases = lp->next;
554 free_client_lease(lp);
555 break;
556 }
557 }
558
559 /*
560 * If this is a preloaded lease, just put it on the list of
561 * recorded leases - don't make it the active lease.
562 */
563 if (is_static) {
564 lease->next = ip->client->leases;
565 ip->client->leases = lease;
566 return;
567 }
568
569 /*
570 * The last lease in the lease file on a particular interface is
571 * the active lease for that interface. Of course, we don't
572 * know what the last lease in the file is until we've parsed
573 * the whole file, so at this point, we assume that the lease we
574 * just parsed is the active lease for its interface. If
575 * there's already an active lease for the interface, and this
576 * lease is for the same ip address, then we just toss the old
577 * active lease and replace it with this one. If this lease is
578 * for a different address, then if the old active lease has
579 * expired, we dump it; if not, we put it on the list of leases
580 * for this interface which are still valid but no longer
581 * active.
582 */
583 if (ip->client->active) {
584 if (ip->client->active->expiry < cur_time)
585 free_client_lease(ip->client->active);
586 else if (ip->client->active->address.len ==
587 lease->address.len &&
588 !memcmp(ip->client->active->address.iabuf,
589 lease->address.iabuf, lease->address.len))
590 free_client_lease(ip->client->active);
591 else {
592 ip->client->active->next = ip->client->leases;
593 ip->client->leases = ip->client->active;
594 }
595 }
596 ip->client->active = lease;
597
598 /* Phew. */
599}
600
601/*
602 * client-lease-declaration :==
603 * BOOTP |
604 * INTERFACE string |
605 * FIXED_ADDR ip_address |
606 * FILENAME string |
607 * SERVER_NAME string |
608 * OPTION option-decl |
609 * RENEW time-decl |
610 * REBIND time-decl |
611 * EXPIRE time-decl
612 */
613void
614parse_client_lease_declaration(FILE *cfile, struct client_lease *lease,
615 struct interface_info **ipp)
616{
617 int token;
618 char *val;
619 struct interface_info *ip;
620
621 switch (next_token(&val, cfile)) {
622 case BOOTP:
623 lease->is_bootp = 1;
624 break;
625 case INTERFACE:
626 token = next_token(&val, cfile);
627 if (token != STRING) {
628 parse_warn("expecting interface name (in quotes).");
629 skip_to_semi(cfile);
630 break;
631 }
632 ip = interface_or_dummy(val);
633 *ipp = ip;
634 break;
635 case FIXED_ADDR:
636 if (!parse_ip_addr(cfile, &lease->address))
637 return;
638 break;
639 case MEDIUM:
640 parse_string_list(cfile, &lease->medium, 0);
641 return;
642 case FILENAME:
643 lease->filename = parse_string(cfile);
644 return;
645 case SERVER_NAME:
646 lease->server_name = parse_string(cfile);
647 return;
648 case RENEW:
649 lease->renewal = parse_date(cfile);
650 return;
651 case REBIND:
652 lease->rebind = parse_date(cfile);
653 return;
654 case EXPIRE:
655 lease->expiry = parse_date(cfile);
656 return;
657 case OPTION:
658 parse_option_decl(cfile, lease->options);
659 return;
660 default:
661 parse_warn("expecting lease declaration.");
662 skip_to_semi(cfile);
663 break;
664 }
665 token = next_token(&val, cfile);
666 if (token != SEMI) {
667 parse_warn("expecting semicolon.");
668 skip_to_semi(cfile);
669 }
670}
671
672struct option *
673parse_option_decl(FILE *cfile, struct option_data *options)
674{
675 char *val;
676 int token;
677 u_int8_t buf[4];
678 u_int8_t hunkbuf[1024];
679 int hunkix = 0;
680 char *vendor;
681 char *fmt;
682 struct universe *universe;
683 struct option *option;
684 struct iaddr ip_addr;
685 u_int8_t *dp;
686 int len;
687 int nul_term = 0;
688
689 token = next_token(&val, cfile);
690 if (!is_identifier(token)) {
691 parse_warn("expecting identifier after option keyword.");
692 if (token != SEMI)
693 skip_to_semi(cfile);
694 return (NULL);
695 }
696 if ((vendor = strdup(val)) == NULL)
697 error("no memory for vendor information.");
698
699 token = peek_token(&val, cfile);
700 if (token == DOT) {
701 /* Go ahead and take the DOT token... */
702 token = next_token(&val, cfile);
703
704 /* The next token should be an identifier... */
705 token = next_token(&val, cfile);
706 if (!is_identifier(token)) {
707 parse_warn("expecting identifier after '.'");
708 if (token != SEMI)
709 skip_to_semi(cfile);
710 return (NULL);
711 }
712
713 /* Look up the option name hash table for the specified
714 vendor. */
715 universe = ((struct universe *)hash_lookup(&universe_hash,
716 (unsigned char *)vendor, 0));
717 /* If it's not there, we can't parse the rest of the
718 declaration. */
719 if (!universe) {
720 parse_warn("no vendor named %s.", vendor);
721 skip_to_semi(cfile);
722 return (NULL);
723 }
724 } else {
725 /* Use the default hash table, which contains all the
726 standard dhcp option names. */
727 val = vendor;
728 universe = &dhcp_universe;
729 }
730
731 /* Look up the actual option info... */
732 option = (struct option *)hash_lookup(universe->hash,
733 (unsigned char *)val, 0);
734
735 /* If we didn't get an option structure, it's an undefined option. */
736 if (!option) {
737 if (val == vendor)
738 parse_warn("no option named %s", val);
739 else
740 parse_warn("no option named %s for vendor %s",
741 val, vendor);
742 skip_to_semi(cfile);
743 return (NULL);
744 }
745
746 /* Free the initial identifier token. */
747 free(vendor);
748
749 /* Parse the option data... */
750 do {
751 for (fmt = option->format; *fmt; fmt++) {
752 if (*fmt == 'A')
753 break;
754 switch (*fmt) {
755 case 'X':
756 len = parse_X(cfile, &hunkbuf[hunkix],
757 sizeof(hunkbuf) - hunkix);
758 hunkix += len;
759 break;
760 case 't': /* Text string... */
761 token = next_token(&val, cfile);
762 if (token != STRING) {
763 parse_warn("expecting string.");
764 skip_to_semi(cfile);
765 return (NULL);
766 }
767 len = strlen(val);
768 if (hunkix + len + 1 > sizeof(hunkbuf)) {
769 parse_warn("option data buffer %s",
770 "overflow");
771 skip_to_semi(cfile);
772 return (NULL);
773 }
774 memcpy(&hunkbuf[hunkix], val, len + 1);
775 nul_term = 1;
776 hunkix += len;
777 break;
778 case 'I': /* IP address. */
779 if (!parse_ip_addr(cfile, &ip_addr))
780 return (NULL);
781 len = ip_addr.len;
782 dp = ip_addr.iabuf;
783alloc:
784 if (hunkix + len > sizeof(hunkbuf)) {
785 parse_warn("option data buffer "
786 "overflow");
787 skip_to_semi(cfile);
788 return (NULL);
789 }
790 memcpy(&hunkbuf[hunkix], dp, len);
791 hunkix += len;
792 break;
793 case 'L': /* Unsigned 32-bit integer... */
794 case 'l': /* Signed 32-bit integer... */
795 token = next_token(&val, cfile);
796 if (token != NUMBER) {
797need_number:
798 parse_warn("expecting number.");
799 if (token != SEMI)
800 skip_to_semi(cfile);
801 return (NULL);
802 }
803 convert_num(buf, val, 0, 32);
804 len = 4;
805 dp = buf;
806 goto alloc;
807 case 's': /* Signed 16-bit integer. */
808 case 'S': /* Unsigned 16-bit integer. */
809 token = next_token(&val, cfile);
810 if (token != NUMBER)
811 goto need_number;
812 convert_num(buf, val, 0, 16);
813 len = 2;
814 dp = buf;
815 goto alloc;
816 case 'b': /* Signed 8-bit integer. */
817 case 'B': /* Unsigned 8-bit integer. */
818 token = next_token(&val, cfile);
819 if (token != NUMBER)
820 goto need_number;
821 convert_num(buf, val, 0, 8);
822 len = 1;
823 dp = buf;
824 goto alloc;
825 case 'f': /* Boolean flag. */
826 token = next_token(&val, cfile);
827 if (!is_identifier(token)) {
828 parse_warn("expecting identifier.");
829bad_flag:
830 if (token != SEMI)
831 skip_to_semi(cfile);
832 return (NULL);
833 }
834 if (!strcasecmp(val, "true") ||
835 !strcasecmp(val, "on"))
836 buf[0] = 1;
837 else if (!strcasecmp(val, "false") ||
838 !strcasecmp(val, "off"))
839 buf[0] = 0;
840 else {
841 parse_warn("expecting boolean.");
842 goto bad_flag;
843 }
844 len = 1;
845 dp = buf;
846 goto alloc;
847 default:
848 warning("Bad format %c in parse_option_param.",
849 *fmt);
850 skip_to_semi(cfile);
851 return (NULL);
852 }
853 }
854 token = next_token(&val, cfile);
855 } while (*fmt == 'A' && token == COMMA);
856
857 if (token != SEMI) {
858 parse_warn("semicolon expected.");
859 skip_to_semi(cfile);
860 return (NULL);
861 }
862
863 options[option->code].data = malloc(hunkix + nul_term);
864 if (!options[option->code].data)
865 error("out of memory allocating option data.");
866 memcpy(options[option->code].data, hunkbuf, hunkix + nul_term);
867 options[option->code].len = hunkix;
868 return (option);
869}
870
871void
872parse_string_list(FILE *cfile, struct string_list **lp, int multiple)
873{
874 int token;
875 char *val;
876 struct string_list *cur, *tmp;
877
878 /* Find the last medium in the media list. */
879 if (*lp)
880 for (cur = *lp; cur->next; cur = cur->next)
881 ; /* nothing */
882 else
883 cur = NULL;
884
885 do {
886 token = next_token(&val, cfile);
887 if (token != STRING) {
888 parse_warn("Expecting media options.");
889 skip_to_semi(cfile);
890 return;
891 }
892
893 tmp = new_string_list(strlen(val) + 1);
894 if (tmp == NULL)
895 error("no memory for string list entry.");
896 strlcpy(tmp->string, val, strlen(val) + 1);
897 tmp->next = NULL;
898
899 /* Store this medium at the end of the media list. */
900 if (cur)
901 cur->next = tmp;
902 else
903 *lp = tmp;
904 cur = tmp;
905
906 token = next_token(&val, cfile);
907 } while (multiple && token == COMMA);
908
909 if (token != SEMI) {
910 parse_warn("expecting semicolon.");
911 skip_to_semi(cfile);
912 }
913}
914
915void
916parse_reject_statement(FILE *cfile, struct client_config *config)
917{
918 int token;
919 char *val;
920 struct iaddr addr;
921 struct iaddrlist *list;
922
923 do {
924 if (!parse_ip_addr(cfile, &addr)) {
925 parse_warn("expecting IP address.");
926 skip_to_semi(cfile);
927 return;
928 }
929
930 list = malloc(sizeof(struct iaddrlist));
931 if (!list)
932 error("no memory for reject list!");
933
934 list->addr = addr;
935 list->next = config->reject_list;
936 config->reject_list = list;
937
938 token = next_token(&val, cfile);
939 } while (token == COMMA);
940
941 if (token != SEMI) {
942 parse_warn("expecting semicolon.");
943 skip_to_semi(cfile);
944 }
945}