sbin/*hammer: Use typedef'd for struct zone_stat*
[dragonfly.git] / sbin / dhclient / clparse.c
blob8b676353677ac0cca19fbc913c29a78793d3bdce
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... */
5 /*
6 * Copyright (c) 1997 The Internet Software Consortium.
7 * All rights reserved.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
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
34 * SUCH DAMAGE.
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''.
43 #include "dhcpd.h"
44 #include "dhctoken.h"
47 * client-conf-file :== client-declarations EOF
48 * client-declarations :== <nil>
49 * | client-declaration
50 * | client-declarations client-declaration
52 int
53 read_client_conf(void)
55 FILE *cfile;
56 int token;
58 new_parse(path_dhclient_conf);
60 /* Set some defaults... */
61 config->link_timeout = 30;
62 config->timeout = 60;
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) {
86 do {
87 token = peek_token(NULL, cfile);
88 if (token == EOF)
89 break;
90 parse_client_statement(cfile);
91 } while (1);
92 token = next_token(NULL, cfile); /* Clear the peek buffer */
93 fclose(cfile);
96 return (!warnings_occurred);
100 * lease-file :== client-lease-statements EOF
101 * client-lease-statements :== <nil>
102 * | client-lease-statements LEASE client-lease-statement
104 void
105 read_client_leases(void)
107 FILE *cfile;
108 int token;
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)
115 return;
116 do {
117 token = next_token(NULL, cfile);
118 if (token == EOF)
119 break;
120 if (token != TOK_LEASE) {
121 warning("Corrupt lease file - possible data loss!");
122 skip_to_semi(cfile);
123 break;
124 } else
125 parse_client_lease_statement(cfile, 0);
127 } while (1);
128 fclose(cfile);
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 |
143 * TOK_RETRY 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
154 void
155 parse_client_statement(FILE *cfile)
157 u_int8_t ignorelist[256];
158 int token, code, count, i;
160 switch (next_token(NULL, cfile)) {
161 case TOK_SEND:
162 parse_option_decl(cfile, &config->send_options[0]);
163 return;
164 case TOK_DEFAULT:
165 code = parse_option_decl(cfile, &config->defaults[0]);
166 if (code != -1)
167 config->default_actions[code] = ACTION_DEFAULT;
168 return;
169 case TOK_SUPERSEDE:
170 code = parse_option_decl(cfile, &config->defaults[0]);
171 if (code != -1)
172 config->default_actions[code] = ACTION_SUPERSEDE;
173 return;
174 case TOK_IGNORE:
175 count = parse_option_list(cfile, ignorelist);
176 for (i = 0; i < count; i++)
177 config->default_actions[ignorelist[i]] = ACTION_IGNORE;
178 return;
179 case TOK_APPEND:
180 code = parse_option_decl(cfile, &config->defaults[0]);
181 if (code != -1)
182 config->default_actions[code] = ACTION_APPEND;
183 return;
184 case TOK_PREPEND:
185 code = parse_option_decl(cfile, &config->defaults[0]);
186 if (code != -1)
187 config->default_actions[code] = ACTION_PREPEND;
188 return;
189 case TOK_MEDIA:
190 skip_to_semi(cfile);
191 return;
192 case TOK_HARDWARE:
193 parse_hardware_param(cfile, &ifi->hw_address);
194 return;
195 case TOK_REQUEST:
196 config->requested_option_count =
197 parse_option_list(cfile, config->requested_options);
198 return;
199 case TOK_REQUIRE:
200 memset(config->required_options, 0,
201 sizeof(config->required_options));
202 parse_option_list(cfile, config->required_options);
203 return;
204 case TOK_LINK_TIMEOUT:
205 parse_lease_time(cfile, &config->link_timeout);
206 return;
207 case TOK_TIMEOUT:
208 parse_lease_time(cfile, &config->timeout);
209 return;
210 case TOK_RETRY:
211 parse_lease_time(cfile, &config->retry_interval);
212 return;
213 case TOK_SELECT_TIMEOUT:
214 parse_lease_time(cfile, &config->select_interval);
215 return;
216 case TOK_REBOOT:
217 parse_lease_time(cfile, &config->reboot_timeout);
218 return;
219 case TOK_BACKOFF_CUTOFF:
220 parse_lease_time(cfile, &config->backoff_cutoff);
221 return;
222 case TOK_INITIAL_INTERVAL:
223 parse_lease_time(cfile, &config->initial_interval);
224 return;
225 case TOK_SCRIPT:
226 config->script_name = parse_string(cfile);
227 return;
228 case TOK_INTERFACE:
229 parse_interface_declaration(cfile);
230 return;
231 case TOK_LEASE:
232 parse_client_lease_statement(cfile, 1);
233 return;
234 case TOK_ALIAS:
235 skip_to_semi(cfile);
236 return;
237 case TOK_REJECT:
238 parse_reject_statement(cfile);
239 return;
240 default:
241 parse_warn("expecting a statement.");
242 skip_to_semi(cfile);
243 break;
245 token = next_token(NULL, cfile);
246 if (token != ';') {
247 parse_warn("semicolon expected.");
248 skip_to_semi(cfile);
253 parse_X(FILE *cfile, u_int8_t *buf, int max)
255 int token;
256 char *val;
257 int len;
259 token = peek_token(&val, cfile);
260 if (token == TOK_NUMBER_OR_NAME || token == TOK_NUMBER) {
261 len = 0;
262 do {
263 token = next_token(&val, cfile);
264 if (token != TOK_NUMBER && token != TOK_NUMBER_OR_NAME) {
265 parse_warn("expecting hexadecimal constant.");
266 skip_to_semi(cfile);
267 return (0);
269 convert_num(&buf[len], val, 16, 8);
270 if (len++ > max) {
271 parse_warn("hexadecimal constant too long.");
272 skip_to_semi(cfile);
273 return (0);
275 token = peek_token(&val, cfile);
276 if (token == ':')
277 token = next_token(&val, cfile);
278 } while (token == ':');
279 val = (char *)buf;
280 } else if (token == TOK_STRING) {
281 token = next_token(&val, cfile);
282 len = strlen(val);
283 if (len + 1 > max) {
284 parse_warn("string constant too long.");
285 skip_to_semi(cfile);
286 return (0);
288 memcpy(buf, val, len + 1);
289 } else {
290 parse_warn("expecting string or hexadecimal data");
291 skip_to_semi(cfile);
292 return (0);
294 return (len);
298 * option-list :== option_name |
299 * option_list COMMA option_name
302 parse_option_list(FILE *cfile, u_int8_t *list)
304 int ix, i;
305 int token;
306 char *val;
308 ix = 0;
309 do {
310 token = next_token(&val, cfile);
311 if (!is_identifier(token)) {
312 parse_warn("expected option name.");
313 skip_to_semi(cfile);
314 return (0);
316 for (i = 0; i < 256; i++)
317 if (!strcasecmp(dhcp_options[i].name, val))
318 break;
320 if (i == 256) {
321 parse_warn("%s: unexpected option name.", val);
322 skip_to_semi(cfile);
323 return (0);
325 list[ix++] = i;
326 if (ix == 256) {
327 parse_warn("%s: too many options.", val);
328 skip_to_semi(cfile);
329 return (0);
331 token = next_token(&val, cfile);
332 } while (token == ',');
333 if (token != ';') {
334 parse_warn("expecting semicolon.");
335 skip_to_semi(cfile);
336 return (0);
338 return (ix);
342 * interface-declaration :==
343 * INTERFACE string LBRACE client-declarations RBRACE
345 void
346 parse_interface_declaration(FILE *cfile)
348 char *val;
349 int token;
351 token = next_token(&val, cfile);
352 if (token != TOK_STRING) {
353 parse_warn("expecting interface name (in quotes).");
354 skip_to_semi(cfile);
355 return;
358 if (strcmp(ifi->name, val) != 0) {
359 skip_to_semi(cfile);
360 return;
363 token = next_token(&val, cfile);
364 if (token != '{') {
365 parse_warn("expecting left brace.");
366 skip_to_semi(cfile);
367 return;
370 do {
371 token = peek_token(&val, cfile);
372 if (token == EOF) {
373 parse_warn("unterminated interface declaration.");
374 return;
376 if (token == '}')
377 break;
378 parse_client_statement(cfile);
379 } while (1);
380 token = next_token(&val, cfile);
384 * client-lease-statement :==
385 * RBRACE client-lease-declarations LBRACE
387 * client-lease-declarations :==
388 * <nil> |
389 * client-lease-declaration |
390 * client-lease-declarations client-lease-declaration
392 void
393 parse_client_lease_statement(FILE *cfile, int is_static)
395 struct client_lease *lease, *lp, *pl;
396 int token;
398 token = next_token(NULL, cfile);
399 if (token != '{') {
400 parse_warn("expecting left brace.");
401 skip_to_semi(cfile);
402 return;
405 lease = malloc(sizeof(struct client_lease));
406 if (!lease)
407 error("no memory for lease.");
408 memset(lease, 0, sizeof(*lease));
409 lease->is_static = is_static;
411 do {
412 token = peek_token(NULL, cfile);
413 if (token == EOF) {
414 parse_warn("unterminated lease declaration.");
415 return;
417 if (token == '}')
418 break;
419 parse_client_lease_declaration(cfile, lease);
420 } while (1);
421 token = next_token(NULL, cfile);
423 /* If the lease declaration didn't include an interface
424 * declaration that we recognized, it's of no use to us.
426 if (!ifi) {
427 free_client_lease(lease);
428 return;
432 * The new lease may supersede a lease that's not the active
433 * lease but is still on the lease list, so scan the lease list
434 * looking for a lease with the same address, and if we find it,
435 * toss it.
437 pl = NULL;
438 for (lp = client->leases; lp; lp = lp->next) {
439 if (addr_eq(lp->address, lease->address)) {
440 if (pl)
441 pl->next = lp->next;
442 else
443 client->leases = lp->next;
444 free_client_lease(lp);
445 break;
446 } else
447 pl = lp;
451 * If this is a preloaded lease, just put it on the list of
452 * recorded leases - don't make it the active lease.
454 if (is_static) {
455 lease->next = client->leases;
456 client->leases = lease;
457 return;
461 * The last lease in the lease file on a particular interface is
462 * the active lease for that interface. Of course, we don't
463 * know what the last lease in the file is until we've parsed
464 * the whole file, so at this point, we assume that the lease we
465 * just parsed is the active lease for its interface. If
466 * there's already an active lease for the interface, and this
467 * lease is for the same ip address, then we just toss the old
468 * active lease and replace it with this one. If this lease is
469 * for a different address, then if the old active lease has
470 * expired, we dump it; if not, we put it on the list of leases
471 * for this interface which are still valid but no longer
472 * active.
474 if (client->active) {
475 if (client->active->expiry < time(NULL))
476 free_client_lease(client->active);
477 else if (addr_eq(client->active->address, lease->address))
478 free_client_lease(client->active);
479 else {
480 client->active->next = client->leases;
481 client->leases = client->active;
484 client->active = lease;
486 /* Phew. */
490 * client-lease-declaration :==
491 * BOOTP |
492 * INTERFACE string |
493 * FIXED_ADDR ip_address |
494 * FILENAME string |
495 * SERVER_NAME string |
496 * OPTION option-decl |
497 * RENEW time-decl |
498 * REBIND time-decl |
499 * EXPIRE time-decl
501 void
502 parse_client_lease_declaration(FILE *cfile, struct client_lease *lease)
504 char *val;
505 int token;
507 switch (next_token(&val, cfile)) {
508 case TOK_BOOTP:
509 lease->is_bootp = 1;
510 break;
511 case TOK_INTERFACE:
512 token = next_token(&val, cfile);
513 if (token != TOK_STRING) {
514 parse_warn("expecting interface name (in quotes).");
515 skip_to_semi(cfile);
516 break;
518 if (strcmp(ifi->name, val) != 0) {
519 parse_warn("wrong interface name. Expecting '%s'.",
520 ifi->name);
521 skip_to_semi(cfile);
522 break;
524 break;
525 case TOK_FIXED_ADDR:
526 if (!parse_ip_addr(cfile, &lease->address))
527 return;
528 break;
529 case TOK_MEDIUM:
530 skip_to_semi(cfile);
531 return;
532 case TOK_FILENAME:
533 lease->filename = parse_string(cfile);
534 return;
535 case TOK_SERVER_NAME:
536 lease->server_name = parse_string(cfile);
537 return;
538 case TOK_RENEW:
539 lease->renewal = parse_date(cfile);
540 return;
541 case TOK_REBIND:
542 lease->rebind = parse_date(cfile);
543 return;
544 case TOK_EXPIRE:
545 lease->expiry = parse_date(cfile);
546 return;
547 case TOK_OPTION:
548 parse_option_decl(cfile, lease->options);
549 return;
550 default:
551 parse_warn("expecting lease declaration.");
552 skip_to_semi(cfile);
553 break;
555 token = next_token(&val, cfile);
556 if (token != ';') {
557 parse_warn("expecting semicolon.");
558 skip_to_semi(cfile);
563 parse_option_decl(FILE *cfile, struct option_data *options)
565 char *val;
566 int token;
567 u_int8_t buf[4];
568 u_int8_t hunkbuf[1024];
569 int hunkix = 0;
570 char *fmt;
571 struct iaddr ip_addr;
572 u_int8_t *dp;
573 int len, code;
574 int nul_term = 0;
576 token = next_token(&val, cfile);
577 if (!is_identifier(token)) {
578 parse_warn("expecting identifier after option keyword.");
579 if (token != ';')
580 skip_to_semi(cfile);
581 return (-1);
584 /* Look up the actual option info. */
585 fmt = NULL;
586 for (code = 0; code < 256; code++)
587 if (strcmp(dhcp_options[code].name, val) == 0)
588 break;
590 if (code > 255) {
591 parse_warn("no option named %s", val);
592 skip_to_semi(cfile);
593 return (-1);
596 /* Parse the option data... */
597 do {
598 for (fmt = dhcp_options[code].format; *fmt; fmt++) {
599 if (*fmt == 'A')
600 break;
601 switch (*fmt) {
602 case 'X':
603 len = parse_X(cfile, &hunkbuf[hunkix],
604 sizeof(hunkbuf) - hunkix);
605 hunkix += len;
606 break;
607 case 't': /* Text string... */
608 token = next_token(&val, cfile);
609 if (token != TOK_STRING) {
610 parse_warn("expecting string.");
611 skip_to_semi(cfile);
612 return (-1);
614 len = strlen(val);
615 if (hunkix + len + 1 > sizeof(hunkbuf)) {
616 parse_warn("option data buffer %s",
617 "overflow");
618 skip_to_semi(cfile);
619 return (-1);
621 memcpy(&hunkbuf[hunkix], val, len + 1);
622 nul_term = 1;
623 hunkix += len;
624 break;
625 case 'I': /* IP address. */
626 if (!parse_ip_addr(cfile, &ip_addr))
627 return (-1);
628 len = ip_addr.len;
629 dp = ip_addr.iabuf;
630 alloc:
631 if (hunkix + len > sizeof(hunkbuf)) {
632 parse_warn("option data buffer "
633 "overflow");
634 skip_to_semi(cfile);
635 return (-1);
637 memcpy(&hunkbuf[hunkix], dp, len);
638 hunkix += len;
639 break;
640 case 'L': /* Unsigned 32-bit integer... */
641 case 'l': /* Signed 32-bit integer... */
642 token = next_token(&val, cfile);
643 if (token != TOK_NUMBER) {
644 need_number:
645 parse_warn("expecting number.");
646 if (token != ';')
647 skip_to_semi(cfile);
648 return (-1);
650 convert_num(buf, val, 0, 32);
651 len = 4;
652 dp = buf;
653 goto alloc;
654 case 's': /* Signed 16-bit integer. */
655 case 'S': /* Unsigned 16-bit integer. */
656 token = next_token(&val, cfile);
657 if (token != TOK_NUMBER)
658 goto need_number;
659 convert_num(buf, val, 0, 16);
660 len = 2;
661 dp = buf;
662 goto alloc;
663 case 'b': /* Signed 8-bit integer. */
664 case 'B': /* Unsigned 8-bit integer. */
665 token = next_token(&val, cfile);
666 if (token != TOK_NUMBER)
667 goto need_number;
668 convert_num(buf, val, 0, 8);
669 len = 1;
670 dp = buf;
671 goto alloc;
672 case 'f': /* Boolean flag. */
673 token = next_token(&val, cfile);
674 if (!is_identifier(token)) {
675 parse_warn("expecting identifier.");
676 bad_flag:
677 if (token != ';')
678 skip_to_semi(cfile);
679 return (-1);
681 if (!strcasecmp(val, "true") ||
682 !strcasecmp(val, "on"))
683 buf[0] = 1;
684 else if (!strcasecmp(val, "false") ||
685 !strcasecmp(val, "off"))
686 buf[0] = 0;
687 else {
688 parse_warn("expecting boolean.");
689 goto bad_flag;
691 len = 1;
692 dp = buf;
693 goto alloc;
694 default:
695 warning("Bad format %c in parse_option_param.",
696 *fmt);
697 skip_to_semi(cfile);
698 return (-1);
701 token = next_token(&val, cfile);
702 } while (*fmt == 'A' && token == ',');
704 if (token != ';') {
705 parse_warn("semicolon expected.");
706 skip_to_semi(cfile);
707 return (-1);
710 options[code].data = malloc(hunkix + nul_term);
711 if (!options[code].data)
712 error("out of memory allocating option data.");
713 memcpy(options[code].data, hunkbuf, hunkix + nul_term);
714 options[code].len = hunkix;
715 return (code);
718 void
719 parse_reject_statement(FILE *cfile)
721 struct iaddrlist *list;
722 struct iaddr addr;
723 int token;
725 do {
726 if (!parse_ip_addr(cfile, &addr)) {
727 parse_warn("expecting IP address.");
728 skip_to_semi(cfile);
729 return;
732 list = malloc(sizeof(struct iaddrlist));
733 if (!list)
734 error("no memory for reject list!");
736 list->addr = addr;
737 list->next = config->reject_list;
738 config->reject_list = list;
740 token = next_token(NULL, cfile);
741 } while (token == ',');
743 if (token != ';') {
744 parse_warn("expecting semicolon.");
745 skip_to_semi(cfile);