1 /* $OpenBSD: src/sbin/dhclient/clparse.c,v 1.41 2012/10/27 23:08:53 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
= 30;
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_CLASSLESS_ROUTES
;
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(NULL
, cfile
);
92 parse_client_statement(cfile
);
94 token
= next_token(NULL
, 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)
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(NULL
, 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
)
159 u_int8_t ignorelist
[256];
160 int token
, code
, count
, i
;
162 switch (next_token(NULL
, 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 count
= parse_option_list(cfile
, ignorelist
);
178 for (i
= 0; i
< count
; i
++)
179 config
->default_actions
[ignorelist
[i
]] = ACTION_IGNORE
;
182 code
= parse_option_decl(cfile
, &config
->defaults
[0]);
184 config
->default_actions
[code
] = ACTION_APPEND
;
187 code
= parse_option_decl(cfile
, &config
->defaults
[0]);
189 config
->default_actions
[code
] = ACTION_PREPEND
;
195 parse_hardware_param(cfile
, &ifi
->hw_address
);
198 config
->requested_option_count
=
199 parse_option_list(cfile
, config
->requested_options
);
202 memset(config
->required_options
, 0,
203 sizeof(config
->required_options
));
204 parse_option_list(cfile
, config
->required_options
);
206 case TOK_LINK_TIMEOUT
:
207 parse_lease_time(cfile
, &config
->link_timeout
);
210 parse_lease_time(cfile
, &config
->timeout
);
213 parse_lease_time(cfile
, &config
->retry_interval
);
215 case TOK_SELECT_TIMEOUT
:
216 parse_lease_time(cfile
, &config
->select_interval
);
219 parse_lease_time(cfile
, &config
->reboot_timeout
);
221 case TOK_BACKOFF_CUTOFF
:
222 parse_lease_time(cfile
, &config
->backoff_cutoff
);
224 case TOK_INITIAL_INTERVAL
:
225 parse_lease_time(cfile
, &config
->initial_interval
);
228 config
->script_name
= parse_string(cfile
);
231 parse_interface_declaration(cfile
);
234 parse_client_lease_statement(cfile
, 1);
240 parse_reject_statement(cfile
);
243 parse_warn("expecting a statement.");
247 token
= next_token(NULL
, cfile
);
249 parse_warn("semicolon expected.");
255 parse_X(FILE *cfile
, u_int8_t
*buf
, int max
)
261 token
= peek_token(&val
, cfile
);
262 if (token
== TOK_NUMBER_OR_NAME
|| token
== TOK_NUMBER
) {
265 token
= next_token(&val
, cfile
);
266 if (token
!= TOK_NUMBER
&& token
!= TOK_NUMBER_OR_NAME
) {
267 parse_warn("expecting hexadecimal constant.");
271 convert_num(&buf
[len
], val
, 16, 8);
273 parse_warn("hexadecimal constant too long.");
277 token
= peek_token(&val
, cfile
);
279 token
= next_token(&val
, cfile
);
280 } while (token
== ':');
282 } else if (token
== TOK_STRING
) {
283 token
= next_token(&val
, cfile
);
286 parse_warn("string constant too long.");
290 memcpy(buf
, val
, len
+ 1);
292 parse_warn("expecting string or hexadecimal data");
300 * option-list :== option_name |
301 * option_list COMMA option_name
304 parse_option_list(FILE *cfile
, u_int8_t
*list
)
312 token
= next_token(&val
, cfile
);
313 if (!is_identifier(token
)) {
314 parse_warn("expected option name.");
318 for (i
= 0; i
< 256; i
++)
319 if (!strcasecmp(dhcp_options
[i
].name
, val
))
323 parse_warn("%s: unexpected option name.", val
);
329 parse_warn("%s: too many options.", val
);
333 token
= next_token(&val
, cfile
);
334 } while (token
== ',');
336 parse_warn("expecting semicolon.");
344 * interface-declaration :==
345 * INTERFACE string LBRACE client-declarations RBRACE
348 parse_interface_declaration(FILE *cfile
)
353 token
= next_token(&val
, cfile
);
354 if (token
!= TOK_STRING
) {
355 parse_warn("expecting interface name (in quotes).");
360 if (strcmp(ifi
->name
, val
) != 0) {
365 token
= next_token(&val
, cfile
);
367 parse_warn("expecting left brace.");
373 token
= peek_token(&val
, cfile
);
375 parse_warn("unterminated interface declaration.");
380 parse_client_statement(cfile
);
382 token
= next_token(&val
, cfile
);
386 * client-lease-statement :==
387 * RBRACE client-lease-declarations LBRACE
389 * client-lease-declarations :==
391 * client-lease-declaration |
392 * client-lease-declarations client-lease-declaration
395 parse_client_lease_statement(FILE *cfile
, int is_static
)
397 struct client_lease
*lease
, *lp
, *pl
;
400 token
= next_token(NULL
, cfile
);
402 parse_warn("expecting left brace.");
407 lease
= malloc(sizeof(struct client_lease
));
409 error("no memory for lease.");
410 memset(lease
, 0, sizeof(*lease
));
411 lease
->is_static
= is_static
;
414 token
= peek_token(NULL
, cfile
);
416 parse_warn("unterminated lease declaration.");
421 parse_client_lease_declaration(cfile
, lease
);
423 token
= next_token(NULL
, cfile
);
425 /* If the lease declaration didn't include an interface
426 * declaration that we recognized, it's of no use to us.
429 free_client_lease(lease
);
434 * The new lease may supersede a lease that's not the active
435 * lease but is still on the lease list, so scan the lease list
436 * looking for a lease with the same address, and if we find it,
440 for (lp
= client
->leases
; lp
; lp
= lp
->next
) {
441 if (addr_eq(lp
->address
, lease
->address
)) {
445 client
->leases
= lp
->next
;
446 free_client_lease(lp
);
453 * If this is a preloaded lease, just put it on the list of
454 * recorded leases - don't make it the active lease.
457 lease
->next
= client
->leases
;
458 client
->leases
= lease
;
463 * The last lease in the lease file on a particular interface is
464 * the active lease for that interface. Of course, we don't
465 * know what the last lease in the file is until we've parsed
466 * the whole file, so at this point, we assume that the lease we
467 * just parsed is the active lease for its interface. If
468 * there's already an active lease for the interface, and this
469 * lease is for the same ip address, then we just toss the old
470 * active lease and replace it with this one. If this lease is
471 * for a different address, then if the old active lease has
472 * expired, we dump it; if not, we put it on the list of leases
473 * for this interface which are still valid but no longer
476 if (client
->active
) {
477 if (client
->active
->expiry
< time(NULL
))
478 free_client_lease(client
->active
);
479 else if (addr_eq(client
->active
->address
, lease
->address
))
480 free_client_lease(client
->active
);
482 client
->active
->next
= client
->leases
;
483 client
->leases
= client
->active
;
486 client
->active
= lease
;
492 * client-lease-declaration :==
495 * FIXED_ADDR ip_address |
497 * SERVER_NAME string |
498 * OPTION option-decl |
504 parse_client_lease_declaration(FILE *cfile
, struct client_lease
*lease
)
509 switch (next_token(&val
, cfile
)) {
514 token
= next_token(&val
, cfile
);
515 if (token
!= TOK_STRING
) {
516 parse_warn("expecting interface name (in quotes).");
520 if (strcmp(ifi
->name
, val
) != 0) {
521 parse_warn("wrong interface name. Expecting '%s'.",
528 if (!parse_ip_addr(cfile
, &lease
->address
))
535 lease
->filename
= parse_string(cfile
);
537 case TOK_SERVER_NAME
:
538 lease
->server_name
= parse_string(cfile
);
541 lease
->renewal
= parse_date(cfile
);
544 lease
->rebind
= parse_date(cfile
);
547 lease
->expiry
= parse_date(cfile
);
550 parse_option_decl(cfile
, lease
->options
);
553 parse_warn("expecting lease declaration.");
557 token
= next_token(&val
, cfile
);
559 parse_warn("expecting semicolon.");
565 parse_option_decl(FILE *cfile
, struct option_data
*options
)
570 u_int8_t hunkbuf
[1024];
573 struct iaddr ip_addr
;
578 token
= next_token(&val
, cfile
);
579 if (!is_identifier(token
)) {
580 parse_warn("expecting identifier after option keyword.");
586 /* Look up the actual option info. */
588 for (code
= 0; code
< 256; code
++)
589 if (strcmp(dhcp_options
[code
].name
, val
) == 0)
593 parse_warn("no option named %s", val
);
598 /* Parse the option data... */
600 for (fmt
= dhcp_options
[code
].format
; *fmt
; fmt
++) {
605 len
= parse_X(cfile
, &hunkbuf
[hunkix
],
606 sizeof(hunkbuf
) - hunkix
);
609 case 't': /* Text string... */
610 token
= next_token(&val
, cfile
);
611 if (token
!= TOK_STRING
) {
612 parse_warn("expecting string.");
617 if (hunkix
+ len
+ 1 > sizeof(hunkbuf
)) {
618 parse_warn("option data buffer %s",
623 memcpy(&hunkbuf
[hunkix
], val
, len
+ 1);
627 case 'I': /* IP address. */
628 if (!parse_ip_addr(cfile
, &ip_addr
))
633 if (hunkix
+ len
> sizeof(hunkbuf
)) {
634 parse_warn("option data buffer "
639 memcpy(&hunkbuf
[hunkix
], dp
, len
);
642 case 'L': /* Unsigned 32-bit integer... */
643 case 'l': /* Signed 32-bit integer... */
644 token
= next_token(&val
, cfile
);
645 if (token
!= TOK_NUMBER
) {
647 parse_warn("expecting number.");
652 convert_num(buf
, val
, 0, 32);
656 case 's': /* Signed 16-bit integer. */
657 case 'S': /* Unsigned 16-bit integer. */
658 token
= next_token(&val
, cfile
);
659 if (token
!= TOK_NUMBER
)
661 convert_num(buf
, val
, 0, 16);
665 case 'b': /* Signed 8-bit integer. */
666 case 'B': /* Unsigned 8-bit integer. */
667 token
= next_token(&val
, cfile
);
668 if (token
!= TOK_NUMBER
)
670 convert_num(buf
, val
, 0, 8);
674 case 'f': /* Boolean flag. */
675 token
= next_token(&val
, cfile
);
676 if (!is_identifier(token
)) {
677 parse_warn("expecting identifier.");
683 if (!strcasecmp(val
, "true") ||
684 !strcasecmp(val
, "on"))
686 else if (!strcasecmp(val
, "false") ||
687 !strcasecmp(val
, "off"))
690 parse_warn("expecting boolean.");
697 warning("Bad format %c in parse_option_param.",
703 token
= next_token(&val
, cfile
);
704 } while (*fmt
== 'A' && token
== ',');
707 parse_warn("semicolon expected.");
712 options
[code
].data
= malloc(hunkix
+ nul_term
);
713 if (!options
[code
].data
)
714 error("out of memory allocating option data.");
715 memcpy(options
[code
].data
, hunkbuf
, hunkix
+ nul_term
);
716 options
[code
].len
= hunkix
;
721 parse_reject_statement(FILE *cfile
)
723 struct iaddrlist
*list
;
728 if (!parse_ip_addr(cfile
, &addr
)) {
729 parse_warn("expecting IP address.");
734 list
= malloc(sizeof(struct iaddrlist
));
736 error("no memory for reject list!");
739 list
->next
= config
->reject_list
;
740 config
->reject_list
= list
;
742 token
= next_token(NULL
, cfile
);
743 } while (token
== ',');
746 parse_warn("expecting semicolon.");