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