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