1 /* $OpenBSD: src/sbin/dhclient/clparse.c,v 1.36 2009/07/19 00:18:02 stevesk Exp $ */
3 /* Parser for dhclient config and lease files... */
6 * Copyright (c) 1997 The Internet Software Consortium.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
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.
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
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''.
47 * client-conf-file :== client-declarations EOF
48 * client-declarations :== <nil>
49 * | client-declaration
50 * | client-declarations client-declaration
53 read_client_conf(void)
59 new_parse(path_dhclient_conf);
61 /* Set some defaults... */
62 config->link_timeout = 10;
64 config->select_interval = 0;
65 config->reboot_timeout = 10;
66 config->retry_interval = 300;
67 config->backoff_cutoff = 15;
68 config->initial_interval = 3;
69 config->bootp_policy = ACCEPT;
70 config->script_name = _PATH_DHCLIENT_SCRIPT;
71 config->requested_options
72 [config->requested_option_count++] = DHO_SUBNET_MASK;
73 config->requested_options
74 [config->requested_option_count++] = DHO_BROADCAST_ADDRESS;
75 config->requested_options
76 [config->requested_option_count++] = DHO_TIME_OFFSET;
77 config->requested_options
78 [config->requested_option_count++] = DHO_ROUTERS;
79 config->requested_options
80 [config->requested_option_count++] = DHO_DOMAIN_NAME;
81 config->requested_options
82 [config->requested_option_count++] = DHO_DOMAIN_NAME_SERVERS;
83 config->requested_options
84 [config->requested_option_count++] = DHO_HOST_NAME;
86 if ((cfile = fopen(path_dhclient_conf, "r")) != NULL) {
88 token = peek_token(&val, cfile);
91 parse_client_statement(cfile);
93 token = next_token(&val, cfile); /* Clear the peek buffer */
97 return (!warnings_occurred);
101 * lease-file :== client-lease-statements EOF
102 * client-lease-statements :== <nil>
103 * | client-lease-statements LEASE client-lease-statement
106 read_client_leases(void)
112 new_parse(path_dhclient_db);
114 /* Open the lease file. If we can't open it, just return -
115 we can safely trust the server to remember our state. */
116 if ((cfile = fopen(path_dhclient_db, "r")) == NULL)
119 token = next_token(&val, cfile);
122 if (token != TOK_LEASE) {
123 warning("Corrupt lease file - possible data loss!");
127 parse_client_lease_statement(cfile, 0);
134 * client-declaration :==
135 * TOK_SEND option-decl |
136 * TOK_DEFAULT option-decl |
137 * TOK_SUPERSEDE option-decl |
138 * TOK_APPEND option-decl |
139 * TOK_PREPEND option-decl |
140 * TOK_MEDIA string-list |
141 * hardware-declaration |
142 * TOK_REQUEST option-list |
143 * TOK_REQUIRE option-list |
144 * TOK_TIMEOUT number |
146 * TOK_SELECT_TIMEOUT number |
147 * TOK_REBOOT number |
148 * TOK_BACKOFF_CUTOFF number |
149 * TOK_INITIAL_INTERVAL number |
150 * TOK_SCRIPT string |
151 * interface-declaration |
152 * TOK_LEASE client-lease-statement |
153 * TOK_ALIAS client-lease-statement |
154 * TOK_REJECT reject-statement
157 parse_client_statement(FILE *cfile)
162 switch (next_token(&val, cfile)) {
164 parse_option_decl(cfile, &config->send_options[0]);
167 code = parse_option_decl(cfile, &config->defaults[0]);
169 config->default_actions[code] = ACTION_DEFAULT;
172 code = parse_option_decl(cfile, &config->defaults[0]);
174 config->default_actions[code] = ACTION_SUPERSEDE;
177 code = parse_option_decl(cfile, &config->defaults[0]);
179 config->default_actions[code] = ACTION_APPEND;
182 code = parse_option_decl(cfile, &config->defaults[0]);
184 config->default_actions[code] = ACTION_PREPEND;
187 parse_string_list(cfile, &config->media, 1);
190 parse_hardware_param(cfile, &ifi->hw_address);
193 config->requested_option_count =
194 parse_option_list(cfile, config->requested_options);
197 memset(config->required_options, 0,
198 sizeof(config->required_options));
199 parse_option_list(cfile, config->required_options);
201 case TOK_LINK_TIMEOUT:
202 parse_lease_time(cfile, &config->link_timeout);
205 parse_lease_time(cfile, &config->timeout);
208 parse_lease_time(cfile, &config->retry_interval);
210 case TOK_SELECT_TIMEOUT:
211 parse_lease_time(cfile, &config->select_interval);
214 parse_lease_time(cfile, &config->reboot_timeout);
216 case TOK_BACKOFF_CUTOFF:
217 parse_lease_time(cfile, &config->backoff_cutoff);
219 case TOK_INITIAL_INTERVAL:
220 parse_lease_time(cfile, &config->initial_interval);
223 config->script_name = parse_string(cfile);
226 parse_interface_declaration(cfile);
229 parse_client_lease_statement(cfile, 1);
232 parse_client_lease_statement(cfile, 2);
235 parse_reject_statement(cfile);
238 parse_warn("expecting a statement.");
242 token = next_token(&val, cfile);
244 parse_warn("semicolon expected.");
250 parse_X(FILE *cfile, u_int8_t *buf, int max)
256 token = peek_token(&val, cfile);
257 if (token == TOK_NUMBER_OR_NAME || token == TOK_NUMBER) {
260 token = next_token(&val, cfile);
261 if (token != TOK_NUMBER && token != TOK_NUMBER_OR_NAME) {
262 parse_warn("expecting hexadecimal constant.");
266 convert_num(&buf[len], val, 16, 8);
268 parse_warn("hexadecimal constant too long.");
272 token = peek_token(&val, cfile);
274 token = next_token(&val, cfile);
275 } while (token == ':');
277 } else if (token == TOK_STRING) {
278 token = next_token(&val, cfile);
281 parse_warn("string constant too long.");
285 memcpy(buf, val, len + 1);
287 parse_warn("expecting string or hexadecimal data");
295 * option-list :== option_name |
296 * option_list COMMA option_name
299 parse_option_list(FILE *cfile, u_int8_t *list)
307 token = next_token(&val, cfile);
308 if (!is_identifier(token)) {
309 parse_warn("expected option name.");
313 for (i = 0; i < 256; i++)
314 if (!strcasecmp(dhcp_options[i].name, val))
318 parse_warn("%s: unexpected option name.", val);
324 parse_warn("%s: too many options.", val);
328 token = next_token(&val, cfile);
329 } while (token == ',');
331 parse_warn("expecting semicolon.");
339 * interface-declaration :==
340 * INTERFACE string LBRACE client-declarations RBRACE
343 parse_interface_declaration(FILE *cfile)
348 token = next_token(&val, cfile);
349 if (token != TOK_STRING) {
350 parse_warn("expecting interface name (in quotes).");
355 if (strcmp(ifi->name, val) != 0) {
360 token = next_token(&val, cfile);
362 parse_warn("expecting left brace.");
368 token = peek_token(&val, cfile);
370 parse_warn("unterminated interface declaration.");
375 parse_client_statement(cfile);
377 token = next_token(&val, cfile);
381 * client-lease-statement :==
382 * RBRACE client-lease-declarations LBRACE
384 * client-lease-declarations :==
386 * client-lease-declaration |
387 * client-lease-declarations client-lease-declaration
390 parse_client_lease_statement(FILE *cfile, int is_static)
392 struct client_lease *lease, *lp, *pl;
396 token = next_token(&val, cfile);
398 parse_warn("expecting left brace.");
403 lease = malloc(sizeof(struct client_lease));
405 error("no memory for lease.");
406 memset(lease, 0, sizeof(*lease));
407 lease->is_static = is_static;
410 token = peek_token(&val, cfile);
412 parse_warn("unterminated lease declaration.");
417 parse_client_lease_declaration(cfile, lease);
419 token = next_token(&val, cfile);
421 /* If the lease declaration didn't include an interface
422 * declaration that we recognized, it's of no use to us.
425 free_client_lease(lease);
429 /* If this is an alias lease, it doesn't need to be sorted in. */
430 if (is_static == 2) {
431 client->alias = lease;
436 * The new lease may supersede a lease that's not the active
437 * lease but is still on the lease list, so scan the lease list
438 * looking for a lease with the same address, and if we find it,
442 for (lp = client->leases; lp; lp = lp->next) {
443 if (addr_eq(lp->address, lease->address)) {
447 client->leases = lp->next;
448 free_client_lease(lp);
455 * If this is a preloaded lease, just put it on the list of
456 * recorded leases - don't make it the active lease.
459 lease->next = client->leases;
460 client->leases = lease;
465 * The last lease in the lease file on a particular interface is
466 * the active lease for that interface. Of course, we don't
467 * know what the last lease in the file is until we've parsed
468 * the whole file, so at this point, we assume that the lease we
469 * just parsed is the active lease for its interface. If
470 * there's already an active lease for the interface, and this
471 * lease is for the same ip address, then we just toss the old
472 * active lease and replace it with this one. If this lease is
473 * for a different address, then if the old active lease has
474 * expired, we dump it; if not, we put it on the list of leases
475 * for this interface which are still valid but no longer
478 if (client->active) {
479 if (client->active->expiry < cur_time)
480 free_client_lease(client->active);
481 else if (addr_eq(client->active->address, lease->address))
482 free_client_lease(client->active);
484 client->active->next = client->leases;
485 client->leases = client->active;
488 client->active = lease;
494 * client-lease-declaration :==
497 * FIXED_ADDR ip_address |
499 * SERVER_NAME string |
500 * OPTION option-decl |
506 parse_client_lease_declaration(FILE *cfile, struct client_lease *lease)
511 switch (next_token(&val, cfile)) {
516 token = next_token(&val, cfile);
517 if (token != TOK_STRING) {
518 parse_warn("expecting interface name (in quotes).");
522 if (strcmp(ifi->name, val) != 0) {
523 parse_warn("wrong interface name. Expecting '%s'.",
530 if (!parse_ip_addr(cfile, &lease->address))
534 parse_string_list(cfile, &lease->medium, 0);
537 lease->filename = parse_string(cfile);
539 case TOK_SERVER_NAME:
540 lease->server_name = parse_string(cfile);
543 lease->renewal = parse_date(cfile);
546 lease->rebind = parse_date(cfile);
549 lease->expiry = parse_date(cfile);
552 parse_option_decl(cfile, lease->options);
555 parse_warn("expecting lease declaration.");
559 token = next_token(&val, cfile);
561 parse_warn("expecting semicolon.");
567 parse_option_decl(FILE *cfile, struct option_data *options)
572 u_int8_t hunkbuf[1024];
575 struct iaddr ip_addr;
580 token = next_token(&val, cfile);
581 if (!is_identifier(token)) {
582 parse_warn("expecting identifier after option keyword.");
588 /* Look up the actual option info. */
590 for (code = 0; code < 256; code++)
591 if (strcmp(dhcp_options[code].name, val) == 0)
595 parse_warn("no option named %s", val);
600 /* Parse the option data... */
602 for (fmt = dhcp_options[code].format; *fmt; fmt++) {
607 len = parse_X(cfile, &hunkbuf[hunkix],
608 sizeof(hunkbuf) - hunkix);
611 case 't': /* Text string... */
612 token = next_token(&val, cfile);
613 if (token != TOK_STRING) {
614 parse_warn("expecting string.");
619 if (hunkix + len + 1 > sizeof(hunkbuf)) {
620 parse_warn("option data buffer %s",
625 memcpy(&hunkbuf[hunkix], val, len + 1);
629 case 'I': /* IP address. */
630 if (!parse_ip_addr(cfile, &ip_addr))
635 if (hunkix + len > sizeof(hunkbuf)) {
636 parse_warn("option data buffer "
641 memcpy(&hunkbuf[hunkix], dp, len);
644 case 'L': /* Unsigned 32-bit integer... */
645 case 'l': /* Signed 32-bit integer... */
646 token = next_token(&val, cfile);
647 if (token != TOK_NUMBER) {
649 parse_warn("expecting number.");
654 convert_num(buf, val, 0, 32);
658 case 's': /* Signed 16-bit integer. */
659 case 'S': /* Unsigned 16-bit integer. */
660 token = next_token(&val, cfile);
661 if (token != TOK_NUMBER)
663 convert_num(buf, val, 0, 16);
667 case 'b': /* Signed 8-bit integer. */
668 case 'B': /* Unsigned 8-bit integer. */
669 token = next_token(&val, cfile);
670 if (token != TOK_NUMBER)
672 convert_num(buf, val, 0, 8);
676 case 'f': /* Boolean flag. */
677 token = next_token(&val, cfile);
678 if (!is_identifier(token)) {
679 parse_warn("expecting identifier.");
685 if (!strcasecmp(val, "true") ||
686 !strcasecmp(val, "on"))
688 else if (!strcasecmp(val, "false") ||
689 !strcasecmp(val, "off"))
692 parse_warn("expecting boolean.");
699 warning("Bad format %c in parse_option_param.",
705 token = next_token(&val, cfile);
706 } while (*fmt == 'A' && token == ',');
709 parse_warn("semicolon expected.");
714 options[code].data = malloc(hunkix + nul_term);
715 if (!options[code].data)
716 error("out of memory allocating option data.");
717 memcpy(options[code].data, hunkbuf, hunkix + nul_term);
718 options[code].len = hunkix;
723 parse_string_list(FILE *cfile, struct string_list **lp, int multiple)
727 struct string_list *cur, *tmp;
729 /* Find the last medium in the media list. */
731 for (cur = *lp; cur->next; cur = cur->next)
737 token = next_token(&val, cfile);
738 if (token != TOK_STRING) {
739 parse_warn("Expecting media options.");
744 tmp = malloc(sizeof(struct string_list) + strlen(val));
746 error("no memory for string list entry.");
747 strlcpy(tmp->string, val, strlen(val) + 1);
750 /* Store this medium at the end of the media list. */
757 token = next_token(&val, cfile);
758 } while (multiple && token == ',');
761 parse_warn("expecting semicolon.");
767 parse_reject_statement(FILE *cfile)
769 struct iaddrlist *list;
775 if (!parse_ip_addr(cfile, &addr)) {
776 parse_warn("expecting IP address.");
781 list = malloc(sizeof(struct iaddrlist));
783 error("no memory for reject list!");
786 list->next = config->reject_list;
787 config->reject_list = list;
789 token = next_token(&val, cfile);
790 } while (token == ',');
793 parse_warn("expecting semicolon.");