1 /* $OpenBSD: clparse.c,v 1.18 2004/09/15 18:15:18 henning Exp $ */
3 /* Parser for dhclient config and lease files... */
6 * SPDX-License-Identifier: BSD-3-Clause
8 * Copyright (c) 1997 The Internet Software Consortium.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. Neither the name of The Internet Software Consortium nor the names
21 * of its contributors may be used to endorse or promote products derived
22 * from this software without specific prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
25 * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
26 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
27 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
28 * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
29 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
32 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
33 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
34 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
35 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38 * This software has been written for the Internet Software Consortium
39 * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
40 * Enterprises. To learn more about the Internet Software Consortium,
41 * see ``http://www.vix.com/isc''. To learn more about Vixie
42 * Enterprises, see ``http://www.vix.com''.
45 #include <sys/cdefs.h>
46 __FBSDID("$FreeBSD$");
51 struct client_config top_level_config;
52 static struct interface_info *dummy_interfaces;
54 static char client_script_name[] = "/sbin/dhclient-script";
57 * client-conf-file :== client-declarations EOF
58 * client-declarations :== <nil>
59 * | client-declaration
60 * | client-declarations client-declaration
63 read_client_conf(void)
68 struct client_config *config;
70 new_parse(path_dhclient_conf);
72 /* Set up the initial dhcp option universe. */
73 initialize_universes();
75 /* Initialize the top level client configuration. */
76 memset(&top_level_config, 0, sizeof(top_level_config));
78 /* Set some defaults... */
79 top_level_config.timeout = 60;
80 top_level_config.select_interval = 0;
81 top_level_config.reboot_timeout = 10;
82 top_level_config.retry_interval = 300;
83 top_level_config.backoff_cutoff = 15;
84 top_level_config.initial_interval = 3;
85 top_level_config.bootp_policy = ACCEPT;
86 top_level_config.script_name = client_script_name;
87 top_level_config.requested_options
88 [top_level_config.requested_option_count++] = DHO_SUBNET_MASK;
89 top_level_config.requested_options
90 [top_level_config.requested_option_count++] = DHO_BROADCAST_ADDRESS;
91 top_level_config.requested_options
92 [top_level_config.requested_option_count++] = DHO_TIME_OFFSET;
93 top_level_config.requested_options
94 [top_level_config.requested_option_count++] = DHO_CLASSLESS_ROUTES;
95 top_level_config.requested_options
96 [top_level_config.requested_option_count++] = DHO_ROUTERS;
97 top_level_config.requested_options
98 [top_level_config.requested_option_count++] = DHO_DOMAIN_NAME;
99 top_level_config.requested_options
100 [top_level_config.requested_option_count++] =
101 DHO_DOMAIN_NAME_SERVERS;
102 top_level_config.requested_options
103 [top_level_config.requested_option_count++] = DHO_HOST_NAME;
104 top_level_config.requested_options
105 [top_level_config.requested_option_count++] = DHO_DOMAIN_SEARCH;
106 top_level_config.requested_options
107 [top_level_config.requested_option_count++] = DHO_INTERFACE_MTU;
109 if ((cfile = fopen(path_dhclient_conf, "r")) != NULL) {
111 token = peek_token(&val, cfile);
114 parse_client_statement(cfile, NULL, &top_level_config);
116 token = next_token(&val, cfile); /* Clear the peek buffer */
121 * Set up state and config structures for clients that don't
122 * have per-interface configuration declarations.
126 ifi->client = malloc(sizeof(struct client_state));
128 error("no memory for client state.");
129 memset(ifi->client, 0, sizeof(*(ifi->client)));
131 if (!ifi->client->config) {
133 config = malloc(sizeof(struct client_config));
135 error("no memory for client config.");
136 memcpy(config, &top_level_config,
137 sizeof(top_level_config));
139 ifi->client->config = config;
142 return (!warnings_occurred);
146 * lease-file :== client-lease-statements EOF
147 * client-lease-statements :== <nil>
148 * | client-lease-statements LEASE client-lease-statement
151 read_client_leases(void)
157 new_parse(path_dhclient_db);
159 /* Open the lease file. If we can't open it, just return -
160 we can safely trust the server to remember our state. */
161 if ((cfile = fopen(path_dhclient_db, "r")) == NULL)
164 token = next_token(&val, cfile);
167 if (token != LEASE) {
168 warning("Corrupt lease file - possible data loss!");
172 parse_client_lease_statement(cfile, 0);
179 * client-declaration :==
181 * DEFAULT option-decl |
182 * SUPERSEDE option-decl |
183 * PREPEND option-decl |
184 * APPEND option-decl |
185 * hardware-declaration |
186 * REQUEST option-list |
187 * REQUIRE option-list |
191 * SELECT_TIMEOUT number |
193 * interface-declaration |
194 * LEASE client-lease-statement |
195 * ALIAS client-lease-statement
198 parse_client_statement(FILE *cfile, struct interface_info *ip,
199 struct client_config *config)
203 struct option *option;
205 switch (next_token(&val, cfile)) {
207 parse_option_decl(cfile, &config->send_options[0]);
210 option = parse_option_decl(cfile, &config->defaults[0]);
212 config->default_actions[option->code] = ACTION_DEFAULT;
215 option = parse_option_decl(cfile, &config->defaults[0]);
217 config->default_actions[option->code] =
221 option = parse_option_decl(cfile, &config->defaults[0]);
223 config->default_actions[option->code] = ACTION_APPEND;
226 option = parse_option_decl(cfile, &config->defaults[0]);
228 config->default_actions[option->code] = ACTION_PREPEND;
231 parse_string_list(cfile, &config->media, 1);
235 parse_hardware_param(cfile, &ip->hw_address);
237 parse_warn("hardware address parameter %s",
238 "not allowed here.");
243 config->requested_option_count =
244 parse_option_list(cfile, config->requested_options);
247 memset(config->required_options, 0,
248 sizeof(config->required_options));
249 parse_option_list(cfile, config->required_options);
252 parse_lease_time(cfile, &config->timeout);
255 parse_lease_time(cfile, &config->retry_interval);
258 parse_lease_time(cfile, &config->select_interval);
261 parse_lease_time(cfile, &config->reboot_timeout);
264 parse_lease_time(cfile, &config->backoff_cutoff);
266 case INITIAL_INTERVAL:
267 parse_lease_time(cfile, &config->initial_interval);
270 config->script_name = parse_string(cfile);
274 parse_warn("nested interface declaration.");
275 parse_interface_declaration(cfile, config);
278 parse_client_lease_statement(cfile, 1);
281 parse_client_lease_statement(cfile, 2);
284 parse_reject_statement(cfile, config);
287 parse_warn("expecting a statement.");
291 token = next_token(&val, cfile);
293 parse_warn("semicolon expected.");
299 parse_X(FILE *cfile, u_int8_t *buf, unsigned max)
305 token = peek_token(&val, cfile);
306 if (token == NUMBER_OR_NAME || token == NUMBER) {
309 token = next_token(&val, cfile);
310 if (token != NUMBER && token != NUMBER_OR_NAME) {
311 parse_warn("expecting hexadecimal constant.");
315 convert_num(&buf[len], val, 16, 8);
317 parse_warn("hexadecimal constant too long.");
321 token = peek_token(&val, cfile);
323 token = next_token(&val, cfile);
324 } while (token == COLON);
326 } else if (token == STRING) {
327 token = next_token(&val, cfile);
330 parse_warn("string constant too long.");
334 memcpy(buf, val, len + 1);
336 parse_warn("expecting string or hexadecimal data");
344 * option-list :== option_name |
345 * option_list COMMA option_name
348 parse_option_list(FILE *cfile, u_int8_t *list)
356 token = next_token(&val, cfile);
357 if (!is_identifier(token)) {
358 parse_warn("expected option name.");
362 for (i = 0; i < 256; i++)
363 if (!strcasecmp(dhcp_options[i].name, val))
367 parse_warn("%s: unexpected option name.", val);
373 parse_warn("%s: too many options.", val);
377 token = next_token(&val, cfile);
378 } while (token == COMMA);
380 parse_warn("expecting semicolon.");
388 * interface-declaration :==
389 * INTERFACE string LBRACE client-declarations RBRACE
392 parse_interface_declaration(FILE *cfile, struct client_config *outer_config)
396 struct interface_info *ip;
398 token = next_token(&val, cfile);
399 if (token != STRING) {
400 parse_warn("expecting interface name (in quotes).");
405 ip = interface_or_dummy(val);
408 make_client_state(ip);
410 if (!ip->client->config)
411 make_client_config(ip, outer_config);
413 token = next_token(&val, cfile);
414 if (token != LBRACE) {
415 parse_warn("expecting left brace.");
421 token = peek_token(&val, cfile);
423 parse_warn("unterminated interface declaration.");
428 parse_client_statement(cfile, ip, ip->client->config);
430 token = next_token(&val, cfile);
433 struct interface_info *
434 interface_or_dummy(char *name)
436 struct interface_info *ip;
438 /* Find the interface (if any) that matches the name. */
439 if (!strcmp(ifi->name, name))
442 /* If it's not a real interface, see if it's on the dummy list. */
443 for (ip = dummy_interfaces; ip; ip = ip->next)
444 if (!strcmp(ip->name, name))
448 * If we didn't find an interface, make a dummy interface as a
451 ip = malloc(sizeof(*ip));
453 error("Insufficient memory to record interface %s", name);
454 memset(ip, 0, sizeof(*ip));
455 strlcpy(ip->name, name, IFNAMSIZ);
456 ip->next = dummy_interfaces;
457 dummy_interfaces = ip;
462 make_client_state(struct interface_info *ip)
464 ip->client = malloc(sizeof(*(ip->client)));
466 error("no memory for state on %s", ip->name);
467 memset(ip->client, 0, sizeof(*(ip->client)));
471 make_client_config(struct interface_info *ip, struct client_config *config)
473 ip->client->config = malloc(sizeof(struct client_config));
474 if (!ip->client->config)
475 error("no memory for config for %s", ip->name);
476 memset(ip->client->config, 0, sizeof(*(ip->client->config)));
477 memcpy(ip->client->config, config, sizeof(*config));
481 * client-lease-statement :==
482 * RBRACE client-lease-declarations LBRACE
484 * client-lease-declarations :==
486 * client-lease-declaration |
487 * client-lease-declarations client-lease-declaration
490 parse_client_lease_statement(FILE *cfile, int is_static)
492 struct client_lease *lease, *lp, *pl;
493 struct interface_info *ip;
497 token = next_token(&val, cfile);
498 if (token != LBRACE) {
499 parse_warn("expecting left brace.");
504 lease = malloc(sizeof(struct client_lease));
506 error("no memory for lease.");
507 memset(lease, 0, sizeof(*lease));
508 lease->is_static = is_static;
513 token = peek_token(&val, cfile);
515 parse_warn("unterminated lease declaration.");
516 free_client_lease(lease);
521 parse_client_lease_declaration(cfile, lease, &ip);
523 token = next_token(&val, cfile);
525 /* If the lease declaration didn't include an interface
526 * declaration that we recognized, it's of no use to us.
529 free_client_lease(lease);
533 /* Make sure there's a client state structure... */
535 make_client_state(ip);
537 /* If this is an alias lease, it doesn't need to be sorted in. */
538 if (is_static == 2) {
539 ip->client->alias = lease;
544 * The new lease may supersede a lease that's not the active
545 * lease but is still on the lease list, so scan the lease list
546 * looking for a lease with the same address, and if we find it,
550 for (lp = ip->client->leases; lp; lp = lp->next) {
551 if (lp->address.len == lease->address.len &&
552 !memcmp(lp->address.iabuf, lease->address.iabuf,
553 lease->address.len)) {
557 ip->client->leases = lp->next;
558 free_client_lease(lp);
564 * If this is a preloaded lease, just put it on the list of
565 * recorded leases - don't make it the active lease.
568 lease->next = ip->client->leases;
569 ip->client->leases = lease;
574 * The last lease in the lease file on a particular interface is
575 * the active lease for that interface. Of course, we don't
576 * know what the last lease in the file is until we've parsed
577 * the whole file, so at this point, we assume that the lease we
578 * just parsed is the active lease for its interface. If
579 * there's already an active lease for the interface, and this
580 * lease is for the same ip address, then we just toss the old
581 * active lease and replace it with this one. If this lease is
582 * for a different address, then if the old active lease has
583 * expired, we dump it; if not, we put it on the list of leases
584 * for this interface which are still valid but no longer
587 if (ip->client->active) {
588 if (ip->client->active->expiry < cur_time)
589 free_client_lease(ip->client->active);
590 else if (ip->client->active->address.len ==
591 lease->address.len &&
592 !memcmp(ip->client->active->address.iabuf,
593 lease->address.iabuf, lease->address.len))
594 free_client_lease(ip->client->active);
596 ip->client->active->next = ip->client->leases;
597 ip->client->leases = ip->client->active;
600 ip->client->active = lease;
606 * client-lease-declaration :==
609 * FIXED_ADDR ip_address |
611 * SERVER_NAME string |
612 * OPTION option-decl |
618 parse_client_lease_declaration(FILE *cfile, struct client_lease *lease,
619 struct interface_info **ipp)
623 struct interface_info *ip;
625 switch (next_token(&val, cfile)) {
630 token = next_token(&val, cfile);
631 if (token != STRING) {
632 parse_warn("expecting interface name (in quotes).");
636 ip = interface_or_dummy(val);
640 if (!parse_ip_addr(cfile, &lease->address))
644 parse_string_list(cfile, &lease->medium, 0);
647 lease->filename = parse_string(cfile);
650 if (!parse_ip_addr(cfile, &lease->nextserver))
654 lease->server_name = parse_string(cfile);
657 lease->renewal = parse_date(cfile);
660 lease->rebind = parse_date(cfile);
663 lease->expiry = parse_date(cfile);
666 parse_option_decl(cfile, lease->options);
669 parse_warn("expecting lease declaration.");
673 token = next_token(&val, cfile);
675 parse_warn("expecting semicolon.");
681 parse_option_decl(FILE *cfile, struct option_data *options)
686 u_int8_t hunkbuf[1024];
690 struct universe *universe;
691 struct option *option;
692 struct iaddr ip_addr;
697 token = next_token(&val, cfile);
698 if (!is_identifier(token)) {
699 parse_warn("expecting identifier after option keyword.");
704 if ((vendor = strdup(val)) == NULL)
705 error("no memory for vendor information.");
707 token = peek_token(&val, cfile);
709 /* Go ahead and take the DOT token... */
710 token = next_token(&val, cfile);
712 /* The next token should be an identifier... */
713 token = next_token(&val, cfile);
714 if (!is_identifier(token)) {
715 parse_warn("expecting identifier after '.'");
722 /* Look up the option name hash table for the specified
724 universe = ((struct universe *)hash_lookup(&universe_hash,
725 (unsigned char *)vendor, 0));
726 /* If it's not there, we can't parse the rest of the
729 parse_warn("no vendor named %s.", vendor);
735 /* Use the default hash table, which contains all the
736 standard dhcp option names. */
738 universe = &dhcp_universe;
741 /* Look up the actual option info... */
742 option = (struct option *)hash_lookup(universe->hash,
743 (unsigned char *)val, 0);
745 /* If we didn't get an option structure, it's an undefined option. */
748 parse_warn("no option named %s", val);
750 parse_warn("no option named %s for vendor %s",
757 /* Free the initial identifier token. */
760 /* Parse the option data... */
762 for (fmt = option->format; *fmt; fmt++) {
767 len = parse_X(cfile, &hunkbuf[hunkix],
768 sizeof(hunkbuf) - hunkix);
771 case 't': /* Text string... */
772 token = next_token(&val, cfile);
773 if (token != STRING) {
774 parse_warn("expecting string.");
779 if (hunkix + len + 1 > sizeof(hunkbuf)) {
780 parse_warn("option data buffer %s",
785 memcpy(&hunkbuf[hunkix], val, len + 1);
789 case 'I': /* IP address. */
790 if (!parse_ip_addr(cfile, &ip_addr))
795 if (hunkix + len > sizeof(hunkbuf)) {
796 parse_warn("option data buffer "
801 memcpy(&hunkbuf[hunkix], dp, len);
804 case 'L': /* Unsigned 32-bit integer... */
805 case 'l': /* Signed 32-bit integer... */
806 token = next_token(&val, cfile);
807 if (token != NUMBER) {
809 parse_warn("expecting number.");
814 convert_num(buf, val, 0, 32);
818 case 's': /* Signed 16-bit integer. */
819 case 'S': /* Unsigned 16-bit integer. */
820 token = next_token(&val, cfile);
823 convert_num(buf, val, 0, 16);
827 case 'b': /* Signed 8-bit integer. */
828 case 'B': /* Unsigned 8-bit integer. */
829 token = next_token(&val, cfile);
832 convert_num(buf, val, 0, 8);
836 case 'f': /* Boolean flag. */
837 token = next_token(&val, cfile);
838 if (!is_identifier(token)) {
839 parse_warn("expecting identifier.");
845 if (!strcasecmp(val, "true") ||
846 !strcasecmp(val, "on"))
848 else if (!strcasecmp(val, "false") ||
849 !strcasecmp(val, "off"))
852 parse_warn("expecting boolean.");
859 warning("Bad format %c in parse_option_param.",
865 token = next_token(&val, cfile);
866 } while (*fmt == 'A' && token == COMMA);
869 parse_warn("semicolon expected.");
874 options[option->code].data = malloc(hunkix + nul_term);
875 if (!options[option->code].data)
876 error("out of memory allocating option data.");
877 memcpy(options[option->code].data, hunkbuf, hunkix + nul_term);
878 options[option->code].len = hunkix;
883 parse_string_list(FILE *cfile, struct string_list **lp, int multiple)
888 struct string_list *cur, *tmp;
890 /* Find the last medium in the media list. */
892 for (cur = *lp; cur->next; cur = cur->next)
898 token = next_token(&val, cfile);
899 if (token != STRING) {
900 parse_warn("Expecting media options.");
905 valsize = strlen(val) + 1;
906 tmp = new_string_list(valsize);
908 error("no memory for string list entry.");
909 memcpy(tmp->string, val, valsize);
912 /* Store this medium at the end of the media list. */
919 token = next_token(&val, cfile);
920 } while (multiple && token == COMMA);
923 parse_warn("expecting semicolon.");
929 parse_reject_statement(FILE *cfile, struct client_config *config)
934 struct iaddrlist *list;
937 if (!parse_ip_addr(cfile, &addr)) {
938 parse_warn("expecting IP address.");
943 list = malloc(sizeof(struct iaddrlist));
945 error("no memory for reject list!");
948 list->next = config->reject_list;
949 config->reject_list = list;
951 token = next_token(&val, cfile);
952 } while (token == COMMA);
955 parse_warn("expecting semicolon.");