1 /* $OpenBSD: clparse.c,v 1.34 2007/02/14 23:19:26 deraadt Exp $ */
2 /* $DragonFly: src/sbin/dhclient/clparse.c,v 1.1 2008/08/30 16:07:58 hasso Exp $ */
4 /* Parser for dhclient config and lease files... */
7 * Copyright (c) 1997 The Internet Software Consortium.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of The Internet Software Consortium nor the names
20 * of its contributors may be used to endorse or promote products derived
21 * from this software without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
24 * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
25 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
26 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27 * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
28 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
30 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
31 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
32 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
33 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
34 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * This software has been written for the Internet Software Consortium
38 * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
39 * Enterprises. To learn more about the Internet Software Consortium,
40 * see ``http://www.vix.com/isc''. To learn more about Vixie
41 * Enterprises, see ``http://www.vix.com''.
48 * client-conf-file :== client-declarations EOF
49 * client-declarations :== <nil>
50 * | client-declaration
51 * | client-declarations client-declaration
54 read_client_conf(void)
60 new_parse(path_dhclient_conf);
62 /* Set some defaults... */
63 config->link_timeout = 10;
65 config->select_interval = 0;
66 config->reboot_timeout = 10;
67 config->retry_interval = 300;
68 config->backoff_cutoff = 15;
69 config->initial_interval = 3;
70 config->bootp_policy = ACCEPT;
71 config->script_name = _PATH_DHCLIENT_SCRIPT;
72 config->requested_options
73 [config->requested_option_count++] = DHO_SUBNET_MASK;
74 config->requested_options
75 [config->requested_option_count++] = DHO_BROADCAST_ADDRESS;
76 config->requested_options
77 [config->requested_option_count++] = DHO_TIME_OFFSET;
78 config->requested_options
79 [config->requested_option_count++] = DHO_ROUTERS;
80 config->requested_options
81 [config->requested_option_count++] = DHO_DOMAIN_NAME;
82 config->requested_options
83 [config->requested_option_count++] = DHO_DOMAIN_NAME_SERVERS;
84 config->requested_options
85 [config->requested_option_count++] = DHO_HOST_NAME;
87 if ((cfile = fopen(path_dhclient_conf, "r")) != NULL) {
89 token = peek_token(&val, cfile);
92 parse_client_statement(cfile);
94 token = next_token(&val, cfile); /* Clear the peek buffer */
98 return (!warnings_occurred);
102 * lease-file :== client-lease-statements EOF
103 * client-lease-statements :== <nil>
104 * | client-lease-statements LEASE client-lease-statement
107 read_client_leases(void)
113 new_parse(path_dhclient_db);
115 /* Open the lease file. If we can't open it, just return -
116 we can safely trust the server to remember our state. */
117 if ((cfile = fopen(path_dhclient_db, "r")) == NULL)
120 token = next_token(&val, cfile);
123 if (token != TOK_LEASE) {
124 warning("Corrupt lease file - possible data loss!");
128 parse_client_lease_statement(cfile, 0);
135 * client-declaration :==
136 * TOK_SEND option-decl |
137 * TOK_DEFAULT option-decl |
138 * TOK_SUPERSEDE option-decl |
139 * TOK_APPEND option-decl |
140 * TOK_PREPEND option-decl |
141 * TOK_MEDIA string-list |
142 * hardware-declaration |
143 * TOK_REQUEST option-list |
144 * TOK_REQUIRE option-list |
145 * TOK_TIMEOUT number |
147 * TOK_SELECT_TIMEOUT number |
148 * TOK_REBOOT number |
149 * TOK_BACKOFF_CUTOFF number |
150 * TOK_INITIAL_INTERVAL number |
151 * TOK_SCRIPT string |
152 * interface-declaration |
153 * TOK_LEASE client-lease-statement |
154 * TOK_ALIAS client-lease-statement |
155 * TOK_REJECT reject-statement
158 parse_client_statement(FILE *cfile)
163 switch (next_token(&val, cfile)) {
165 parse_option_decl(cfile, &config->send_options[0]);
168 code = parse_option_decl(cfile, &config->defaults[0]);
170 config->default_actions[code] = ACTION_DEFAULT;
173 code = parse_option_decl(cfile, &config->defaults[0]);
175 config->default_actions[code] = ACTION_SUPERSEDE;
178 code = parse_option_decl(cfile, &config->defaults[0]);
180 config->default_actions[code] = ACTION_APPEND;
183 code = parse_option_decl(cfile, &config->defaults[0]);
185 config->default_actions[code] = ACTION_PREPEND;
188 parse_string_list(cfile, &config->media, 1);
191 parse_hardware_param(cfile, &ifi->hw_address);
194 config->requested_option_count =
195 parse_option_list(cfile, config->requested_options);
198 memset(config->required_options, 0,
199 sizeof(config->required_options));
200 parse_option_list(cfile, config->required_options);
202 case TOK_LINK_TIMEOUT:
203 parse_lease_time(cfile, &config->link_timeout);
206 parse_lease_time(cfile, &config->timeout);
209 parse_lease_time(cfile, &config->retry_interval);
211 case TOK_SELECT_TIMEOUT:
212 parse_lease_time(cfile, &config->select_interval);
215 parse_lease_time(cfile, &config->reboot_timeout);
217 case TOK_BACKOFF_CUTOFF:
218 parse_lease_time(cfile, &config->backoff_cutoff);
220 case TOK_INITIAL_INTERVAL:
221 parse_lease_time(cfile, &config->initial_interval);
224 config->script_name = parse_string(cfile);
227 parse_interface_declaration(cfile);
230 parse_client_lease_statement(cfile, 1);
233 parse_client_lease_statement(cfile, 2);
236 parse_reject_statement(cfile);
239 parse_warn("expecting a statement.");
243 token = next_token(&val, cfile);
245 parse_warn("semicolon expected.");
251 parse_X(FILE *cfile, u_int8_t *buf, int max)
257 token = peek_token(&val, cfile);
258 if (token == TOK_NUMBER_OR_NAME || token == TOK_NUMBER) {
261 token = next_token(&val, cfile);
262 if (token != TOK_NUMBER && token != TOK_NUMBER_OR_NAME) {
263 parse_warn("expecting hexadecimal constant.");
267 convert_num(&buf[len], val, 16, 8);
269 parse_warn("hexadecimal constant too long.");
273 token = peek_token(&val, cfile);
275 token = next_token(&val, cfile);
276 } while (token == ':');
278 } else if (token == TOK_STRING) {
279 token = next_token(&val, cfile);
282 parse_warn("string constant too long.");
286 memcpy(buf, val, len + 1);
288 parse_warn("expecting string or hexadecimal data");
296 * option-list :== option_name |
297 * option_list COMMA option_name
300 parse_option_list(FILE *cfile, u_int8_t *list)
308 token = next_token(&val, cfile);
309 if (!is_identifier(token)) {
310 parse_warn("expected option name.");
314 for (i = 0; i < 256; i++)
315 if (!strcasecmp(dhcp_options[i].name, val))
319 parse_warn("%s: unexpected option name.", val);
325 parse_warn("%s: too many options.", val);
329 token = next_token(&val, cfile);
330 } while (token == ',');
332 parse_warn("expecting semicolon.");
340 * interface-declaration :==
341 * INTERFACE string LBRACE client-declarations RBRACE
344 parse_interface_declaration(FILE *cfile)
349 token = next_token(&val, cfile);
350 if (token != TOK_STRING) {
351 parse_warn("expecting interface name (in quotes).");
356 if (strcmp(ifi->name, val) != 0) {
361 token = next_token(&val, cfile);
363 parse_warn("expecting left brace.");
369 token = peek_token(&val, cfile);
371 parse_warn("unterminated interface declaration.");
376 parse_client_statement(cfile);
378 token = next_token(&val, cfile);
382 * client-lease-statement :==
383 * RBRACE client-lease-declarations LBRACE
385 * client-lease-declarations :==
387 * client-lease-declaration |
388 * client-lease-declarations client-lease-declaration
391 parse_client_lease_statement(FILE *cfile, int is_static)
393 struct client_lease *lease, *lp, *pl;
397 token = next_token(&val, cfile);
399 parse_warn("expecting left brace.");
404 lease = malloc(sizeof(struct client_lease));
406 error("no memory for lease.");
407 memset(lease, 0, sizeof(*lease));
408 lease->is_static = is_static;
411 token = peek_token(&val, cfile);
413 parse_warn("unterminated lease declaration.");
418 parse_client_lease_declaration(cfile, lease);
420 token = next_token(&val, cfile);
422 /* If the lease declaration didn't include an interface
423 * declaration that we recognized, it's of no use to us.
426 free_client_lease(lease);
430 /* If this is an alias lease, it doesn't need to be sorted in. */
431 if (is_static == 2) {
432 client->alias = lease;
437 * The new lease may supersede a lease that's not the active
438 * lease but is still on the lease list, so scan the lease list
439 * looking for a lease with the same address, and if we find it,
443 for (lp = client->leases; lp; lp = lp->next) {
444 if (lp->address.len == lease->address.len &&
445 !memcmp(lp->address.iabuf, lease->address.iabuf,
446 lease->address.len)) {
450 client->leases = lp->next;
451 free_client_lease(lp);
457 * If this is a preloaded lease, just put it on the list of
458 * recorded leases - don't make it the active lease.
461 lease->next = client->leases;
462 client->leases = lease;
467 * The last lease in the lease file on a particular interface is
468 * the active lease for that interface. Of course, we don't
469 * know what the last lease in the file is until we've parsed
470 * the whole file, so at this point, we assume that the lease we
471 * just parsed is the active lease for its interface. If
472 * there's already an active lease for the interface, and this
473 * lease is for the same ip address, then we just toss the old
474 * active lease and replace it with this one. If this lease is
475 * for a different address, then if the old active lease has
476 * expired, we dump it; if not, we put it on the list of leases
477 * for this interface which are still valid but no longer
480 if (client->active) {
481 if (client->active->expiry < cur_time)
482 free_client_lease(client->active);
483 else if (client->active->address.len ==
484 lease->address.len &&
485 !memcmp(client->active->address.iabuf,
486 lease->address.iabuf, lease->address.len))
487 free_client_lease(client->active);
489 client->active->next = client->leases;
490 client->leases = client->active;
493 client->active = lease;
499 * client-lease-declaration :==
502 * FIXED_ADDR ip_address |
504 * SERVER_NAME string |
505 * OPTION option-decl |
511 parse_client_lease_declaration(FILE *cfile, struct client_lease *lease)
516 switch (next_token(&val, cfile)) {
521 token = next_token(&val, cfile);
522 if (token != TOK_STRING) {
523 parse_warn("expecting interface name (in quotes).");
527 if (strcmp(ifi->name, val) != 0) {
528 parse_warn("wrong interface name. Expecting '%s'.",
535 if (!parse_ip_addr(cfile, &lease->address))
539 parse_string_list(cfile, &lease->medium, 0);
542 lease->filename = parse_string(cfile);
544 case TOK_SERVER_NAME:
545 lease->server_name = parse_string(cfile);
548 lease->renewal = parse_date(cfile);
551 lease->rebind = parse_date(cfile);
554 lease->expiry = parse_date(cfile);
557 parse_option_decl(cfile, lease->options);
560 parse_warn("expecting lease declaration.");
564 token = next_token(&val, cfile);
566 parse_warn("expecting semicolon.");
572 parse_option_decl(FILE *cfile, struct option_data *options)
577 u_int8_t hunkbuf[1024];
580 struct iaddr ip_addr;
585 token = next_token(&val, cfile);
586 if (!is_identifier(token)) {
587 parse_warn("expecting identifier after option keyword.");
593 /* Look up the actual option info. */
595 for (code = 0; code < 256; code++)
596 if (strcmp(dhcp_options[code].name, val) == 0)
600 parse_warn("no option named %s", val);
605 /* Parse the option data... */
607 for (fmt = dhcp_options[code].format; *fmt; fmt++) {
612 len = parse_X(cfile, &hunkbuf[hunkix],
613 sizeof(hunkbuf) - hunkix);
616 case 't': /* Text string... */
617 token = next_token(&val, cfile);
618 if (token != TOK_STRING) {
619 parse_warn("expecting string.");
624 if (hunkix + len + 1 > sizeof(hunkbuf)) {
625 parse_warn("option data buffer %s",
630 memcpy(&hunkbuf[hunkix], val, len + 1);
634 case 'I': /* IP address. */
635 if (!parse_ip_addr(cfile, &ip_addr))
640 if (hunkix + len > sizeof(hunkbuf)) {
641 parse_warn("option data buffer "
646 memcpy(&hunkbuf[hunkix], dp, len);
649 case 'L': /* Unsigned 32-bit integer... */
650 case 'l': /* Signed 32-bit integer... */
651 token = next_token(&val, cfile);
652 if (token != TOK_NUMBER) {
654 parse_warn("expecting number.");
659 convert_num(buf, val, 0, 32);
663 case 's': /* Signed 16-bit integer. */
664 case 'S': /* Unsigned 16-bit integer. */
665 token = next_token(&val, cfile);
666 if (token != TOK_NUMBER)
668 convert_num(buf, val, 0, 16);
672 case 'b': /* Signed 8-bit integer. */
673 case 'B': /* Unsigned 8-bit integer. */
674 token = next_token(&val, cfile);
675 if (token != TOK_NUMBER)
677 convert_num(buf, val, 0, 8);
681 case 'f': /* Boolean flag. */
682 token = next_token(&val, cfile);
683 if (!is_identifier(token)) {
684 parse_warn("expecting identifier.");
690 if (!strcasecmp(val, "true") ||
691 !strcasecmp(val, "on"))
693 else if (!strcasecmp(val, "false") ||
694 !strcasecmp(val, "off"))
697 parse_warn("expecting boolean.");
704 warning("Bad format %c in parse_option_param.",
710 token = next_token(&val, cfile);
711 } while (*fmt == 'A' && token == ',');
714 parse_warn("semicolon expected.");
719 options[code].data = malloc(hunkix + nul_term);
720 if (!options[code].data)
721 error("out of memory allocating option data.");
722 memcpy(options[code].data, hunkbuf, hunkix + nul_term);
723 options[code].len = hunkix;
728 parse_string_list(FILE *cfile, struct string_list **lp, int multiple)
732 struct string_list *cur, *tmp;
734 /* Find the last medium in the media list. */
736 for (cur = *lp; cur->next; cur = cur->next)
742 token = next_token(&val, cfile);
743 if (token != TOK_STRING) {
744 parse_warn("Expecting media options.");
749 tmp = malloc(sizeof(struct string_list) + strlen(val));
751 error("no memory for string list entry.");
752 strlcpy(tmp->string, val, strlen(val) + 1);
755 /* Store this medium at the end of the media list. */
762 token = next_token(&val, cfile);
763 } while (multiple && token == ',');
766 parse_warn("expecting semicolon.");
772 parse_reject_statement(FILE *cfile)
774 struct iaddrlist *list;
780 if (!parse_ip_addr(cfile, &addr)) {
781 parse_warn("expecting IP address.");
786 list = malloc(sizeof(struct iaddrlist));
788 error("no memory for reject list!");
791 list->next = config->reject_list;
792 config->reject_list = list;
794 token = next_token(&val, cfile);
795 } while (token == ',');
798 parse_warn("expecting semicolon.");