1 /* $OpenBSD: clparse.c,v 1.18 2004/09/15 18:15:18 henning Exp $ */
2
3 /* Parser for dhclient config and lease files... */
4
5 /*-
6 * SPDX-License-Identifier: BSD-3-Clause
7 *
8 * Copyright (c) 1997 The Internet Software Consortium.
9 * All rights reserved.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 *
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. Neither the name of The Internet Software Consortium nor the names
21 * of its contributors may be used to endorse or promote products derived
22 * from this software without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
25 * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
26 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
27 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
28 * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
29 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
32 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
33 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
34 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
35 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * SUCH DAMAGE.
37 *
38 * This software has been written for the Internet Software Consortium
39 * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
40 * Enterprises. To learn more about the Internet Software Consortium,
41 * see ``http://www.vix.com/isc''. To learn more about Vixie
42 * Enterprises, see ``http://www.vix.com''.
43 */
44
45 #include <sys/cdefs.h>
46 #include "dhcpd.h"
47 #include "dhctoken.h"
48
49 struct client_config top_level_config;
50 static struct interface_info *dummy_interfaces;
51
52 static char client_script_name[] = "/sbin/dhclient-script";
53
54 /*
55 * Options the user ignores must not be asked for either.
56 * This matters for the IPv6-Only Preferred option.
57 */
58 static void
unrequest_ignored_options(struct client_config * config)59 unrequest_ignored_options(struct client_config *config)
60 {
61 bool ignored[256] = {};
62 int i, n;
63
64 for (i = 0; config->ignored_options[i] != 0; i++)
65 ignored[config->ignored_options[i]] = true;
66
67 for (i = n = 0; i < config->requested_option_count; i++) {
68 if (ignored[config->requested_options[i]])
69 continue;
70 config->requested_options[n++] = config->requested_options[i];
71 }
72 config->requested_option_count = n;
73 }
74
75 /*
76 * client-conf-file :== client-declarations EOF
77 * client-declarations :== <nil>
78 * | client-declaration
79 * | client-declarations client-declaration
80 */
81 int
read_client_conf(void)82 read_client_conf(void)
83 {
84 FILE *cfile;
85 char *val;
86 int token;
87 struct client_config *config;
88
89 new_parse(path_dhclient_conf);
90
91 /* Set up the initial dhcp option universe. */
92 initialize_universes();
93
94 /* Initialize the top level client configuration. */
95 memset(&top_level_config, 0, sizeof(top_level_config));
96
97 /* Set some defaults... */
98 top_level_config.vlan_pcp = 0;
99 top_level_config.timeout = 60;
100 top_level_config.select_interval = 0;
101 top_level_config.reboot_timeout = 10;
102 top_level_config.retry_interval = 300;
103 top_level_config.backoff_cutoff = 15;
104 top_level_config.initial_interval = 3;
105 top_level_config.bootp_policy = ACCEPT;
106 top_level_config.script_name = client_script_name;
107 top_level_config.requested_options
108 [top_level_config.requested_option_count++] = DHO_SUBNET_MASK;
109 top_level_config.requested_options
110 [top_level_config.requested_option_count++] = DHO_BROADCAST_ADDRESS;
111 top_level_config.requested_options
112 [top_level_config.requested_option_count++] = DHO_TIME_OFFSET;
113 top_level_config.requested_options
114 [top_level_config.requested_option_count++] = DHO_CLASSLESS_ROUTES;
115 top_level_config.requested_options
116 [top_level_config.requested_option_count++] = DHO_ROUTERS;
117 top_level_config.requested_options
118 [top_level_config.requested_option_count++] = DHO_DOMAIN_NAME;
119 top_level_config.requested_options
120 [top_level_config.requested_option_count++] =
121 DHO_DOMAIN_NAME_SERVERS;
122 top_level_config.requested_options
123 [top_level_config.requested_option_count++] = DHO_HOST_NAME;
124 top_level_config.requested_options
125 [top_level_config.requested_option_count++] = DHO_DOMAIN_SEARCH;
126 top_level_config.requested_options
127 [top_level_config.requested_option_count++] = DHO_INTERFACE_MTU;
128 #ifdef INET6
129 /*
130 * RFC 8925 sec 3.2: The DHCPv4 client on an IPv4-requiring host MUST
131 * NOT include the IPv6-Only Preferred option code in the Parameter
132 * Request List.
133 */
134 top_level_config.requested_options
135 [top_level_config.requested_option_count++] = DHO_IPV6_ONLY;
136 #endif
137
138 if ((cfile = fopen(path_dhclient_conf, "r")) != NULL) {
139 do {
140 token = peek_token(&val, cfile);
141 if (token == EOF)
142 break;
143 parse_client_statement(cfile, NULL, &top_level_config);
144 } while (1);
145 token = next_token(&val, cfile); /* Clear the peek buffer */
146 fclose(cfile);
147 }
148
149 /*
150 * Set up state and config structures for clients that don't
151 * have per-interface configuration declarations.
152 */
153 config = NULL;
154 if (!ifi->client) {
155 ifi->client = malloc(sizeof(struct client_state));
156 if (!ifi->client)
157 error("no memory for client state.");
158 memset(ifi->client, 0, sizeof(*(ifi->client)));
159 }
160 if (!ifi->client->config) {
161 if (!config) {
162 config = malloc(sizeof(struct client_config));
163 if (!config)
164 error("no memory for client config.");
165 memcpy(config, &top_level_config,
166 sizeof(top_level_config));
167 }
168 ifi->client->config = config;
169 }
170 unrequest_ignored_options(ifi->client->config);
171
172 return (!warnings_occurred);
173 }
174
175 /*
176 * lease-file :== client-lease-statements EOF
177 * client-lease-statements :== <nil>
178 * | client-lease-statements LEASE client-lease-statement
179 */
180 void
read_client_leases(void)181 read_client_leases(void)
182 {
183 FILE *cfile;
184 char *val;
185 int token;
186
187 new_parse(path_dhclient_db);
188
189 /* Open the lease file. If we can't open it, just return -
190 we can safely trust the server to remember our state. */
191 if ((cfile = fopen(path_dhclient_db, "r")) == NULL)
192 return;
193 do {
194 token = next_token(&val, cfile);
195 if (token == EOF)
196 break;
197 if (token != LEASE) {
198 warning("Corrupt lease file - possible data loss!");
199 skip_to_semi(cfile);
200 break;
201 } else
202 parse_client_lease_statement(cfile, 0);
203
204 } while (1);
205 fclose(cfile);
206 }
207
208 /*
209 * client-declaration :==
210 * SEND option-decl |
211 * DEFAULT option-decl |
212 * SUPERSEDE option-decl |
213 * PREPEND option-decl |
214 * APPEND option-decl |
215 * hardware-declaration |
216 * REQUEST option-list |
217 * REQUIRE option-list |
218 * IGNORE option-list |
219 * TIMEOUT number |
220 * RETRY number |
221 * REBOOT number |
222 * SELECT_TIMEOUT number |
223 * SCRIPT string |
224 * interface-declaration |
225 * LEASE client-lease-statement |
226 * ALIAS client-lease-statement
227 */
228 void
parse_client_statement(FILE * cfile,struct interface_info * ip,struct client_config * config)229 parse_client_statement(FILE *cfile, struct interface_info *ip,
230 struct client_config *config)
231 {
232 char *val;
233 struct option *option;
234 time_t tmp;
235
236 switch (next_token(&val, cfile)) {
237 case SEND:
238 parse_option_decl(cfile, &config->send_options[0]);
239 return;
240 case DEFAULT:
241 option = parse_option_decl(cfile, &config->defaults[0]);
242 if (option)
243 config->default_actions[option->code] = ACTION_DEFAULT;
244 return;
245 case SUPERSEDE:
246 option = parse_option_decl(cfile, &config->defaults[0]);
247 if (option)
248 config->default_actions[option->code] =
249 ACTION_SUPERSEDE;
250 return;
251 case APPEND:
252 option = parse_option_decl(cfile, &config->defaults[0]);
253 if (option)
254 config->default_actions[option->code] = ACTION_APPEND;
255 return;
256 case PREPEND:
257 option = parse_option_decl(cfile, &config->defaults[0]);
258 if (option)
259 config->default_actions[option->code] = ACTION_PREPEND;
260 return;
261 case MEDIA:
262 parse_string_list(cfile, &config->media, 1);
263 return;
264 case HARDWARE:
265 if (ip)
266 parse_hardware_param(cfile, &ip->hw_address);
267 else {
268 parse_warn("hardware address parameter %s",
269 "not allowed here.");
270 skip_to_semi(cfile);
271 }
272 return;
273 case REQUEST:
274 config->requested_option_count =
275 parse_option_list(cfile, config->requested_options);
276 return;
277 case REQUIRE:
278 memset(config->required_options, 0,
279 sizeof(config->required_options));
280 parse_option_list(cfile, config->required_options);
281 return;
282 case IGNORE:
283 parse_option_list(cfile, config->ignored_options);
284 return;
285 case TIMEOUT:
286 parse_lease_time(cfile, &config->timeout);
287 return;
288 case RETRY:
289 parse_lease_time(cfile, &config->retry_interval);
290 return;
291 case SELECT_TIMEOUT:
292 parse_lease_time(cfile, &config->select_interval);
293 return;
294 case REBOOT:
295 parse_lease_time(cfile, &config->reboot_timeout);
296 return;
297 case VLAN_PCP:
298 parse_lease_time(cfile, &tmp);
299 config->vlan_pcp = (u_int)tmp;
300 return;
301 case BACKOFF_CUTOFF:
302 parse_lease_time(cfile, &config->backoff_cutoff);
303 return;
304 case INITIAL_INTERVAL:
305 parse_lease_time(cfile, &config->initial_interval);
306 return;
307 case SCRIPT:
308 config->script_name = parse_string(cfile);
309 return;
310 case INTERFACE:
311 if (ip)
312 parse_warn("nested interface declaration.");
313 parse_interface_declaration(cfile, config);
314 return;
315 case LEASE:
316 parse_client_lease_statement(cfile, 1);
317 return;
318 case ALIAS:
319 parse_client_lease_statement(cfile, 2);
320 return;
321 case REJECT:
322 parse_reject_statement(cfile, config);
323 return;
324 default:
325 break;
326 }
327
328 parse_warn("expecting a statement.");
329 skip_to_semi(cfile);
330 }
331
332 unsigned
parse_X(FILE * cfile,u_int8_t * buf,unsigned max)333 parse_X(FILE *cfile, u_int8_t *buf, unsigned max)
334 {
335 int token;
336 char *val;
337 unsigned len;
338
339 token = peek_token(&val, cfile);
340 if (token == NUMBER_OR_NAME || token == NUMBER) {
341 len = 0;
342 do {
343 token = next_token(&val, cfile);
344 if (token != NUMBER && token != NUMBER_OR_NAME) {
345 parse_warn("expecting hexadecimal constant.");
346 skip_to_semi(cfile);
347 return (0);
348 }
349 convert_num(&buf[len], val, 16, 8);
350 if (len++ > max) {
351 parse_warn("hexadecimal constant too long.");
352 skip_to_semi(cfile);
353 return (0);
354 }
355 token = peek_token(&val, cfile);
356 if (token == COLON)
357 token = next_token(&val, cfile);
358 } while (token == COLON);
359 val = (char *)buf;
360 } else if (token == STRING) {
361 token = next_token(&val, cfile);
362 len = strlen(val);
363 if (len + 1 > max) {
364 parse_warn("string constant too long.");
365 skip_to_semi(cfile);
366 return (0);
367 }
368 memcpy(buf, val, len + 1);
369 } else {
370 parse_warn("expecting string or hexadecimal data");
371 skip_to_semi(cfile);
372 return (0);
373 }
374 return (len);
375 }
376
377 /*
378 * option-list :== option_name |
379 * option_list COMMA option_name
380 */
381 int
parse_option_list(FILE * cfile,u_int8_t * list)382 parse_option_list(FILE *cfile, u_int8_t *list)
383 {
384 int ix, i;
385 int token;
386 char *val;
387
388 ix = 0;
389 do {
390 token = next_token(&val, cfile);
391 if (!is_identifier(token)) {
392 parse_warn("expected option name.");
393 skip_to_semi(cfile);
394 return (0);
395 }
396 for (i = 0; i < 256; i++)
397 if (!strcasecmp(dhcp_options[i].name, val))
398 break;
399
400 if (i == 256) {
401 parse_warn("%s: unexpected option name.", val);
402 skip_to_semi(cfile);
403 return (0);
404 }
405 list[ix++] = i;
406 if (ix == 256) {
407 parse_warn("%s: too many options.", val);
408 skip_to_semi(cfile);
409 return (0);
410 }
411 token = next_token(&val, cfile);
412 } while (token == COMMA);
413 if (token != SEMI) {
414 parse_warn("expecting semicolon.");
415 skip_to_semi(cfile);
416 return (0);
417 }
418 return (ix);
419 }
420
421 /*
422 * interface-declaration :==
423 * INTERFACE string LBRACE client-declarations RBRACE
424 */
425 void
parse_interface_declaration(FILE * cfile,struct client_config * outer_config)426 parse_interface_declaration(FILE *cfile, struct client_config *outer_config)
427 {
428 int token;
429 char *val;
430 struct interface_info *ip;
431
432 token = next_token(&val, cfile);
433 if (token != STRING) {
434 parse_warn("expecting interface name (in quotes).");
435 skip_to_semi(cfile);
436 return;
437 }
438
439 ip = interface_or_dummy(val);
440
441 if (!ip->client)
442 make_client_state(ip);
443
444 if (!ip->client->config)
445 make_client_config(ip, outer_config);
446
447 token = next_token(&val, cfile);
448 if (token != LBRACE) {
449 parse_warn("expecting left brace.");
450 skip_to_semi(cfile);
451 return;
452 }
453
454 do {
455 token = peek_token(&val, cfile);
456 if (token == EOF) {
457 parse_warn("unterminated interface declaration.");
458 return;
459 }
460 if (token == RBRACE)
461 break;
462 parse_client_statement(cfile, ip, ip->client->config);
463 } while (1);
464 token = next_token(&val, cfile);
465 }
466
467 struct interface_info *
interface_or_dummy(char * name)468 interface_or_dummy(char *name)
469 {
470 struct interface_info *ip;
471
472 /* Find the interface (if any) that matches the name. */
473 if (!strcmp(ifi->name, name))
474 return (ifi);
475
476 /* If it's not a real interface, see if it's on the dummy list. */
477 for (ip = dummy_interfaces; ip; ip = ip->next)
478 if (!strcmp(ip->name, name))
479 return (ip);
480
481 /*
482 * If we didn't find an interface, make a dummy interface as a
483 * placeholder.
484 */
485 ip = malloc(sizeof(*ip));
486 if (!ip)
487 error("Insufficient memory to record interface %s", name);
488 memset(ip, 0, sizeof(*ip));
489 strlcpy(ip->name, name, IFNAMSIZ);
490 ip->next = dummy_interfaces;
491 dummy_interfaces = ip;
492 return (ip);
493 }
494
495 void
make_client_state(struct interface_info * ip)496 make_client_state(struct interface_info *ip)
497 {
498 ip->client = malloc(sizeof(*(ip->client)));
499 if (!ip->client)
500 error("no memory for state on %s", ip->name);
501 memset(ip->client, 0, sizeof(*(ip->client)));
502 }
503
504 void
make_client_config(struct interface_info * ip,struct client_config * config)505 make_client_config(struct interface_info *ip, struct client_config *config)
506 {
507 ip->client->config = malloc(sizeof(struct client_config));
508 if (!ip->client->config)
509 error("no memory for config for %s", ip->name);
510 memset(ip->client->config, 0, sizeof(*(ip->client->config)));
511 memcpy(ip->client->config, config, sizeof(*config));
512 }
513
514 /*
515 * client-lease-statement :==
516 * RBRACE client-lease-declarations LBRACE
517 *
518 * client-lease-declarations :==
519 * <nil> |
520 * client-lease-declaration |
521 * client-lease-declarations client-lease-declaration
522 */
523 void
parse_client_lease_statement(FILE * cfile,int is_static)524 parse_client_lease_statement(FILE *cfile, int is_static)
525 {
526 struct client_lease *lease, *lp, *pl;
527 struct interface_info *ip;
528 int token;
529 char *val;
530
531 token = next_token(&val, cfile);
532 if (token != LBRACE) {
533 parse_warn("expecting left brace.");
534 skip_to_semi(cfile);
535 return;
536 }
537
538 lease = malloc(sizeof(struct client_lease));
539 if (!lease)
540 error("no memory for lease.");
541 memset(lease, 0, sizeof(*lease));
542 lease->is_static = is_static;
543
544 ip = NULL;
545
546 do {
547 token = peek_token(&val, cfile);
548 if (token == EOF) {
549 parse_warn("unterminated lease declaration.");
550 free_client_lease(lease);
551 return;
552 }
553 if (token == RBRACE)
554 break;
555 parse_client_lease_declaration(cfile, lease, &ip);
556 } while (1);
557 token = next_token(&val, cfile);
558
559 /* If the lease declaration didn't include an interface
560 * declaration that we recognized, it's of no use to us.
561 */
562 if (!ip) {
563 free_client_lease(lease);
564 return;
565 }
566
567 /* Make sure there's a client state structure... */
568 if (!ip->client)
569 make_client_state(ip);
570
571 /* If this is an alias lease, it doesn't need to be sorted in. */
572 if (is_static == 2) {
573 ip->client->alias = lease;
574 return;
575 }
576
577 /*
578 * The new lease may supersede a lease that's not the active
579 * lease but is still on the lease list, so scan the lease list
580 * looking for a lease with the same address, and if we find it,
581 * toss it.
582 */
583 pl = NULL;
584 for (lp = ip->client->leases; lp; lp = lp->next) {
585 if (lp->address.len == lease->address.len &&
586 !memcmp(lp->address.iabuf, lease->address.iabuf,
587 lease->address.len)) {
588 if (pl)
589 pl->next = lp->next;
590 else
591 ip->client->leases = lp->next;
592 free_client_lease(lp);
593 break;
594 }
595 }
596
597 /*
598 * If this is a preloaded lease, just put it on the list of
599 * recorded leases - don't make it the active lease.
600 */
601 if (is_static) {
602 lease->next = ip->client->leases;
603 ip->client->leases = lease;
604 return;
605 }
606
607 /*
608 * The last lease in the lease file on a particular interface is
609 * the active lease for that interface. Of course, we don't
610 * know what the last lease in the file is until we've parsed
611 * the whole file, so at this point, we assume that the lease we
612 * just parsed is the active lease for its interface. If
613 * there's already an active lease for the interface, and this
614 * lease is for the same ip address, then we just toss the old
615 * active lease and replace it with this one. If this lease is
616 * for a different address, then if the old active lease has
617 * expired, we dump it; if not, we put it on the list of leases
618 * for this interface which are still valid but no longer
619 * active.
620 */
621 if (ip->client->active) {
622 if (ip->client->active->expiry < cur_time)
623 free_client_lease(ip->client->active);
624 else if (ip->client->active->address.len ==
625 lease->address.len &&
626 !memcmp(ip->client->active->address.iabuf,
627 lease->address.iabuf, lease->address.len))
628 free_client_lease(ip->client->active);
629 else {
630 ip->client->active->next = ip->client->leases;
631 ip->client->leases = ip->client->active;
632 }
633 }
634 ip->client->active = lease;
635
636 /* Phew. */
637 }
638
639 /*
640 * client-lease-declaration :==
641 * BOOTP |
642 * INTERFACE string |
643 * FIXED_ADDR ip_address |
644 * FILENAME string |
645 * SERVER_NAME string |
646 * OPTION option-decl |
647 * RENEW time-decl |
648 * REBIND time-decl |
649 * EXPIRE time-decl
650 */
651 void
parse_client_lease_declaration(FILE * cfile,struct client_lease * lease,struct interface_info ** ipp)652 parse_client_lease_declaration(FILE *cfile, struct client_lease *lease,
653 struct interface_info **ipp)
654 {
655 int token;
656 char *val;
657 struct interface_info *ip;
658
659 switch (next_token(&val, cfile)) {
660 case BOOTP:
661 lease->is_bootp = 1;
662 break;
663 case INTERFACE:
664 token = next_token(&val, cfile);
665 if (token != STRING) {
666 parse_warn("expecting interface name (in quotes).");
667 skip_to_semi(cfile);
668 return;
669 }
670 ip = interface_or_dummy(val);
671 *ipp = ip;
672 break;
673 case FIXED_ADDR:
674 if (!parse_ip_addr(cfile, &lease->address))
675 return;
676 break;
677 case MEDIUM:
678 parse_string_list(cfile, &lease->medium, 0);
679 return;
680 case FILENAME:
681 lease->filename = parse_string(cfile);
682 return;
683 case NEXT_SERVER:
684 if (!parse_ip_addr(cfile, &lease->nextserver))
685 return;
686 break;
687 case SERVER_NAME:
688 lease->server_name = parse_string(cfile);
689 return;
690 case RENEW:
691 lease->renewal = parse_date(cfile);
692 return;
693 case REBIND:
694 lease->rebind = parse_date(cfile);
695 return;
696 case EXPIRE:
697 lease->expiry = parse_date(cfile);
698 return;
699 case OPTION:
700 parse_option_decl(cfile, lease->options);
701 return;
702 default:
703 parse_warn("expecting lease declaration.");
704 skip_to_semi(cfile);
705 return;
706 }
707 token = next_token(&val, cfile);
708 if (token != SEMI) {
709 parse_warn("expecting semicolon.");
710 skip_to_semi(cfile);
711 }
712 }
713
714 struct option *
parse_option_decl(FILE * cfile,struct option_data * options)715 parse_option_decl(FILE *cfile, struct option_data *options)
716 {
717 char *val;
718 int token;
719 u_int8_t buf[4];
720 u_int8_t hunkbuf[1024];
721 unsigned hunkix = 0;
722 char *vendor;
723 const char *fmt;
724 struct universe *universe;
725 struct option *option;
726 struct iaddr ip_addr;
727 u_int8_t *dp;
728 unsigned len;
729 int nul_term = 0;
730
731 token = next_token(&val, cfile);
732 if (!is_identifier(token)) {
733 parse_warn("expecting identifier after option keyword.");
734 if (token != SEMI)
735 skip_to_semi(cfile);
736 return (NULL);
737 }
738 if ((vendor = strdup(val)) == NULL)
739 error("no memory for vendor information.");
740
741 token = peek_token(&val, cfile);
742 if (token == DOT) {
743 /* Go ahead and take the DOT token... */
744 token = next_token(&val, cfile);
745
746 /* The next token should be an identifier... */
747 token = next_token(&val, cfile);
748 if (!is_identifier(token)) {
749 parse_warn("expecting identifier after '.'");
750 if (token != SEMI)
751 skip_to_semi(cfile);
752 free(vendor);
753 return (NULL);
754 }
755
756 /* Look up the option name hash table for the specified
757 vendor. */
758 universe = ((struct universe *)hash_lookup(&universe_hash,
759 (unsigned char *)vendor, 0));
760 /* If it's not there, we can't parse the rest of the
761 declaration. */
762 if (!universe) {
763 parse_warn("no vendor named %s.", vendor);
764 skip_to_semi(cfile);
765 free(vendor);
766 return (NULL);
767 }
768 } else {
769 /* Use the default hash table, which contains all the
770 standard dhcp option names. */
771 val = vendor;
772 universe = &dhcp_universe;
773 }
774
775 /* Look up the actual option info... */
776 option = (struct option *)hash_lookup(universe->hash,
777 (unsigned char *)val, 0);
778
779 /* If we didn't get an option structure, it's an undefined option. */
780 if (!option) {
781 if (val == vendor)
782 parse_warn("no option named %s", val);
783 else
784 parse_warn("no option named %s for vendor %s",
785 val, vendor);
786 skip_to_semi(cfile);
787 free(vendor);
788 return (NULL);
789 }
790
791 /* Free the initial identifier token. */
792 free(vendor);
793
794 /* Parse the option data... */
795 do {
796 for (fmt = option->format; *fmt; fmt++) {
797 if (*fmt == 'A')
798 break;
799 switch (*fmt) {
800 case 'X':
801 len = parse_X(cfile, &hunkbuf[hunkix],
802 sizeof(hunkbuf) - hunkix);
803 hunkix += len;
804 break;
805 case 't': /* Text string... */
806 token = next_token(&val, cfile);
807 if (token != STRING) {
808 parse_warn("expecting string.");
809 skip_to_semi(cfile);
810 return (NULL);
811 }
812 len = strlen(val);
813 if (hunkix + len + 1 > sizeof(hunkbuf)) {
814 parse_warn("option data buffer %s",
815 "overflow");
816 skip_to_semi(cfile);
817 return (NULL);
818 }
819 memcpy(&hunkbuf[hunkix], val, len + 1);
820 nul_term = 1;
821 hunkix += len;
822 break;
823 case 'I': /* IP address. */
824 if (!parse_ip_addr(cfile, &ip_addr))
825 return (NULL);
826 len = ip_addr.len;
827 dp = ip_addr.iabuf;
828 alloc:
829 if (hunkix + len > sizeof(hunkbuf)) {
830 parse_warn("option data buffer "
831 "overflow");
832 skip_to_semi(cfile);
833 return (NULL);
834 }
835 memcpy(&hunkbuf[hunkix], dp, len);
836 hunkix += len;
837 break;
838 case 'L': /* Unsigned 32-bit integer... */
839 case 'l': /* Signed 32-bit integer... */
840 token = next_token(&val, cfile);
841 if (token != NUMBER) {
842 need_number:
843 parse_warn("expecting number.");
844 if (token != SEMI)
845 skip_to_semi(cfile);
846 return (NULL);
847 }
848 convert_num(buf, val, 0, 32);
849 len = 4;
850 dp = buf;
851 goto alloc;
852 case 's': /* Signed 16-bit integer. */
853 case 'S': /* Unsigned 16-bit integer. */
854 token = next_token(&val, cfile);
855 if (token != NUMBER)
856 goto need_number;
857 convert_num(buf, val, 0, 16);
858 len = 2;
859 dp = buf;
860 goto alloc;
861 case 'b': /* Signed 8-bit integer. */
862 case 'B': /* Unsigned 8-bit integer. */
863 token = next_token(&val, cfile);
864 if (token != NUMBER)
865 goto need_number;
866 convert_num(buf, val, 0, 8);
867 len = 1;
868 dp = buf;
869 goto alloc;
870 case 'f': /* Boolean flag. */
871 token = next_token(&val, cfile);
872 if (!is_identifier(token)) {
873 parse_warn("expecting identifier.");
874 bad_flag:
875 if (token != SEMI)
876 skip_to_semi(cfile);
877 return (NULL);
878 }
879 if (!strcasecmp(val, "true") ||
880 !strcasecmp(val, "on"))
881 buf[0] = 1;
882 else if (!strcasecmp(val, "false") ||
883 !strcasecmp(val, "off"))
884 buf[0] = 0;
885 else {
886 parse_warn("expecting boolean.");
887 goto bad_flag;
888 }
889 len = 1;
890 dp = buf;
891 goto alloc;
892 default:
893 warning("Bad format %c in parse_option_param.",
894 *fmt);
895 skip_to_semi(cfile);
896 return (NULL);
897 }
898 }
899 token = next_token(&val, cfile);
900 } while (*fmt == 'A' && token == COMMA);
901
902 if (token != SEMI) {
903 parse_warn("semicolon expected.");
904 skip_to_semi(cfile);
905 return (NULL);
906 }
907
908 options[option->code].data = malloc(hunkix + nul_term);
909 if (!options[option->code].data)
910 error("out of memory allocating option data.");
911 memcpy(options[option->code].data, hunkbuf, hunkix + nul_term);
912 options[option->code].len = hunkix;
913 return (option);
914 }
915
916 void
parse_string_list(FILE * cfile,struct string_list ** lp,int multiple)917 parse_string_list(FILE *cfile, struct string_list **lp, int multiple)
918 {
919 int token;
920 char *val;
921 size_t valsize;
922 struct string_list *cur, *tmp;
923
924 /* Find the last medium in the media list. */
925 if (*lp)
926 for (cur = *lp; cur->next; cur = cur->next)
927 ; /* nothing */
928 else
929 cur = NULL;
930
931 do {
932 token = next_token(&val, cfile);
933 if (token != STRING) {
934 parse_warn("Expecting media options.");
935 skip_to_semi(cfile);
936 return;
937 }
938
939 valsize = strlen(val) + 1;
940 tmp = new_string_list(valsize);
941 if (tmp == NULL)
942 error("no memory for string list entry.");
943 memcpy(tmp->string, val, valsize);
944 tmp->next = NULL;
945
946 /* Store this medium at the end of the media list. */
947 if (cur)
948 cur->next = tmp;
949 else
950 *lp = tmp;
951 cur = tmp;
952
953 token = next_token(&val, cfile);
954 } while (multiple && token == COMMA);
955
956 if (token != SEMI) {
957 parse_warn("expecting semicolon.");
958 skip_to_semi(cfile);
959 }
960 }
961
962 void
parse_reject_statement(FILE * cfile,struct client_config * config)963 parse_reject_statement(FILE *cfile, struct client_config *config)
964 {
965 int token;
966 char *val;
967 struct iaddr addr;
968 struct iaddrlist *list;
969
970 do {
971 if (!parse_ip_addr(cfile, &addr)) {
972 parse_warn("expecting IP address.");
973 skip_to_semi(cfile);
974 return;
975 }
976
977 list = malloc(sizeof(struct iaddrlist));
978 if (!list)
979 error("no memory for reject list!");
980
981 list->addr = addr;
982 list->next = config->reject_list;
983 config->reject_list = list;
984
985 token = next_token(&val, cfile);
986 } while (token == COMMA);
987
988 if (token != SEMI) {
989 parse_warn("expecting semicolon.");
990 skip_to_semi(cfile);
991 }
992 }
993