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