89c44bccc0c5f9fbd8778711fd4efcfbcfd36cd3
[dragonfly.git] / sbin / dhclient / clparse.c
1 /*      $OpenBSD: src/sbin/dhclient/clparse.c,v 1.37 2011/04/04 11:14:52 krw 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 "dhcpd.h"
44 #include "dhctoken.h"
45
46 /*
47  * client-conf-file :== client-declarations EOF
48  * client-declarations :== <nil>
49  *                       | client-declaration
50  *                       | client-declarations client-declaration
51  */
52 int
53 read_client_conf(void)
54 {
55         FILE *cfile;
56         char *val;
57         int token;
58
59         new_parse(path_dhclient_conf);
60
61         /* Set some defaults... */
62         config->link_timeout = 10;
63         config->timeout = 60;
64         config->select_interval = 0;
65         config->reboot_timeout = 10;
66         config->retry_interval = 300;
67         config->backoff_cutoff = 15;
68         config->initial_interval = 3;
69         config->bootp_policy = ACCEPT;
70         config->script_name = _PATH_DHCLIENT_SCRIPT;
71         config->requested_options
72             [config->requested_option_count++] = DHO_SUBNET_MASK;
73         config->requested_options
74             [config->requested_option_count++] = DHO_BROADCAST_ADDRESS;
75         config->requested_options
76             [config->requested_option_count++] = DHO_TIME_OFFSET;
77         config->requested_options
78             [config->requested_option_count++] = DHO_ROUTERS;
79         config->requested_options
80             [config->requested_option_count++] = DHO_DOMAIN_NAME;
81         config->requested_options
82             [config->requested_option_count++] = DHO_DOMAIN_NAME_SERVERS;
83         config->requested_options
84             [config->requested_option_count++] = DHO_HOST_NAME;
85
86         if ((cfile = fopen(path_dhclient_conf, "r")) != NULL) {
87                 do {
88                         token = peek_token(&val, cfile);
89                         if (token == EOF)
90                                 break;
91                         parse_client_statement(cfile);
92                 } while (1);
93                 token = next_token(&val, cfile); /* Clear the peek buffer */
94                 fclose(cfile);
95         }
96
97         return (!warnings_occurred);
98 }
99
100 /*
101  * lease-file :== client-lease-statements EOF
102  * client-lease-statements :== <nil>
103  *                   | client-lease-statements LEASE client-lease-statement
104  */
105 void
106 read_client_leases(void)
107 {
108         FILE    *cfile;
109         char    *val;
110         int      token;
111
112         new_parse(path_dhclient_db);
113
114         /* Open the lease file.   If we can't open it, just return -
115            we can safely trust the server to remember our state. */
116         if ((cfile = fopen(path_dhclient_db, "r")) == NULL)
117                 return;
118         do {
119                 token = next_token(&val, cfile);
120                 if (token == EOF)
121                         break;
122                 if (token != TOK_LEASE) {
123                         warning("Corrupt lease file - possible data loss!");
124                         skip_to_semi(cfile);
125                         break;
126                 } else
127                         parse_client_lease_statement(cfile, 0);
128
129         } while (1);
130         fclose(cfile);
131 }
132
133 /*
134  * client-declaration :==
135  *      TOK_SEND option-decl |
136  *      TOK_DEFAULT option-decl |
137  *      TOK_SUPERSEDE option-decl |
138  *      TOK_APPEND option-decl |
139  *      TOK_PREPEND option-decl |
140  *      TOK_MEDIA string-list |
141  *      hardware-declaration |
142  *      TOK_REQUEST option-list |
143  *      TOK_REQUIRE option-list |
144  *      TOK_TIMEOUT number |
145  *      TOK_RETRY number |
146  *      TOK_SELECT_TIMEOUT number |
147  *      TOK_REBOOT number |
148  *      TOK_BACKOFF_CUTOFF number |
149  *      TOK_INITIAL_INTERVAL number |
150  *      TOK_SCRIPT string |
151  *      interface-declaration |
152  *      TOK_LEASE client-lease-statement |
153  *      TOK_ALIAS client-lease-statement |
154  *      TOK_REJECT reject-statement
155  */
156 void
157 parse_client_statement(FILE *cfile)
158 {
159         char *val;
160         int token, code;
161
162         switch (next_token(&val, cfile)) {
163         case TOK_SEND:
164                 parse_option_decl(cfile, &config->send_options[0]);
165                 return;
166         case TOK_DEFAULT:
167                 code = parse_option_decl(cfile, &config->defaults[0]);
168                 if (code != -1)
169                         config->default_actions[code] = ACTION_DEFAULT;
170                 return;
171         case TOK_SUPERSEDE:
172                 code = parse_option_decl(cfile, &config->defaults[0]);
173                 if (code != -1)
174                         config->default_actions[code] = ACTION_SUPERSEDE;
175                 return;
176         case TOK_APPEND:
177                 code = parse_option_decl(cfile, &config->defaults[0]);
178                 if (code != -1)
179                         config->default_actions[code] = ACTION_APPEND;
180                 return;
181         case TOK_PREPEND:
182                 code = parse_option_decl(cfile, &config->defaults[0]);
183                 if (code != -1)
184                         config->default_actions[code] = ACTION_PREPEND;
185                 return;
186         case TOK_MEDIA:
187                 skip_to_semi(cfile);
188                 return;
189         case TOK_HARDWARE:
190                 parse_hardware_param(cfile, &ifi->hw_address);
191                 return;
192         case TOK_REQUEST:
193                 config->requested_option_count =
194                         parse_option_list(cfile, config->requested_options);
195                 return;
196         case TOK_REQUIRE:
197                 memset(config->required_options, 0,
198                     sizeof(config->required_options));
199                 parse_option_list(cfile, config->required_options);
200                 return;
201         case TOK_LINK_TIMEOUT:
202                 parse_lease_time(cfile, &config->link_timeout);
203                 return;
204         case TOK_TIMEOUT:
205                 parse_lease_time(cfile, &config->timeout);
206                 return;
207         case TOK_RETRY:
208                 parse_lease_time(cfile, &config->retry_interval);
209                 return;
210         case TOK_SELECT_TIMEOUT:
211                 parse_lease_time(cfile, &config->select_interval);
212                 return;
213         case TOK_REBOOT:
214                 parse_lease_time(cfile, &config->reboot_timeout);
215                 return;
216         case TOK_BACKOFF_CUTOFF:
217                 parse_lease_time(cfile, &config->backoff_cutoff);
218                 return;
219         case TOK_INITIAL_INTERVAL:
220                 parse_lease_time(cfile, &config->initial_interval);
221                 return;
222         case TOK_SCRIPT:
223                 config->script_name = parse_string(cfile);
224                 return;
225         case TOK_INTERFACE:
226                 parse_interface_declaration(cfile);
227                 return;
228         case TOK_LEASE:
229                 parse_client_lease_statement(cfile, 1);
230                 return;
231         case TOK_ALIAS:
232                 skip_to_semi(cfile);
233                 return;
234         case TOK_REJECT:
235                 parse_reject_statement(cfile);
236                 return;
237         default:
238                 parse_warn("expecting a statement.");
239                 skip_to_semi(cfile);
240                 break;
241         }
242         token = next_token(&val, cfile);
243         if (token != ';') {
244                 parse_warn("semicolon expected.");
245                 skip_to_semi(cfile);
246         }
247 }
248
249 int
250 parse_X(FILE *cfile, u_int8_t *buf, int max)
251 {
252         int      token;
253         char    *val;
254         int      len;
255
256         token = peek_token(&val, cfile);
257         if (token == TOK_NUMBER_OR_NAME || token == TOK_NUMBER) {
258                 len = 0;
259                 do {
260                         token = next_token(&val, cfile);
261                         if (token != TOK_NUMBER && token != TOK_NUMBER_OR_NAME) {
262                                 parse_warn("expecting hexadecimal constant.");
263                                 skip_to_semi(cfile);
264                                 return (0);
265                         }
266                         convert_num(&buf[len], val, 16, 8);
267                         if (len++ > max) {
268                                 parse_warn("hexadecimal constant too long.");
269                                 skip_to_semi(cfile);
270                                 return (0);
271                         }
272                         token = peek_token(&val, cfile);
273                         if (token == ':')
274                                 token = next_token(&val, cfile);
275                 } while (token == ':');
276                 val = (char *)buf;
277         } else if (token == TOK_STRING) {
278                 token = next_token(&val, cfile);
279                 len = strlen(val);
280                 if (len + 1 > max) {
281                         parse_warn("string constant too long.");
282                         skip_to_semi(cfile);
283                         return (0);
284                 }
285                 memcpy(buf, val, len + 1);
286         } else {
287                 parse_warn("expecting string or hexadecimal data");
288                 skip_to_semi(cfile);
289                 return (0);
290         }
291         return (len);
292 }
293
294 /*
295  * option-list :== option_name |
296  *                 option_list COMMA option_name
297  */
298 int
299 parse_option_list(FILE *cfile, u_int8_t *list)
300 {
301         int      ix, i;
302         int      token;
303         char    *val;
304
305         ix = 0;
306         do {
307                 token = next_token(&val, cfile);
308                 if (!is_identifier(token)) {
309                         parse_warn("expected option name.");
310                         skip_to_semi(cfile);
311                         return (0);
312                 }
313                 for (i = 0; i < 256; i++)
314                         if (!strcasecmp(dhcp_options[i].name, val))
315                                 break;
316
317                 if (i == 256) {
318                         parse_warn("%s: unexpected option name.", val);
319                         skip_to_semi(cfile);
320                         return (0);
321                 }
322                 list[ix++] = i;
323                 if (ix == 256) {
324                         parse_warn("%s: too many options.", val);
325                         skip_to_semi(cfile);
326                         return (0);
327                 }
328                 token = next_token(&val, cfile);
329         } while (token == ',');
330         if (token != ';') {
331                 parse_warn("expecting semicolon.");
332                 skip_to_semi(cfile);
333                 return (0);
334         }
335         return (ix);
336 }
337
338 /*
339  * interface-declaration :==
340  *      INTERFACE string LBRACE client-declarations RBRACE
341  */
342 void
343 parse_interface_declaration(FILE *cfile)
344 {
345         char *val;
346         int token;
347
348         token = next_token(&val, cfile);
349         if (token != TOK_STRING) {
350                 parse_warn("expecting interface name (in quotes).");
351                 skip_to_semi(cfile);
352                 return;
353         }
354
355         if (strcmp(ifi->name, val) != 0) {
356                 skip_to_semi(cfile);
357                 return;
358         }
359
360         token = next_token(&val, cfile);
361         if (token != '{') {
362                 parse_warn("expecting left brace.");
363                 skip_to_semi(cfile);
364                 return;
365         }
366
367         do {
368                 token = peek_token(&val, cfile);
369                 if (token == EOF) {
370                         parse_warn("unterminated interface declaration.");
371                         return;
372                 }
373                 if (token == '}')
374                         break;
375                 parse_client_statement(cfile);
376         } while (1);
377         token = next_token(&val, cfile);
378 }
379
380 /*
381  * client-lease-statement :==
382  *      RBRACE client-lease-declarations LBRACE
383  *
384  *      client-lease-declarations :==
385  *              <nil> |
386  *              client-lease-declaration |
387  *              client-lease-declarations client-lease-declaration
388  */
389 void
390 parse_client_lease_statement(FILE *cfile, int is_static)
391 {
392         struct client_lease     *lease, *lp, *pl;
393         int                      token;
394         char                    *val;
395
396         token = next_token(&val, cfile);
397         if (token != '{') {
398                 parse_warn("expecting left brace.");
399                 skip_to_semi(cfile);
400                 return;
401         }
402
403         lease = malloc(sizeof(struct client_lease));
404         if (!lease)
405                 error("no memory for lease.");
406         memset(lease, 0, sizeof(*lease));
407         lease->is_static = is_static;
408
409         do {
410                 token = peek_token(&val, cfile);
411                 if (token == EOF) {
412                         parse_warn("unterminated lease declaration.");
413                         return;
414                 }
415                 if (token == '}')
416                         break;
417                 parse_client_lease_declaration(cfile, lease);
418         } while (1);
419         token = next_token(&val, cfile);
420
421         /* If the lease declaration didn't include an interface
422          * declaration that we recognized, it's of no use to us.
423          */
424         if (!ifi) {
425                 free_client_lease(lease);
426                 return;
427         }
428
429         /*
430          * The new lease may supersede a lease that's not the active
431          * lease but is still on the lease list, so scan the lease list
432          * looking for a lease with the same address, and if we find it,
433          * toss it.
434          */
435         pl = NULL;
436         for (lp = client->leases; lp; lp = lp->next) {
437                 if (addr_eq(lp->address, lease->address)) {
438                         if (pl)
439                                 pl->next = lp->next;
440                         else
441                                 client->leases = lp->next;
442                         free_client_lease(lp);
443                         break;
444                 } else
445                         pl = lp;
446         }
447
448         /*
449          * If this is a preloaded lease, just put it on the list of
450          * recorded leases - don't make it the active lease.
451          */
452         if (is_static) {
453                 lease->next = client->leases;
454                 client->leases = lease;
455                 return;
456         }
457
458         /*
459          * The last lease in the lease file on a particular interface is
460          * the active lease for that interface.    Of course, we don't
461          * know what the last lease in the file is until we've parsed
462          * the whole file, so at this point, we assume that the lease we
463          * just parsed is the active lease for its interface.   If
464          * there's already an active lease for the interface, and this
465          * lease is for the same ip address, then we just toss the old
466          * active lease and replace it with this one.   If this lease is
467          * for a different address, then if the old active lease has
468          * expired, we dump it; if not, we put it on the list of leases
469          * for this interface which are still valid but no longer
470          * active.
471          */
472         if (client->active) {
473                 if (client->active->expiry < cur_time)
474                         free_client_lease(client->active);
475                 else if (addr_eq(client->active->address, lease->address))
476                         free_client_lease(client->active);
477                 else {
478                         client->active->next = client->leases;
479                         client->leases = client->active;
480                 }
481         }
482         client->active = lease;
483
484         /* Phew. */
485 }
486
487 /*
488  * client-lease-declaration :==
489  *      BOOTP |
490  *      INTERFACE string |
491  *      FIXED_ADDR ip_address |
492  *      FILENAME string |
493  *      SERVER_NAME string |
494  *      OPTION option-decl |
495  *      RENEW time-decl |
496  *      REBIND time-decl |
497  *      EXPIRE time-decl
498  */
499 void
500 parse_client_lease_declaration(FILE *cfile, struct client_lease *lease)
501 {
502         char *val;
503         int token;
504
505         switch (next_token(&val, cfile)) {
506         case TOK_BOOTP:
507                 lease->is_bootp = 1;
508                 break;
509         case TOK_INTERFACE:
510                 token = next_token(&val, cfile);
511                 if (token != TOK_STRING) {
512                         parse_warn("expecting interface name (in quotes).");
513                         skip_to_semi(cfile);
514                         break;
515                 }
516                 if (strcmp(ifi->name, val) != 0) {
517                         parse_warn("wrong interface name. Expecting '%s'.",
518                            ifi->name);
519                         skip_to_semi(cfile);
520                         break;
521                 }
522                 break;
523         case TOK_FIXED_ADDR:
524                 if (!parse_ip_addr(cfile, &lease->address))
525                         return;
526                 break;
527         case TOK_MEDIUM:
528                 skip_to_semi(cfile);
529                 return;
530         case TOK_FILENAME:
531                 lease->filename = parse_string(cfile);
532                 return;
533         case TOK_SERVER_NAME:
534                 lease->server_name = parse_string(cfile);
535                 return;
536         case TOK_RENEW:
537                 lease->renewal = parse_date(cfile);
538                 return;
539         case TOK_REBIND:
540                 lease->rebind = parse_date(cfile);
541                 return;
542         case TOK_EXPIRE:
543                 lease->expiry = parse_date(cfile);
544                 return;
545         case TOK_OPTION:
546                 parse_option_decl(cfile, lease->options);
547                 return;
548         default:
549                 parse_warn("expecting lease declaration.");
550                 skip_to_semi(cfile);
551                 break;
552         }
553         token = next_token(&val, cfile);
554         if (token != ';') {
555                 parse_warn("expecting semicolon.");
556                 skip_to_semi(cfile);
557         }
558 }
559
560 int
561 parse_option_decl(FILE *cfile, struct option_data *options)
562 {
563         char            *val;
564         int              token;
565         u_int8_t         buf[4];
566         u_int8_t         hunkbuf[1024];
567         int              hunkix = 0;
568         char            *fmt;
569         struct iaddr     ip_addr;
570         u_int8_t        *dp;
571         int              len, code;
572         int              nul_term = 0;
573
574         token = next_token(&val, cfile);
575         if (!is_identifier(token)) {
576                 parse_warn("expecting identifier after option keyword.");
577                 if (token != ';')
578                         skip_to_semi(cfile);
579                 return (-1);
580         }
581
582         /* Look up the actual option info. */
583         fmt = NULL;
584         for (code = 0; code < 256; code++)
585                 if (strcmp(dhcp_options[code].name, val) == 0)
586                         break;
587
588         if (code > 255) {
589                 parse_warn("no option named %s", val);
590                 skip_to_semi(cfile);
591                 return (-1);
592         }
593
594         /* Parse the option data... */
595         do {
596                 for (fmt = dhcp_options[code].format; *fmt; fmt++) {
597                         if (*fmt == 'A')
598                                 break;
599                         switch (*fmt) {
600                         case 'X':
601                                 len = parse_X(cfile, &hunkbuf[hunkix],
602                                     sizeof(hunkbuf) - hunkix);
603                                 hunkix += len;
604                                 break;
605                         case 't': /* Text string... */
606                                 token = next_token(&val, cfile);
607                                 if (token != TOK_STRING) {
608                                         parse_warn("expecting string.");
609                                         skip_to_semi(cfile);
610                                         return (-1);
611                                 }
612                                 len = strlen(val);
613                                 if (hunkix + len + 1 > sizeof(hunkbuf)) {
614                                         parse_warn("option data buffer %s",
615                                             "overflow");
616                                         skip_to_semi(cfile);
617                                         return (-1);
618                                 }
619                                 memcpy(&hunkbuf[hunkix], val, len + 1);
620                                 nul_term = 1;
621                                 hunkix += len;
622                                 break;
623                         case 'I': /* IP address. */
624                                 if (!parse_ip_addr(cfile, &ip_addr))
625                                         return (-1);
626                                 len = ip_addr.len;
627                                 dp = ip_addr.iabuf;
628 alloc:
629                                 if (hunkix + len > sizeof(hunkbuf)) {
630                                         parse_warn("option data buffer "
631                                             "overflow");
632                                         skip_to_semi(cfile);
633                                         return (-1);
634                                 }
635                                 memcpy(&hunkbuf[hunkix], dp, len);
636                                 hunkix += len;
637                                 break;
638                         case 'L':       /* Unsigned 32-bit integer... */
639                         case 'l':       /* Signed 32-bit integer... */
640                                 token = next_token(&val, cfile);
641                                 if (token != TOK_NUMBER) {
642 need_number:
643                                         parse_warn("expecting number.");
644                                         if (token != ';')
645                                                 skip_to_semi(cfile);
646                                         return (-1);
647                                 }
648                                 convert_num(buf, val, 0, 32);
649                                 len = 4;
650                                 dp = buf;
651                                 goto alloc;
652                         case 's':       /* Signed 16-bit integer. */
653                         case 'S':       /* Unsigned 16-bit integer. */
654                                 token = next_token(&val, cfile);
655                                 if (token != TOK_NUMBER)
656                                         goto need_number;
657                                 convert_num(buf, val, 0, 16);
658                                 len = 2;
659                                 dp = buf;
660                                 goto alloc;
661                         case 'b':       /* Signed 8-bit integer. */
662                         case 'B':       /* Unsigned 8-bit integer. */
663                                 token = next_token(&val, cfile);
664                                 if (token != TOK_NUMBER)
665                                         goto need_number;
666                                 convert_num(buf, val, 0, 8);
667                                 len = 1;
668                                 dp = buf;
669                                 goto alloc;
670                         case 'f': /* Boolean flag. */
671                                 token = next_token(&val, cfile);
672                                 if (!is_identifier(token)) {
673                                         parse_warn("expecting identifier.");
674 bad_flag:
675                                         if (token != ';')
676                                                 skip_to_semi(cfile);
677                                         return (-1);
678                                 }
679                                 if (!strcasecmp(val, "true") ||
680                                     !strcasecmp(val, "on"))
681                                         buf[0] = 1;
682                                 else if (!strcasecmp(val, "false") ||
683                                     !strcasecmp(val, "off"))
684                                         buf[0] = 0;
685                                 else {
686                                         parse_warn("expecting boolean.");
687                                         goto bad_flag;
688                                 }
689                                 len = 1;
690                                 dp = buf;
691                                 goto alloc;
692                         default:
693                                 warning("Bad format %c in parse_option_param.",
694                                     *fmt);
695                                 skip_to_semi(cfile);
696                                 return (-1);
697                         }
698                 }
699                 token = next_token(&val, cfile);
700         } while (*fmt == 'A' && token == ',');
701
702         if (token != ';') {
703                 parse_warn("semicolon expected.");
704                 skip_to_semi(cfile);
705                 return (-1);
706         }
707
708         options[code].data = malloc(hunkix + nul_term);
709         if (!options[code].data)
710                 error("out of memory allocating option data.");
711         memcpy(options[code].data, hunkbuf, hunkix + nul_term);
712         options[code].len = hunkix;
713         return (code);
714 }
715
716 void
717 parse_reject_statement(FILE *cfile)
718 {
719         struct iaddrlist *list;
720         struct iaddr addr;
721         char *val;
722         int token;
723
724         do {
725                 if (!parse_ip_addr(cfile, &addr)) {
726                         parse_warn("expecting IP address.");
727                         skip_to_semi(cfile);
728                         return;
729                 }
730
731                 list = malloc(sizeof(struct iaddrlist));
732                 if (!list)
733                         error("no memory for reject list!");
734
735                 list->addr = addr;
736                 list->next = config->reject_list;
737                 config->reject_list = list;
738
739                 token = next_token(&val, cfile);
740         } while (token == ',');
741
742         if (token != ';') {
743                 parse_warn("expecting semicolon.");
744                 skip_to_semi(cfile);
745         }
746 }