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