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