kernel - Fix (unused) SEGEX_IDX macro
[dragonfly.git] / sbin / dhclient / clparse.c
blob119634969e58c55aa731d68735f7c927300f225b
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... */
6 /*
7 * Copyright (c) 1997 The Internet Software Consortium.
8 * All rights reserved.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
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
35 * SUCH DAMAGE.
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''.
44 #include "dhcpd.h"
45 #include "dhctoken.h"
48 * client-conf-file :== client-declarations EOF
49 * client-declarations :== <nil>
50 * | client-declaration
51 * | client-declarations client-declaration
53 int
54 read_client_conf(void)
56 FILE *cfile;
57 char *val;
58 int token;
60 new_parse(path_dhclient_conf);
62 /* Set some defaults... */
63 config->link_timeout = 10;
64 config->timeout = 60;
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) {
88 do {
89 token = peek_token(&val, cfile);
90 if (token == EOF)
91 break;
92 parse_client_statement(cfile);
93 } while (1);
94 token = next_token(&val, cfile); /* Clear the peek buffer */
95 fclose(cfile);
98 return (!warnings_occurred);
102 * lease-file :== client-lease-statements EOF
103 * client-lease-statements :== <nil>
104 * | client-lease-statements LEASE client-lease-statement
106 void
107 read_client_leases(void)
109 FILE *cfile;
110 char *val;
111 int token;
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)
118 return;
119 do {
120 token = next_token(&val, cfile);
121 if (token == EOF)
122 break;
123 if (token != TOK_LEASE) {
124 warning("Corrupt lease file - possible data loss!");
125 skip_to_semi(cfile);
126 break;
127 } else
128 parse_client_lease_statement(cfile, 0);
130 } while (1);
131 fclose(cfile);
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 |
146 * TOK_RETRY 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
157 void
158 parse_client_statement(FILE *cfile)
160 char *val;
161 int token, code;
163 switch (next_token(&val, cfile)) {
164 case TOK_SEND:
165 parse_option_decl(cfile, &config->send_options[0]);
166 return;
167 case TOK_DEFAULT:
168 code = parse_option_decl(cfile, &config->defaults[0]);
169 if (code != -1)
170 config->default_actions[code] = ACTION_DEFAULT;
171 return;
172 case TOK_SUPERSEDE:
173 code = parse_option_decl(cfile, &config->defaults[0]);
174 if (code != -1)
175 config->default_actions[code] = ACTION_SUPERSEDE;
176 return;
177 case TOK_APPEND:
178 code = parse_option_decl(cfile, &config->defaults[0]);
179 if (code != -1)
180 config->default_actions[code] = ACTION_APPEND;
181 return;
182 case TOK_PREPEND:
183 code = parse_option_decl(cfile, &config->defaults[0]);
184 if (code != -1)
185 config->default_actions[code] = ACTION_PREPEND;
186 return;
187 case TOK_MEDIA:
188 parse_string_list(cfile, &config->media, 1);
189 return;
190 case TOK_HARDWARE:
191 parse_hardware_param(cfile, &ifi->hw_address);
192 return;
193 case TOK_REQUEST:
194 config->requested_option_count =
195 parse_option_list(cfile, config->requested_options);
196 return;
197 case TOK_REQUIRE:
198 memset(config->required_options, 0,
199 sizeof(config->required_options));
200 parse_option_list(cfile, config->required_options);
201 return;
202 case TOK_LINK_TIMEOUT:
203 parse_lease_time(cfile, &config->link_timeout);
204 return;
205 case TOK_TIMEOUT:
206 parse_lease_time(cfile, &config->timeout);
207 return;
208 case TOK_RETRY:
209 parse_lease_time(cfile, &config->retry_interval);
210 return;
211 case TOK_SELECT_TIMEOUT:
212 parse_lease_time(cfile, &config->select_interval);
213 return;
214 case TOK_REBOOT:
215 parse_lease_time(cfile, &config->reboot_timeout);
216 return;
217 case TOK_BACKOFF_CUTOFF:
218 parse_lease_time(cfile, &config->backoff_cutoff);
219 return;
220 case TOK_INITIAL_INTERVAL:
221 parse_lease_time(cfile, &config->initial_interval);
222 return;
223 case TOK_SCRIPT:
224 config->script_name = parse_string(cfile);
225 return;
226 case TOK_INTERFACE:
227 parse_interface_declaration(cfile);
228 return;
229 case TOK_LEASE:
230 parse_client_lease_statement(cfile, 1);
231 return;
232 case TOK_ALIAS:
233 parse_client_lease_statement(cfile, 2);
234 return;
235 case TOK_REJECT:
236 parse_reject_statement(cfile);
237 return;
238 default:
239 parse_warn("expecting a statement.");
240 skip_to_semi(cfile);
241 break;
243 token = next_token(&val, cfile);
244 if (token != ';') {
245 parse_warn("semicolon expected.");
246 skip_to_semi(cfile);
251 parse_X(FILE *cfile, u_int8_t *buf, int max)
253 int token;
254 char *val;
255 int len;
257 token = peek_token(&val, cfile);
258 if (token == TOK_NUMBER_OR_NAME || token == TOK_NUMBER) {
259 len = 0;
260 do {
261 token = next_token(&val, cfile);
262 if (token != TOK_NUMBER && token != TOK_NUMBER_OR_NAME) {
263 parse_warn("expecting hexadecimal constant.");
264 skip_to_semi(cfile);
265 return (0);
267 convert_num(&buf[len], val, 16, 8);
268 if (len++ > max) {
269 parse_warn("hexadecimal constant too long.");
270 skip_to_semi(cfile);
271 return (0);
273 token = peek_token(&val, cfile);
274 if (token == ':')
275 token = next_token(&val, cfile);
276 } while (token == ':');
277 val = (char *)buf;
278 } else if (token == TOK_STRING) {
279 token = next_token(&val, cfile);
280 len = strlen(val);
281 if (len + 1 > max) {
282 parse_warn("string constant too long.");
283 skip_to_semi(cfile);
284 return (0);
286 memcpy(buf, val, len + 1);
287 } else {
288 parse_warn("expecting string or hexadecimal data");
289 skip_to_semi(cfile);
290 return (0);
292 return (len);
296 * option-list :== option_name |
297 * option_list COMMA option_name
300 parse_option_list(FILE *cfile, u_int8_t *list)
302 int ix, i;
303 int token;
304 char *val;
306 ix = 0;
307 do {
308 token = next_token(&val, cfile);
309 if (!is_identifier(token)) {
310 parse_warn("expected option name.");
311 skip_to_semi(cfile);
312 return (0);
314 for (i = 0; i < 256; i++)
315 if (!strcasecmp(dhcp_options[i].name, val))
316 break;
318 if (i == 256) {
319 parse_warn("%s: unexpected option name.", val);
320 skip_to_semi(cfile);
321 return (0);
323 list[ix++] = i;
324 if (ix == 256) {
325 parse_warn("%s: too many options.", val);
326 skip_to_semi(cfile);
327 return (0);
329 token = next_token(&val, cfile);
330 } while (token == ',');
331 if (token != ';') {
332 parse_warn("expecting semicolon.");
333 skip_to_semi(cfile);
334 return (0);
336 return (ix);
340 * interface-declaration :==
341 * INTERFACE string LBRACE client-declarations RBRACE
343 void
344 parse_interface_declaration(FILE *cfile)
346 char *val;
347 int token;
349 token = next_token(&val, cfile);
350 if (token != TOK_STRING) {
351 parse_warn("expecting interface name (in quotes).");
352 skip_to_semi(cfile);
353 return;
356 if (strcmp(ifi->name, val) != 0) {
357 skip_to_semi(cfile);
358 return;
361 token = next_token(&val, cfile);
362 if (token != '{') {
363 parse_warn("expecting left brace.");
364 skip_to_semi(cfile);
365 return;
368 do {
369 token = peek_token(&val, cfile);
370 if (token == EOF) {
371 parse_warn("unterminated interface declaration.");
372 return;
374 if (token == '}')
375 break;
376 parse_client_statement(cfile);
377 } while (1);
378 token = next_token(&val, cfile);
382 * client-lease-statement :==
383 * RBRACE client-lease-declarations LBRACE
385 * client-lease-declarations :==
386 * <nil> |
387 * client-lease-declaration |
388 * client-lease-declarations client-lease-declaration
390 void
391 parse_client_lease_statement(FILE *cfile, int is_static)
393 struct client_lease *lease, *lp, *pl;
394 int token;
395 char *val;
397 token = next_token(&val, cfile);
398 if (token != '{') {
399 parse_warn("expecting left brace.");
400 skip_to_semi(cfile);
401 return;
404 lease = malloc(sizeof(struct client_lease));
405 if (!lease)
406 error("no memory for lease.");
407 memset(lease, 0, sizeof(*lease));
408 lease->is_static = is_static;
410 do {
411 token = peek_token(&val, cfile);
412 if (token == EOF) {
413 parse_warn("unterminated lease declaration.");
414 return;
416 if (token == '}')
417 break;
418 parse_client_lease_declaration(cfile, lease);
419 } while (1);
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.
425 if (!ifi) {
426 free_client_lease(lease);
427 return;
430 /* If this is an alias lease, it doesn't need to be sorted in. */
431 if (is_static == 2) {
432 client->alias = lease;
433 return;
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,
440 * toss it.
442 pl = NULL;
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)) {
447 if (pl)
448 pl->next = lp->next;
449 else
450 client->leases = lp->next;
451 free_client_lease(lp);
452 break;
457 * If this is a preloaded lease, just put it on the list of
458 * recorded leases - don't make it the active lease.
460 if (is_static) {
461 lease->next = client->leases;
462 client->leases = lease;
463 return;
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
478 * active.
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);
488 else {
489 client->active->next = client->leases;
490 client->leases = client->active;
493 client->active = lease;
495 /* Phew. */
499 * client-lease-declaration :==
500 * BOOTP |
501 * INTERFACE string |
502 * FIXED_ADDR ip_address |
503 * FILENAME string |
504 * SERVER_NAME string |
505 * OPTION option-decl |
506 * RENEW time-decl |
507 * REBIND time-decl |
508 * EXPIRE time-decl
510 void
511 parse_client_lease_declaration(FILE *cfile, struct client_lease *lease)
513 char *val;
514 int token;
516 switch (next_token(&val, cfile)) {
517 case TOK_BOOTP:
518 lease->is_bootp = 1;
519 break;
520 case TOK_INTERFACE:
521 token = next_token(&val, cfile);
522 if (token != TOK_STRING) {
523 parse_warn("expecting interface name (in quotes).");
524 skip_to_semi(cfile);
525 break;
527 if (strcmp(ifi->name, val) != 0) {
528 parse_warn("wrong interface name. Expecting '%s'.",
529 ifi->name);
530 skip_to_semi(cfile);
531 break;
533 break;
534 case TOK_FIXED_ADDR:
535 if (!parse_ip_addr(cfile, &lease->address))
536 return;
537 break;
538 case TOK_MEDIUM:
539 parse_string_list(cfile, &lease->medium, 0);
540 return;
541 case TOK_FILENAME:
542 lease->filename = parse_string(cfile);
543 return;
544 case TOK_SERVER_NAME:
545 lease->server_name = parse_string(cfile);
546 return;
547 case TOK_RENEW:
548 lease->renewal = parse_date(cfile);
549 return;
550 case TOK_REBIND:
551 lease->rebind = parse_date(cfile);
552 return;
553 case TOK_EXPIRE:
554 lease->expiry = parse_date(cfile);
555 return;
556 case TOK_OPTION:
557 parse_option_decl(cfile, lease->options);
558 return;
559 default:
560 parse_warn("expecting lease declaration.");
561 skip_to_semi(cfile);
562 break;
564 token = next_token(&val, cfile);
565 if (token != ';') {
566 parse_warn("expecting semicolon.");
567 skip_to_semi(cfile);
572 parse_option_decl(FILE *cfile, struct option_data *options)
574 char *val;
575 int token;
576 u_int8_t buf[4];
577 u_int8_t hunkbuf[1024];
578 int hunkix = 0;
579 char *fmt;
580 struct iaddr ip_addr;
581 u_int8_t *dp;
582 int len, code;
583 int nul_term = 0;
585 token = next_token(&val, cfile);
586 if (!is_identifier(token)) {
587 parse_warn("expecting identifier after option keyword.");
588 if (token != ';')
589 skip_to_semi(cfile);
590 return (-1);
593 /* Look up the actual option info. */
594 fmt = NULL;
595 for (code = 0; code < 256; code++)
596 if (strcmp(dhcp_options[code].name, val) == 0)
597 break;
599 if (code > 255) {
600 parse_warn("no option named %s", val);
601 skip_to_semi(cfile);
602 return (-1);
605 /* Parse the option data... */
606 do {
607 for (fmt = dhcp_options[code].format; *fmt; fmt++) {
608 if (*fmt == 'A')
609 break;
610 switch (*fmt) {
611 case 'X':
612 len = parse_X(cfile, &hunkbuf[hunkix],
613 sizeof(hunkbuf) - hunkix);
614 hunkix += len;
615 break;
616 case 't': /* Text string... */
617 token = next_token(&val, cfile);
618 if (token != TOK_STRING) {
619 parse_warn("expecting string.");
620 skip_to_semi(cfile);
621 return (-1);
623 len = strlen(val);
624 if (hunkix + len + 1 > sizeof(hunkbuf)) {
625 parse_warn("option data buffer %s",
626 "overflow");
627 skip_to_semi(cfile);
628 return (-1);
630 memcpy(&hunkbuf[hunkix], val, len + 1);
631 nul_term = 1;
632 hunkix += len;
633 break;
634 case 'I': /* IP address. */
635 if (!parse_ip_addr(cfile, &ip_addr))
636 return (-1);
637 len = ip_addr.len;
638 dp = ip_addr.iabuf;
639 alloc:
640 if (hunkix + len > sizeof(hunkbuf)) {
641 parse_warn("option data buffer "
642 "overflow");
643 skip_to_semi(cfile);
644 return (-1);
646 memcpy(&hunkbuf[hunkix], dp, len);
647 hunkix += len;
648 break;
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) {
653 need_number:
654 parse_warn("expecting number.");
655 if (token != ';')
656 skip_to_semi(cfile);
657 return (-1);
659 convert_num(buf, val, 0, 32);
660 len = 4;
661 dp = buf;
662 goto alloc;
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)
667 goto need_number;
668 convert_num(buf, val, 0, 16);
669 len = 2;
670 dp = buf;
671 goto alloc;
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)
676 goto need_number;
677 convert_num(buf, val, 0, 8);
678 len = 1;
679 dp = buf;
680 goto alloc;
681 case 'f': /* Boolean flag. */
682 token = next_token(&val, cfile);
683 if (!is_identifier(token)) {
684 parse_warn("expecting identifier.");
685 bad_flag:
686 if (token != ';')
687 skip_to_semi(cfile);
688 return (-1);
690 if (!strcasecmp(val, "true") ||
691 !strcasecmp(val, "on"))
692 buf[0] = 1;
693 else if (!strcasecmp(val, "false") ||
694 !strcasecmp(val, "off"))
695 buf[0] = 0;
696 else {
697 parse_warn("expecting boolean.");
698 goto bad_flag;
700 len = 1;
701 dp = buf;
702 goto alloc;
703 default:
704 warning("Bad format %c in parse_option_param.",
705 *fmt);
706 skip_to_semi(cfile);
707 return (-1);
710 token = next_token(&val, cfile);
711 } while (*fmt == 'A' && token == ',');
713 if (token != ';') {
714 parse_warn("semicolon expected.");
715 skip_to_semi(cfile);
716 return (-1);
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;
724 return (code);
727 void
728 parse_string_list(FILE *cfile, struct string_list **lp, int multiple)
730 int token;
731 char *val;
732 struct string_list *cur, *tmp;
734 /* Find the last medium in the media list. */
735 if (*lp)
736 for (cur = *lp; cur->next; cur = cur->next)
737 ; /* nothing */
738 else
739 cur = NULL;
741 do {
742 token = next_token(&val, cfile);
743 if (token != TOK_STRING) {
744 parse_warn("Expecting media options.");
745 skip_to_semi(cfile);
746 return;
749 tmp = malloc(sizeof(struct string_list) + strlen(val));
750 if (tmp == NULL)
751 error("no memory for string list entry.");
752 strlcpy(tmp->string, val, strlen(val) + 1);
753 tmp->next = NULL;
755 /* Store this medium at the end of the media list. */
756 if (cur)
757 cur->next = tmp;
758 else
759 *lp = tmp;
760 cur = tmp;
762 token = next_token(&val, cfile);
763 } while (multiple && token == ',');
765 if (token != ';') {
766 parse_warn("expecting semicolon.");
767 skip_to_semi(cfile);
771 void
772 parse_reject_statement(FILE *cfile)
774 struct iaddrlist *list;
775 struct iaddr addr;
776 char *val;
777 int token;
779 do {
780 if (!parse_ip_addr(cfile, &addr)) {
781 parse_warn("expecting IP address.");
782 skip_to_semi(cfile);
783 return;
786 list = malloc(sizeof(struct iaddrlist));
787 if (!list)
788 error("no memory for reject list!");
790 list->addr = addr;
791 list->next = config->reject_list;
792 config->reject_list = list;
794 token = next_token(&val, cfile);
795 } while (token == ',');
797 if (token != ';') {
798 parse_warn("expecting semicolon.");
799 skip_to_semi(cfile);