1 /* $OpenBSD: src/sbin/dhclient/clparse.c,v 1.38 2011/12/10 17:15:27 krw 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)
58 new_parse(path_dhclient_conf
);
60 /* Set some defaults... */
61 config
->link_timeout
= 10;
63 config
->select_interval
= 0;
64 config
->reboot_timeout
= 10;
65 config
->retry_interval
= 300;
66 config
->backoff_cutoff
= 15;
67 config
->initial_interval
= 3;
68 config
->bootp_policy
= ACCEPT
;
69 config
->script_name
= _PATH_DHCLIENT_SCRIPT
;
70 config
->requested_options
71 [config
->requested_option_count
++] = DHO_SUBNET_MASK
;
72 config
->requested_options
73 [config
->requested_option_count
++] = DHO_BROADCAST_ADDRESS
;
74 config
->requested_options
75 [config
->requested_option_count
++] = DHO_TIME_OFFSET
;
76 config
->requested_options
77 [config
->requested_option_count
++] = DHO_ROUTERS
;
78 config
->requested_options
79 [config
->requested_option_count
++] = DHO_DOMAIN_NAME
;
80 config
->requested_options
81 [config
->requested_option_count
++] = DHO_DOMAIN_NAME_SERVERS
;
82 config
->requested_options
83 [config
->requested_option_count
++] = DHO_HOST_NAME
;
85 if ((cfile
= fopen(path_dhclient_conf
, "r")) != NULL
) {
87 token
= peek_token(NULL
, cfile
);
90 parse_client_statement(cfile
);
92 token
= next_token(NULL
, cfile
); /* Clear the peek buffer */
96 return (!warnings_occurred
);
100 * lease-file :== client-lease-statements EOF
101 * client-lease-statements :== <nil>
102 * | client-lease-statements LEASE client-lease-statement
105 read_client_leases(void)
110 new_parse(path_dhclient_db
);
112 /* Open the lease file. If we can't open it, just return -
113 we can safely trust the server to remember our state. */
114 if ((cfile
= fopen(path_dhclient_db
, "r")) == NULL
)
117 token
= next_token(NULL
, cfile
);
120 if (token
!= TOK_LEASE
) {
121 warning("Corrupt lease file - possible data loss!");
125 parse_client_lease_statement(cfile
, 0);
132 * client-declaration :==
133 * TOK_SEND option-decl |
134 * TOK_DEFAULT option-decl |
135 * TOK_SUPERSEDE option-decl |
136 * TOK_APPEND option-decl |
137 * TOK_PREPEND option-decl |
138 * TOK_MEDIA string-list |
139 * hardware-declaration |
140 * TOK_REQUEST option-list |
141 * TOK_REQUIRE option-list |
142 * TOK_TIMEOUT number |
144 * TOK_SELECT_TIMEOUT number |
145 * TOK_REBOOT number |
146 * TOK_BACKOFF_CUTOFF number |
147 * TOK_INITIAL_INTERVAL number |
148 * TOK_SCRIPT string |
149 * interface-declaration |
150 * TOK_LEASE client-lease-statement |
151 * TOK_ALIAS client-lease-statement |
152 * TOK_REJECT reject-statement
155 parse_client_statement(FILE *cfile
)
159 switch (next_token(NULL
, cfile
)) {
161 parse_option_decl(cfile
, &config
->send_options
[0]);
164 code
= parse_option_decl(cfile
, &config
->defaults
[0]);
166 config
->default_actions
[code
] = ACTION_DEFAULT
;
169 code
= parse_option_decl(cfile
, &config
->defaults
[0]);
171 config
->default_actions
[code
] = ACTION_SUPERSEDE
;
174 code
= parse_option_decl(cfile
, &config
->defaults
[0]);
176 config
->default_actions
[code
] = ACTION_APPEND
;
179 code
= parse_option_decl(cfile
, &config
->defaults
[0]);
181 config
->default_actions
[code
] = ACTION_PREPEND
;
187 parse_hardware_param(cfile
, &ifi
->hw_address
);
190 config
->requested_option_count
=
191 parse_option_list(cfile
, config
->requested_options
);
194 memset(config
->required_options
, 0,
195 sizeof(config
->required_options
));
196 parse_option_list(cfile
, config
->required_options
);
198 case TOK_LINK_TIMEOUT
:
199 parse_lease_time(cfile
, &config
->link_timeout
);
202 parse_lease_time(cfile
, &config
->timeout
);
205 parse_lease_time(cfile
, &config
->retry_interval
);
207 case TOK_SELECT_TIMEOUT
:
208 parse_lease_time(cfile
, &config
->select_interval
);
211 parse_lease_time(cfile
, &config
->reboot_timeout
);
213 case TOK_BACKOFF_CUTOFF
:
214 parse_lease_time(cfile
, &config
->backoff_cutoff
);
216 case TOK_INITIAL_INTERVAL
:
217 parse_lease_time(cfile
, &config
->initial_interval
);
220 config
->script_name
= parse_string(cfile
);
223 parse_interface_declaration(cfile
);
226 parse_client_lease_statement(cfile
, 1);
232 parse_reject_statement(cfile
);
235 parse_warn("expecting a statement.");
239 token
= next_token(NULL
, cfile
);
241 parse_warn("semicolon expected.");
247 parse_X(FILE *cfile
, u_int8_t
*buf
, int max
)
253 token
= peek_token(&val
, cfile
);
254 if (token
== TOK_NUMBER_OR_NAME
|| token
== TOK_NUMBER
) {
257 token
= next_token(&val
, cfile
);
258 if (token
!= TOK_NUMBER
&& token
!= TOK_NUMBER_OR_NAME
) {
259 parse_warn("expecting hexadecimal constant.");
263 convert_num(&buf
[len
], val
, 16, 8);
265 parse_warn("hexadecimal constant too long.");
269 token
= peek_token(&val
, cfile
);
271 token
= next_token(&val
, cfile
);
272 } while (token
== ':');
274 } else if (token
== TOK_STRING
) {
275 token
= next_token(&val
, cfile
);
278 parse_warn("string constant too long.");
282 memcpy(buf
, val
, len
+ 1);
284 parse_warn("expecting string or hexadecimal data");
292 * option-list :== option_name |
293 * option_list COMMA option_name
296 parse_option_list(FILE *cfile
, u_int8_t
*list
)
304 token
= next_token(&val
, cfile
);
305 if (!is_identifier(token
)) {
306 parse_warn("expected option name.");
310 for (i
= 0; i
< 256; i
++)
311 if (!strcasecmp(dhcp_options
[i
].name
, val
))
315 parse_warn("%s: unexpected option name.", val
);
321 parse_warn("%s: too many options.", val
);
325 token
= next_token(&val
, cfile
);
326 } while (token
== ',');
328 parse_warn("expecting semicolon.");
336 * interface-declaration :==
337 * INTERFACE string LBRACE client-declarations RBRACE
340 parse_interface_declaration(FILE *cfile
)
345 token
= next_token(&val
, cfile
);
346 if (token
!= TOK_STRING
) {
347 parse_warn("expecting interface name (in quotes).");
352 if (strcmp(ifi
->name
, val
) != 0) {
357 token
= next_token(&val
, cfile
);
359 parse_warn("expecting left brace.");
365 token
= peek_token(&val
, cfile
);
367 parse_warn("unterminated interface declaration.");
372 parse_client_statement(cfile
);
374 token
= next_token(&val
, cfile
);
378 * client-lease-statement :==
379 * RBRACE client-lease-declarations LBRACE
381 * client-lease-declarations :==
383 * client-lease-declaration |
384 * client-lease-declarations client-lease-declaration
387 parse_client_lease_statement(FILE *cfile
, int is_static
)
389 struct client_lease
*lease
, *lp
, *pl
;
392 token
= next_token(NULL
, cfile
);
394 parse_warn("expecting left brace.");
399 lease
= malloc(sizeof(struct client_lease
));
401 error("no memory for lease.");
402 memset(lease
, 0, sizeof(*lease
));
403 lease
->is_static
= is_static
;
406 token
= peek_token(NULL
, cfile
);
408 parse_warn("unterminated lease declaration.");
413 parse_client_lease_declaration(cfile
, lease
);
415 token
= next_token(NULL
, cfile
);
417 /* If the lease declaration didn't include an interface
418 * declaration that we recognized, it's of no use to us.
421 free_client_lease(lease
);
426 * The new lease may supersede a lease that's not the active
427 * lease but is still on the lease list, so scan the lease list
428 * looking for a lease with the same address, and if we find it,
432 for (lp
= client
->leases
; lp
; lp
= lp
->next
) {
433 if (addr_eq(lp
->address
, lease
->address
)) {
437 client
->leases
= lp
->next
;
438 free_client_lease(lp
);
445 * If this is a preloaded lease, just put it on the list of
446 * recorded leases - don't make it the active lease.
449 lease
->next
= client
->leases
;
450 client
->leases
= lease
;
455 * The last lease in the lease file on a particular interface is
456 * the active lease for that interface. Of course, we don't
457 * know what the last lease in the file is until we've parsed
458 * the whole file, so at this point, we assume that the lease we
459 * just parsed is the active lease for its interface. If
460 * there's already an active lease for the interface, and this
461 * lease is for the same ip address, then we just toss the old
462 * active lease and replace it with this one. If this lease is
463 * for a different address, then if the old active lease has
464 * expired, we dump it; if not, we put it on the list of leases
465 * for this interface which are still valid but no longer
468 if (client
->active
) {
469 if (client
->active
->expiry
< cur_time
)
470 free_client_lease(client
->active
);
471 else if (addr_eq(client
->active
->address
, lease
->address
))
472 free_client_lease(client
->active
);
474 client
->active
->next
= client
->leases
;
475 client
->leases
= client
->active
;
478 client
->active
= lease
;
484 * client-lease-declaration :==
487 * FIXED_ADDR ip_address |
489 * SERVER_NAME string |
490 * OPTION option-decl |
496 parse_client_lease_declaration(FILE *cfile
, struct client_lease
*lease
)
501 switch (next_token(&val
, cfile
)) {
506 token
= next_token(&val
, cfile
);
507 if (token
!= TOK_STRING
) {
508 parse_warn("expecting interface name (in quotes).");
512 if (strcmp(ifi
->name
, val
) != 0) {
513 parse_warn("wrong interface name. Expecting '%s'.",
520 if (!parse_ip_addr(cfile
, &lease
->address
))
527 lease
->filename
= parse_string(cfile
);
529 case TOK_SERVER_NAME
:
530 lease
->server_name
= parse_string(cfile
);
533 lease
->renewal
= parse_date(cfile
);
536 lease
->rebind
= parse_date(cfile
);
539 lease
->expiry
= parse_date(cfile
);
542 parse_option_decl(cfile
, lease
->options
);
545 parse_warn("expecting lease declaration.");
549 token
= next_token(&val
, cfile
);
551 parse_warn("expecting semicolon.");
557 parse_option_decl(FILE *cfile
, struct option_data
*options
)
562 u_int8_t hunkbuf
[1024];
565 struct iaddr ip_addr
;
570 token
= next_token(&val
, cfile
);
571 if (!is_identifier(token
)) {
572 parse_warn("expecting identifier after option keyword.");
578 /* Look up the actual option info. */
580 for (code
= 0; code
< 256; code
++)
581 if (strcmp(dhcp_options
[code
].name
, val
) == 0)
585 parse_warn("no option named %s", val
);
590 /* Parse the option data... */
592 for (fmt
= dhcp_options
[code
].format
; *fmt
; fmt
++) {
597 len
= parse_X(cfile
, &hunkbuf
[hunkix
],
598 sizeof(hunkbuf
) - hunkix
);
601 case 't': /* Text string... */
602 token
= next_token(&val
, cfile
);
603 if (token
!= TOK_STRING
) {
604 parse_warn("expecting string.");
609 if (hunkix
+ len
+ 1 > sizeof(hunkbuf
)) {
610 parse_warn("option data buffer %s",
615 memcpy(&hunkbuf
[hunkix
], val
, len
+ 1);
619 case 'I': /* IP address. */
620 if (!parse_ip_addr(cfile
, &ip_addr
))
625 if (hunkix
+ len
> sizeof(hunkbuf
)) {
626 parse_warn("option data buffer "
631 memcpy(&hunkbuf
[hunkix
], dp
, len
);
634 case 'L': /* Unsigned 32-bit integer... */
635 case 'l': /* Signed 32-bit integer... */
636 token
= next_token(&val
, cfile
);
637 if (token
!= TOK_NUMBER
) {
639 parse_warn("expecting number.");
644 convert_num(buf
, val
, 0, 32);
648 case 's': /* Signed 16-bit integer. */
649 case 'S': /* Unsigned 16-bit integer. */
650 token
= next_token(&val
, cfile
);
651 if (token
!= TOK_NUMBER
)
653 convert_num(buf
, val
, 0, 16);
657 case 'b': /* Signed 8-bit integer. */
658 case 'B': /* Unsigned 8-bit integer. */
659 token
= next_token(&val
, cfile
);
660 if (token
!= TOK_NUMBER
)
662 convert_num(buf
, val
, 0, 8);
666 case 'f': /* Boolean flag. */
667 token
= next_token(&val
, cfile
);
668 if (!is_identifier(token
)) {
669 parse_warn("expecting identifier.");
675 if (!strcasecmp(val
, "true") ||
676 !strcasecmp(val
, "on"))
678 else if (!strcasecmp(val
, "false") ||
679 !strcasecmp(val
, "off"))
682 parse_warn("expecting boolean.");
689 warning("Bad format %c in parse_option_param.",
695 token
= next_token(&val
, cfile
);
696 } while (*fmt
== 'A' && token
== ',');
699 parse_warn("semicolon expected.");
704 options
[code
].data
= malloc(hunkix
+ nul_term
);
705 if (!options
[code
].data
)
706 error("out of memory allocating option data.");
707 memcpy(options
[code
].data
, hunkbuf
, hunkix
+ nul_term
);
708 options
[code
].len
= hunkix
;
713 parse_reject_statement(FILE *cfile
)
715 struct iaddrlist
*list
;
720 if (!parse_ip_addr(cfile
, &addr
)) {
721 parse_warn("expecting IP address.");
726 list
= malloc(sizeof(struct iaddrlist
));
728 error("no memory for reject list!");
731 list
->next
= config
->reject_list
;
732 config
->reject_list
= list
;
734 token
= next_token(NULL
, cfile
);
735 } while (token
== ',');
738 parse_warn("expecting semicolon.");