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