1 /* $OpenBSD: clparse.c,v 1.18 2004/09/15 18:15:18 henning 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''.
46 struct client_config top_level_config;
47 struct interface_info *dummy_interfaces;
48 extern struct interface_info *ifi;
50 char client_script_name[] = "/sbin/dhclient-script";
53 * client-conf-file :== client-declarations EOF
54 * client-declarations :== <nil>
55 * | client-declaration
56 * | client-declarations client-declaration
59 read_client_conf(void)
64 struct client_config *config;
66 new_parse(path_dhclient_conf);
68 /* Set up the initial dhcp option universe. */
69 initialize_universes();
71 /* Initialize the top level client configuration. */
72 memset(&top_level_config, 0, sizeof(top_level_config));
74 /* Set some defaults... */
75 top_level_config.timeout = 60;
76 top_level_config.select_interval = 0;
77 top_level_config.reboot_timeout = 10;
78 top_level_config.retry_interval = 300;
79 top_level_config.backoff_cutoff = 15;
80 top_level_config.initial_interval = 3;
81 top_level_config.bootp_policy = ACCEPT;
82 top_level_config.script_name = client_script_name;
83 top_level_config.requested_options
84 [top_level_config.requested_option_count++] = DHO_SUBNET_MASK;
85 top_level_config.requested_options
86 [top_level_config.requested_option_count++] = DHO_BROADCAST_ADDRESS;
87 top_level_config.requested_options
88 [top_level_config.requested_option_count++] = DHO_TIME_OFFSET;
89 top_level_config.requested_options
90 [top_level_config.requested_option_count++] = DHO_ROUTERS;
91 top_level_config.requested_options
92 [top_level_config.requested_option_count++] = DHO_DOMAIN_NAME;
93 top_level_config.requested_options
94 [top_level_config.requested_option_count++] =
95 DHO_DOMAIN_NAME_SERVERS;
96 top_level_config.requested_options
97 [top_level_config.requested_option_count++] = DHO_HOST_NAME;
99 if ((cfile = fopen(path_dhclient_conf, "r")) != NULL) {
101 token = peek_token(&val, cfile);
104 parse_client_statement(cfile, NULL, &top_level_config);
106 token = next_token(&val, cfile); /* Clear the peek buffer */
111 * Set up state and config structures for clients that don't
112 * have per-interface configuration declarations.
116 ifi->client = malloc(sizeof(struct client_state));
118 error("no memory for client state.");
119 memset(ifi->client, 0, sizeof(*(ifi->client)));
121 if (!ifi->client->config) {
123 config = malloc(sizeof(struct client_config));
125 error("no memory for client config.");
126 memcpy(config, &top_level_config,
127 sizeof(top_level_config));
129 ifi->client->config = config;
132 return (!warnings_occurred);
136 * lease-file :== client-lease-statements EOF
137 * client-lease-statements :== <nil>
138 * | client-lease-statements LEASE client-lease-statement
141 read_client_leases(void)
147 new_parse(path_dhclient_db);
149 /* Open the lease file. If we can't open it, just return -
150 we can safely trust the server to remember our state. */
151 if ((cfile = fopen(path_dhclient_db, "r")) == NULL)
154 token = next_token(&val, cfile);
157 if (token != LEASE) {
158 warning("Corrupt lease file - possible data loss!");
162 parse_client_lease_statement(cfile, 0);
169 * client-declaration :==
171 * DEFAULT option-decl |
172 * SUPERSEDE option-decl |
173 * PREPEND option-decl |
174 * APPEND option-decl |
175 * hardware-declaration |
176 * REQUEST option-list |
177 * REQUIRE option-list |
181 * SELECT_TIMEOUT number |
183 * interface-declaration |
184 * LEASE client-lease-statement |
185 * ALIAS client-lease-statement
188 parse_client_statement(FILE *cfile, struct interface_info *ip,
189 struct client_config *config)
193 struct option *option;
195 switch (next_token(&val, cfile)) {
197 parse_option_decl(cfile, &config->send_options[0]);
200 option = parse_option_decl(cfile, &config->defaults[0]);
202 config->default_actions[option->code] = ACTION_DEFAULT;
205 option = parse_option_decl(cfile, &config->defaults[0]);
207 config->default_actions[option->code] =
211 option = parse_option_decl(cfile, &config->defaults[0]);
213 config->default_actions[option->code] = ACTION_APPEND;
216 option = parse_option_decl(cfile, &config->defaults[0]);
218 config->default_actions[option->code] = ACTION_PREPEND;
221 parse_string_list(cfile, &config->media, 1);
225 parse_hardware_param(cfile, &ip->hw_address);
227 parse_warn("hardware address parameter %s",
228 "not allowed here.");
233 config->requested_option_count =
234 parse_option_list(cfile, config->requested_options);
237 memset(config->required_options, 0,
238 sizeof(config->required_options));
239 parse_option_list(cfile, config->required_options);
242 parse_lease_time(cfile, &config->timeout);
245 parse_lease_time(cfile, &config->retry_interval);
248 parse_lease_time(cfile, &config->select_interval);
251 parse_lease_time(cfile, &config->reboot_timeout);
254 parse_lease_time(cfile, &config->backoff_cutoff);
256 case INITIAL_INTERVAL:
257 parse_lease_time(cfile, &config->initial_interval);
260 config->script_name = parse_string(cfile);
264 parse_warn("nested interface declaration.");
265 parse_interface_declaration(cfile, config);
268 parse_client_lease_statement(cfile, 1);
271 parse_client_lease_statement(cfile, 2);
274 parse_reject_statement(cfile, config);
277 parse_warn("expecting a statement.");
281 token = next_token(&val, cfile);
283 parse_warn("semicolon expected.");
289 parse_X(FILE *cfile, u_int8_t *buf, int max)
295 token = peek_token(&val, cfile);
296 if (token == NUMBER_OR_NAME || token == NUMBER) {
299 token = next_token(&val, cfile);
300 if (token != NUMBER && token != NUMBER_OR_NAME) {
301 parse_warn("expecting hexadecimal constant.");
305 convert_num(&buf[len], val, 16, 8);
307 parse_warn("hexadecimal constant too long.");
311 token = peek_token(&val, cfile);
313 token = next_token(&val, cfile);
314 } while (token == COLON);
316 } else if (token == STRING) {
317 token = next_token(&val, cfile);
320 parse_warn("string constant too long.");
324 memcpy(buf, val, len + 1);
326 parse_warn("expecting string or hexadecimal data");
334 * option-list :== option_name |
335 * option_list COMMA option_name
338 parse_option_list(FILE *cfile, u_int8_t *list)
346 token = next_token(&val, cfile);
347 if (!is_identifier(token)) {
348 parse_warn("expected option name.");
352 for (i = 0; i < 256; i++)
353 if (!strcasecmp(dhcp_options[i].name, val))
357 parse_warn("%s: unexpected option name.", val);
363 parse_warn("%s: too many options.", val);
367 token = next_token(&val, cfile);
368 } while (token == COMMA);
370 parse_warn("expecting semicolon.");
378 * interface-declaration :==
379 * INTERFACE string LBRACE client-declarations RBRACE
382 parse_interface_declaration(FILE *cfile, struct client_config *outer_config)
386 struct interface_info *ip;
388 token = next_token(&val, cfile);
389 if (token != STRING) {
390 parse_warn("expecting interface name (in quotes).");
395 ip = interface_or_dummy(val);
398 make_client_state(ip);
400 if (!ip->client->config)
401 make_client_config(ip, outer_config);
403 token = next_token(&val, cfile);
404 if (token != LBRACE) {
405 parse_warn("expecting left brace.");
411 token = peek_token(&val, cfile);
413 parse_warn("unterminated interface declaration.");
418 parse_client_statement(cfile, ip, ip->client->config);
420 token = next_token(&val, cfile);
423 struct interface_info *
424 interface_or_dummy(char *name)
426 struct interface_info *ip;
428 /* Find the interface (if any) that matches the name. */
429 if (!strcmp(ifi->name, name))
432 /* If it's not a real interface, see if it's on the dummy list. */
433 for (ip = dummy_interfaces; ip; ip = ip->next)
434 if (!strcmp(ip->name, name))
438 * If we didn't find an interface, make a dummy interface as a
441 ip = malloc(sizeof(*ip));
443 error("Insufficient memory to record interface %s", name);
444 memset(ip, 0, sizeof(*ip));
445 strlcpy(ip->name, name, IFNAMSIZ);
446 ip->next = dummy_interfaces;
447 dummy_interfaces = ip;
452 make_client_state(struct interface_info *ip)
454 ip->client = malloc(sizeof(*(ip->client)));
456 error("no memory for state on %s", ip->name);
457 memset(ip->client, 0, sizeof(*(ip->client)));
461 make_client_config(struct interface_info *ip, struct client_config *config)
463 ip->client->config = malloc(sizeof(struct client_config));
464 if (!ip->client->config)
465 error("no memory for config for %s", ip->name);
466 memset(ip->client->config, 0, sizeof(*(ip->client->config)));
467 memcpy(ip->client->config, config, sizeof(*config));
471 * client-lease-statement :==
472 * RBRACE client-lease-declarations LBRACE
474 * client-lease-declarations :==
476 * client-lease-declaration |
477 * client-lease-declarations client-lease-declaration
480 parse_client_lease_statement(FILE *cfile, int is_static)
482 struct client_lease *lease, *lp, *pl;
483 struct interface_info *ip;
487 token = next_token(&val, cfile);
488 if (token != LBRACE) {
489 parse_warn("expecting left brace.");
494 lease = malloc(sizeof(struct client_lease));
496 error("no memory for lease.");
497 memset(lease, 0, sizeof(*lease));
498 lease->is_static = is_static;
503 token = peek_token(&val, cfile);
505 parse_warn("unterminated lease declaration.");
510 parse_client_lease_declaration(cfile, lease, &ip);
512 token = next_token(&val, cfile);
514 /* If the lease declaration didn't include an interface
515 * declaration that we recognized, it's of no use to us.
518 free_client_lease(lease);
522 /* Make sure there's a client state structure... */
524 make_client_state(ip);
526 /* If this is an alias lease, it doesn't need to be sorted in. */
527 if (is_static == 2) {
528 ip->client->alias = lease;
533 * The new lease may supersede a lease that's not the active
534 * lease but is still on the lease list, so scan the lease list
535 * looking for a lease with the same address, and if we find it,
539 for (lp = ip->client->leases; lp; lp = lp->next) {
540 if (lp->address.len == lease->address.len &&
541 !memcmp(lp->address.iabuf, lease->address.iabuf,
542 lease->address.len)) {
546 ip->client->leases = lp->next;
547 free_client_lease(lp);
553 * If this is a preloaded lease, just put it on the list of
554 * recorded leases - don't make it the active lease.
557 lease->next = ip->client->leases;
558 ip->client->leases = lease;
563 * The last lease in the lease file on a particular interface is
564 * the active lease for that interface. Of course, we don't
565 * know what the last lease in the file is until we've parsed
566 * the whole file, so at this point, we assume that the lease we
567 * just parsed is the active lease for its interface. If
568 * there's already an active lease for the interface, and this
569 * lease is for the same ip address, then we just toss the old
570 * active lease and replace it with this one. If this lease is
571 * for a different address, then if the old active lease has
572 * expired, we dump it; if not, we put it on the list of leases
573 * for this interface which are still valid but no longer
576 if (ip->client->active) {
577 if (ip->client->active->expiry < cur_time)
578 free_client_lease(ip->client->active);
579 else if (ip->client->active->address.len ==
580 lease->address.len &&
581 !memcmp(ip->client->active->address.iabuf,
582 lease->address.iabuf, lease->address.len))
583 free_client_lease(ip->client->active);
585 ip->client->active->next = ip->client->leases;
586 ip->client->leases = ip->client->active;
589 ip->client->active = lease;
595 * client-lease-declaration :==
598 * FIXED_ADDR ip_address |
600 * SERVER_NAME string |
601 * OPTION option-decl |
607 parse_client_lease_declaration(FILE *cfile, struct client_lease *lease,
608 struct interface_info **ipp)
612 struct interface_info *ip;
614 switch (next_token(&val, cfile)) {
619 token = next_token(&val, cfile);
620 if (token != STRING) {
621 parse_warn("expecting interface name (in quotes).");
625 ip = interface_or_dummy(val);
629 if (!parse_ip_addr(cfile, &lease->address))
633 parse_string_list(cfile, &lease->medium, 0);
636 lease->filename = parse_string(cfile);
639 lease->server_name = parse_string(cfile);
642 lease->renewal = parse_date(cfile);
645 lease->rebind = parse_date(cfile);
648 lease->expiry = parse_date(cfile);
651 parse_option_decl(cfile, lease->options);
654 parse_warn("expecting lease declaration.");
658 token = next_token(&val, cfile);
660 parse_warn("expecting semicolon.");
666 parse_option_decl(FILE *cfile, struct option_data *options)
671 u_int8_t hunkbuf[1024];
675 struct universe *universe;
676 struct option *option;
677 struct iaddr ip_addr;
682 token = next_token(&val, cfile);
683 if (!is_identifier(token)) {
684 parse_warn("expecting identifier after option keyword.");
689 if ((vendor = strdup(val)) == NULL)
690 error("no memory for vendor information.");
692 token = peek_token(&val, cfile);
694 /* Go ahead and take the DOT token... */
695 token = next_token(&val, cfile);
697 /* The next token should be an identifier... */
698 token = next_token(&val, cfile);
699 if (!is_identifier(token)) {
700 parse_warn("expecting identifier after '.'");
706 /* Look up the option name hash table for the specified
708 universe = ((struct universe *)hash_lookup(&universe_hash,
709 (unsigned char *)vendor, 0));
710 /* If it's not there, we can't parse the rest of the
713 parse_warn("no vendor named %s.", vendor);
718 /* Use the default hash table, which contains all the
719 standard dhcp option names. */
721 universe = &dhcp_universe;
724 /* Look up the actual option info... */
725 option = (struct option *)hash_lookup(universe->hash,
726 (unsigned char *)val, 0);
728 /* If we didn't get an option structure, it's an undefined option. */
731 parse_warn("no option named %s", val);
733 parse_warn("no option named %s for vendor %s",
739 /* Free the initial identifier token. */
742 /* Parse the option data... */
744 for (fmt = option->format; *fmt; fmt++) {
749 len = parse_X(cfile, &hunkbuf[hunkix],
750 sizeof(hunkbuf) - hunkix);
753 case 't': /* Text string... */
754 token = next_token(&val, cfile);
755 if (token != STRING) {
756 parse_warn("expecting string.");
761 if (hunkix + len + 1 > sizeof(hunkbuf)) {
762 parse_warn("option data buffer %s",
767 memcpy(&hunkbuf[hunkix], val, len + 1);
771 case 'I': /* IP address. */
772 if (!parse_ip_addr(cfile, &ip_addr))
777 if (hunkix + len > sizeof(hunkbuf)) {
778 parse_warn("option data buffer "
783 memcpy(&hunkbuf[hunkix], dp, len);
786 case 'L': /* Unsigned 32-bit integer... */
787 case 'l': /* Signed 32-bit integer... */
788 token = next_token(&val, cfile);
789 if (token != NUMBER) {
791 parse_warn("expecting number.");
796 convert_num(buf, val, 0, 32);
800 case 's': /* Signed 16-bit integer. */
801 case 'S': /* Unsigned 16-bit integer. */
802 token = next_token(&val, cfile);
805 convert_num(buf, val, 0, 16);
809 case 'b': /* Signed 8-bit integer. */
810 case 'B': /* Unsigned 8-bit integer. */
811 token = next_token(&val, cfile);
814 convert_num(buf, val, 0, 8);
818 case 'f': /* Boolean flag. */
819 token = next_token(&val, cfile);
820 if (!is_identifier(token)) {
821 parse_warn("expecting identifier.");
827 if (!strcasecmp(val, "true") ||
828 !strcasecmp(val, "on"))
830 else if (!strcasecmp(val, "false") ||
831 !strcasecmp(val, "off"))
834 parse_warn("expecting boolean.");
841 warning("Bad format %c in parse_option_param.",
847 token = next_token(&val, cfile);
848 } while (*fmt == 'A' && token == COMMA);
851 parse_warn("semicolon expected.");
856 options[option->code].data = malloc(hunkix + nul_term);
857 if (!options[option->code].data)
858 error("out of memory allocating option data.");
859 memcpy(options[option->code].data, hunkbuf, hunkix + nul_term);
860 options[option->code].len = hunkix;
865 parse_string_list(FILE *cfile, struct string_list **lp, int multiple)
869 struct string_list *cur, *tmp;
871 /* Find the last medium in the media list. */
873 for (cur = *lp; cur->next; cur = cur->next)
879 token = next_token(&val, cfile);
880 if (token != STRING) {
881 parse_warn("Expecting media options.");
886 tmp = new_string_list(strlen(val) + 1);
888 error("no memory for string list entry.");
889 strlcpy(tmp->string, val, strlen(val) + 1);
892 /* Store this medium at the end of the media list. */
899 token = next_token(&val, cfile);
900 } while (multiple && token == COMMA);
903 parse_warn("expecting semicolon.");
909 parse_reject_statement(FILE *cfile, struct client_config *config)
914 struct iaddrlist *list;
917 if (!parse_ip_addr(cfile, &addr)) {
918 parse_warn("expecting IP address.");
923 list = malloc(sizeof(struct iaddrlist));
925 error("no memory for reject list!");
928 list->next = config->reject_list;
929 config->reject_list = list;
931 token = next_token(&val, cfile);
932 } while (token == COMMA);
935 parse_warn("expecting semicolon.");