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