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