]>
Commit | Line | Data |
---|---|---|
d7837182 TL |
1 | /* confpars.c |
2 | ||
3 | Parser for dhcpd config file... */ | |
4 | ||
5 | /* | |
95821729 TL |
6 | * Copyright (c) 1995, 1996 The Internet Software Consortium. |
7 | * All rights reserved. | |
d7837182 TL |
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 | "@(#) Copyright (c) 1995 The Internet Software Consortium. All rights reserved.\n"; | |
46 | #endif /* not lint */ | |
47 | ||
48 | #include "dhcpd.h" | |
49 | #include "dhctoken.h" | |
50 | ||
51 | static TIME parsed_time; | |
52 | ||
53 | /* conf-file :== statements | |
54 | declarations :== <nil> | declaration | declarations declaration */ | |
55 | ||
56 | void readconf (void) | |
57 | { | |
58 | FILE *cfile; | |
59 | char *val; | |
60 | int token; | |
61 | ||
62 | /* Set up the initial dhcp option universe. */ | |
63 | initialize_universes (); | |
64 | ||
65 | if ((cfile = fopen (_PATH_DHCPD_CONF, "r")) == NULL) | |
66 | error ("Can't open %s: %m", _PATH_DHCPD_CONF); | |
67 | do { | |
68 | token = peek_token (&val, cfile); | |
69 | if (token == EOF) | |
70 | break; | |
71 | parse_statement (cfile); | |
72 | } while (1); | |
73 | } | |
74 | ||
75 | /* statement :== host_statement */ | |
76 | ||
77 | void parse_statement (cfile) | |
78 | FILE *cfile; | |
79 | { | |
80 | char *val; | |
81 | jmp_buf bc; | |
82 | ||
83 | switch (next_token (&val, cfile)) { | |
84 | case HOST: | |
85 | if (!setjmp (bc)) { | |
86 | struct host_decl *hd = | |
87 | parse_host_statement (cfile, &bc); | |
88 | if (hd) { | |
89 | enter_host (hd); | |
90 | } | |
91 | } | |
92 | break; | |
93 | case LEASE: | |
94 | if (!setjmp (bc)) { | |
95 | struct lease *lease = | |
96 | parse_lease_statement (cfile, &bc); | |
97 | enter_lease (lease); | |
98 | } | |
99 | break; | |
100 | case TIMESTAMP: | |
101 | if (!setjmp (bc)) { | |
102 | parsed_time = parse_timestamp (cfile, &bc); | |
103 | } | |
104 | break; | |
105 | case RANGE: | |
106 | if (!setjmp (bc)) { | |
107 | parse_address_range (cfile, &bc); | |
108 | } | |
109 | break; | |
110 | default: | |
111 | parse_warn ("expecting a declaration."); | |
112 | skip_to_semi (cfile); | |
113 | break; | |
114 | } | |
115 | } | |
116 | ||
117 | void skip_to_semi (cfile) | |
118 | FILE *cfile; | |
119 | { | |
120 | int token; | |
121 | char *val; | |
122 | ||
123 | do { | |
124 | token = next_token (&val, cfile); | |
125 | } while (token != SEMI && token != EOF); | |
126 | } | |
127 | ||
128 | /* host_statement :== HOST hostname declarations SEMI | |
129 | host_declarations :== <nil> | host_declaration | |
089fb364 | 130 | | host_declarations host_declaration SEMI */ |
d7837182 TL |
131 | |
132 | struct host_decl *parse_host_statement (cfile, bc) | |
133 | FILE *cfile; | |
134 | jmp_buf *bc; | |
135 | { | |
136 | char *val; | |
137 | int token; | |
138 | struct host_decl tmp, *perm; | |
139 | ||
140 | memset (&tmp, 0, sizeof tmp); | |
141 | tmp.name = parse_host_name (cfile, bc); | |
142 | do { | |
143 | token = peek_token (&val, cfile); | |
144 | if (token == SEMI) { | |
145 | token = next_token (&val, cfile); | |
146 | break; | |
147 | } | |
148 | parse_host_decl (cfile, bc, &tmp); | |
149 | } while (1); | |
150 | perm = (struct host_decl *)malloc (sizeof (struct host_decl)); | |
151 | if (!perm) | |
152 | error ("can't allocate host decl struct for %s.", tmp.name); | |
153 | *perm = tmp; | |
154 | return perm; | |
155 | } | |
156 | ||
157 | /* host_name :== identifier | host_name DOT identifier */ | |
158 | ||
159 | char *parse_host_name (cfile, bc) | |
160 | FILE *cfile; | |
161 | jmp_buf *bc; | |
162 | { | |
163 | char *val; | |
164 | int token; | |
165 | int len = 0; | |
166 | char *s; | |
167 | char *t; | |
168 | pair c = (pair)0; | |
169 | ||
170 | /* Read a dotted hostname... */ | |
171 | do { | |
172 | /* Read a token, which should be an identifier. */ | |
173 | token = next_token (&val, cfile); | |
174 | if (!is_identifier (token)) { | |
089fb364 | 175 | parse_warn ("expecting an identifier in hostname"); |
d7837182 TL |
176 | skip_to_semi (cfile); |
177 | longjmp (*bc, 1); | |
178 | } | |
179 | /* Store this identifier... */ | |
180 | if (!(s = (char *)malloc (strlen (val) + 1))) | |
181 | error ("can't allocate temp space for hostname."); | |
182 | strcpy (s, val); | |
183 | c = cons ((caddr_t)s, c); | |
184 | len += strlen (s) + 1; | |
185 | /* Look for a dot; if it's there, keep going, otherwise | |
186 | we're done. */ | |
187 | token = peek_token (&val, cfile); | |
188 | if (token == DOT) | |
189 | token = next_token (&val, cfile); | |
190 | } while (token == DOT); | |
191 | ||
192 | /* Assemble the hostname together into a string. */ | |
193 | if (!(s = (char *)malloc (len))) | |
194 | error ("can't allocate space for hostname."); | |
195 | t = s + len; | |
196 | *--t = 0; | |
197 | while (c) { | |
198 | pair cdr = c -> cdr; | |
199 | int l = strlen ((char *)(c -> car)); | |
200 | t -= l; | |
201 | memcpy (t, (char *)(c -> car), l); | |
202 | /* Free up temp space. */ | |
203 | free (c -> car); | |
204 | free (c); | |
205 | c = cdr; | |
206 | if (t != s) | |
207 | *--t = '.'; | |
208 | } | |
209 | return s; | |
210 | } | |
211 | ||
212 | /* host_declaration :== hardware_declaration | filename_declaration | |
213 | | fixed_addr_declaration | option_declaration */ | |
214 | ||
215 | void parse_host_decl (cfile, bc, decl) | |
216 | FILE *cfile; | |
217 | jmp_buf *bc; | |
218 | struct host_decl *decl; | |
219 | { | |
220 | char *val; | |
221 | int token; | |
222 | ||
223 | token = next_token (&val, cfile); | |
224 | switch (token) { | |
225 | case HARDWARE: | |
226 | parse_hardware_decl (cfile, bc, decl); | |
227 | break; | |
228 | case FILENAME: | |
229 | parse_filename_decl (cfile, bc, decl); | |
230 | break; | |
231 | case FIXED_ADDR: | |
232 | parse_fixed_addr_decl (cfile, bc, decl); | |
233 | break; | |
234 | case OPTION: | |
235 | parse_option_decl (cfile, bc, decl); | |
236 | break; | |
97ca1699 TL |
237 | case CIADDR: |
238 | decl -> ciaddr = | |
239 | tree_cache (parse_ip_addr_or_hostname (cfile, bc, 0)); | |
240 | break; | |
241 | case YIADDR: | |
242 | decl -> yiaddr = | |
243 | tree_cache (parse_ip_addr_or_hostname (cfile, bc, 0)); | |
244 | break; | |
245 | case SIADDR: | |
246 | decl -> siaddr = | |
247 | tree_cache (parse_ip_addr_or_hostname (cfile, bc, 0)); | |
248 | break; | |
249 | case GIADDR: | |
250 | decl -> giaddr = | |
251 | tree_cache (parse_ip_addr_or_hostname (cfile, bc, 0)); | |
252 | break; | |
d7837182 TL |
253 | default: |
254 | parse_warn ("expecting a dhcp option declaration."); | |
255 | skip_to_semi (cfile); | |
256 | longjmp (*bc, 1); | |
257 | break; | |
258 | } | |
259 | } | |
260 | ||
261 | /* hardware_decl :== HARDWARE ETHERNET NUMBER COLON NUMBER COLON NUMBER COLON | |
262 | NUMBER COLON NUMBER COLON NUMBER */ | |
263 | ||
264 | void parse_hardware_decl (cfile, bc, decl) | |
265 | FILE *cfile; | |
266 | jmp_buf *bc; | |
267 | struct host_decl *decl; | |
268 | { | |
269 | char *val; | |
270 | int token; | |
271 | struct hardware hw; | |
272 | ||
273 | hw = parse_hardware_addr (cfile, bc); | |
274 | ||
275 | /* Find space for the new interface... */ | |
276 | if (decl -> interfaces) { | |
277 | decl -> interfaces = | |
278 | (struct hardware *)realloc (decl -> interfaces, | |
279 | ++decl -> interface_count * | |
280 | sizeof (struct hardware)); | |
281 | } else { | |
282 | decl -> interfaces = | |
283 | (struct hardware *)malloc (sizeof (struct hardware)); | |
284 | decl -> interface_count = 1; | |
285 | } | |
286 | if (!decl -> interfaces) | |
287 | error ("no memory for hardware interface info."); | |
288 | ||
289 | /* Copy out the information... */ | |
290 | decl -> interfaces [decl -> interface_count - 1].htype = hw.htype; | |
291 | decl -> interfaces [decl -> interface_count - 1].hlen = hw.hlen; | |
292 | memcpy (decl -> interfaces [decl -> interface_count - 1].haddr, | |
293 | &hw.haddr, hw.hlen); | |
294 | } | |
295 | ||
296 | struct hardware parse_hardware_addr (cfile, bc) | |
297 | FILE *cfile; | |
298 | jmp_buf *bc; | |
299 | { | |
300 | char *val; | |
301 | int token; | |
302 | int hlen; | |
303 | struct hardware rv; | |
304 | ||
305 | token = next_token (&val, cfile); | |
306 | switch (token) { | |
307 | case ETHERNET: | |
308 | rv.htype = ARPHRD_ETHER; | |
309 | hlen = 6; | |
310 | parse_numeric_aggregate (cfile, bc, | |
311 | (unsigned char *)&rv.haddr, &hlen, | |
312 | COLON, 16, 8); | |
313 | rv.hlen = hlen; | |
314 | break; | |
315 | default: | |
316 | parse_warn ("expecting a network hardware type"); | |
317 | skip_to_semi (cfile); | |
318 | longjmp (*bc, 1); | |
319 | } | |
320 | return rv; | |
321 | } | |
322 | ||
323 | /* filename_decl :== FILENAME STRING */ | |
324 | ||
325 | void parse_filename_decl (cfile, bc, decl) | |
326 | FILE *cfile; | |
327 | jmp_buf *bc; | |
328 | struct host_decl *decl; | |
329 | { | |
330 | char *val; | |
331 | int token; | |
332 | char *s; | |
333 | ||
334 | token = next_token (&val, cfile); | |
335 | if (token != STRING) { | |
336 | parse_warn ("filename must be a string"); | |
337 | skip_to_semi (cfile); | |
338 | longjmp (*bc, 1); | |
339 | } | |
340 | s = (char *)malloc (strlen (val)); | |
341 | if (!s) | |
342 | error ("no memory for filename."); | |
343 | strcpy (s, val); | |
344 | decl -> filename = s; | |
345 | } | |
346 | ||
347 | /* ip_addr_or_hostname :== ip_address | hostname | |
348 | ip_address :== NUMBER DOT NUMBER DOT NUMBER DOT NUMBER | |
349 | ||
350 | Parse an ip address or a hostname. If uniform is zero, put in | |
351 | a TREE_LIMIT node to catch hostnames that evaluate to more than | |
352 | one IP address. */ | |
353 | ||
354 | struct tree *parse_ip_addr_or_hostname (cfile, bc, uniform) | |
355 | FILE *cfile; | |
356 | jmp_buf *bc; | |
357 | int uniform; | |
358 | { | |
359 | char *val; | |
360 | int token; | |
361 | unsigned char addr [4]; | |
362 | int len = sizeof addr; | |
363 | char *name; | |
364 | struct tree *rv; | |
365 | ||
366 | token = peek_token (&val, cfile); | |
367 | if (is_identifier (token)) { | |
368 | name = parse_host_name (cfile, bc); | |
369 | rv = tree_host_lookup (name); | |
370 | if (!uniform) | |
371 | rv = tree_limit (rv, 4); | |
372 | } else if (token == NUMBER) { | |
373 | parse_numeric_aggregate (cfile, bc, addr, &len, DOT, 10, 8); | |
374 | rv = tree_const (addr, len); | |
375 | } else { | |
376 | parse_warn ("%s (%d): expecting IP address or hostname", | |
377 | val, token); | |
378 | skip_to_semi (cfile); | |
379 | longjmp (*bc, 1); | |
380 | } | |
381 | return rv; | |
382 | } | |
383 | ||
384 | ||
385 | /* fixed_addr_declaration :== FIXED_ADDR ip_addr_or_hostname */ | |
386 | ||
387 | void parse_fixed_addr_decl (cfile, bc, decl) | |
388 | FILE *cfile; | |
389 | jmp_buf *bc; | |
390 | struct host_decl *decl; | |
391 | { | |
392 | decl -> fixed_addr = | |
393 | tree_cache (parse_ip_addr_or_hostname (cfile, bc, 0)); | |
394 | } | |
395 | ||
396 | /* option_declaration :== OPTION identifier DOT identifier <syntax> | | |
397 | OPTION identifier <syntax> | |
398 | ||
399 | Option syntax is handled specially through format strings, so it | |
400 | would be painful to come up with BNF for it. However, it always | |
401 | starts as above. */ | |
402 | ||
403 | void parse_option_decl (cfile, bc, decl) | |
404 | FILE *cfile; | |
405 | jmp_buf *bc; | |
406 | struct host_decl *decl; | |
407 | { | |
408 | char *val; | |
409 | int token; | |
410 | unsigned char buf [4]; | |
411 | char *vendor; | |
412 | char *fmt; | |
413 | struct universe *universe; | |
414 | struct option *option; | |
415 | struct tree *tree = (struct tree *)0; | |
416 | ||
417 | token = next_token (&val, cfile); | |
418 | if (!is_identifier (token)) { | |
419 | parse_warn ("expecting identifier after option keyword."); | |
420 | if (token != SEMI) | |
421 | skip_to_semi (cfile); | |
422 | longjmp (*bc, 1); | |
423 | } | |
424 | vendor = dmalloc (strlen (val) + 1, "parse_option_decl"); | |
425 | strcpy (vendor, val); | |
426 | token = peek_token (&val, cfile); | |
427 | if (token == DOT) { | |
428 | /* Go ahead and take the DOT token... */ | |
429 | token = next_token (&val, cfile); | |
430 | ||
431 | /* The next token should be an identifier... */ | |
432 | token = next_token (&val, cfile); | |
433 | if (!is_identifier (token)) { | |
434 | parse_warn ("expecting identifier after '.'"); | |
435 | if (token != SEMI) | |
436 | skip_to_semi (cfile); | |
437 | longjmp (*bc, 1); | |
438 | } | |
439 | ||
440 | /* Look up the option name hash table for the specified | |
441 | vendor. */ | |
442 | universe = (struct universe *)hash_lookup (&universe_hash, | |
443 | vendor, 0); | |
444 | /* If it's not there, we can't parse the rest of the | |
445 | statement. */ | |
446 | if (!universe) { | |
447 | parse_warn ("no vendor named %s.", vendor); | |
448 | skip_to_semi (cfile); | |
449 | longjmp (*bc, 1); | |
450 | } | |
451 | } else { | |
452 | /* Use the default hash table, which contains all the | |
453 | standard dhcp option names. */ | |
454 | val = vendor; | |
455 | universe = &dhcp_universe; | |
456 | } | |
457 | ||
458 | /* Look up the actual option info... */ | |
459 | option = (struct option *)hash_lookup (universe -> hash, val, 0); | |
460 | ||
461 | /* If we didn't get an option structure, it's an undefined option. */ | |
462 | if (!option) { | |
463 | if (val == vendor) | |
464 | parse_warn ("no option named %s", val); | |
465 | else | |
466 | parse_warn ("no option named %s for vendor %s", | |
467 | val, vendor); | |
468 | skip_to_semi (cfile); | |
469 | longjmp (*bc, 1); | |
470 | } | |
471 | ||
472 | /* Free the initial identifier token. */ | |
473 | free (vendor); | |
474 | ||
475 | /* Parse the option data... */ | |
476 | do { | |
477 | /* Set a flag if this is an array of a simple type (i.e., | |
478 | not an array of pairs of IP addresses, or something | |
479 | like that. */ | |
480 | int uniform = option -> format [1] == 'A'; | |
481 | ||
482 | for (fmt = option -> format; *fmt; fmt++) { | |
483 | if (*fmt == 'A') | |
484 | break; | |
485 | switch (*fmt) { | |
486 | case 't': /* Text string... */ | |
487 | token = next_token (&val, cfile); | |
488 | if (token != STRING | |
489 | && !is_identifier (token)) { | |
490 | parse_warn ("expecting string."); | |
491 | if (token != SEMI) | |
492 | skip_to_semi (cfile); | |
493 | longjmp (*bc, 1); | |
494 | } | |
495 | tree = tree_concat (tree, | |
496 | tree_const (val, | |
497 | strlen (val))); | |
498 | break; | |
499 | ||
500 | case 'I': /* IP address or hostname. */ | |
501 | tree = tree_concat (tree, | |
502 | parse_ip_addr_or_hostname | |
503 | (cfile, bc, uniform)); | |
504 | break; | |
505 | ||
506 | case 'L': /* Unsigned 32-bit integer... */ | |
507 | case 'l': /* Signed 32-bit integer... */ | |
508 | token = next_token (&val, cfile); | |
509 | if (token != NUMBER) { | |
510 | need_number: | |
511 | parse_warn ("expecting number."); | |
512 | if (token != SEMI) | |
513 | skip_to_semi (cfile); | |
514 | longjmp (*bc, 1); | |
515 | } | |
516 | convert_num (buf, val, 0, 32); | |
517 | tree = tree_concat (tree, tree_const (buf, 4)); | |
518 | break; | |
519 | case 's': /* Signed 16-bit integer. */ | |
520 | case 'S': /* Unsigned 16-bit integer. */ | |
521 | token = next_token (&val, cfile); | |
522 | if (token != NUMBER) | |
523 | goto need_number; | |
524 | convert_num (buf, val, 0, 16); | |
525 | tree = tree_concat (tree, tree_const (buf, 2)); | |
526 | break; | |
527 | case 'b': /* Signed 8-bit integer. */ | |
528 | case 'B': /* Unsigned 8-bit integer. */ | |
529 | token = next_token (&val, cfile); | |
530 | if (token != NUMBER) | |
531 | goto need_number; | |
532 | convert_num (buf, val, 0, 8); | |
533 | tree = tree_concat (tree, tree_const (buf, 1)); | |
534 | break; | |
535 | case 'f': /* Boolean flag. */ | |
536 | token = next_token (&val, cfile); | |
537 | if (!is_identifier (token)) { | |
538 | parse_warn ("expecting identifier."); | |
539 | bad_flag: | |
540 | if (token != SEMI) | |
541 | skip_to_semi (cfile); | |
542 | longjmp (*bc, 1); | |
543 | } | |
544 | if (!strcasecmp (val, "true") | |
545 | || !strcasecmp (val, "on")) | |
546 | buf [0] = 1; | |
547 | else if (!strcasecmp (val, "false") | |
548 | || !strcasecmp (val, "off")) | |
549 | buf [0] = 0; | |
550 | else { | |
551 | parse_warn ("expecting boolean."); | |
552 | goto bad_flag; | |
553 | } | |
554 | tree = tree_concat (tree, tree_const (buf, 1)); | |
555 | break; | |
556 | default: | |
557 | warn ("Bad format %c in parse_option_decl.", | |
558 | *fmt); | |
559 | skip_to_semi (cfile); | |
560 | longjmp (*bc, 1); | |
561 | } | |
562 | } | |
563 | if (*fmt == 'A') { | |
564 | token = peek_token (&val, cfile); | |
565 | if (token == COMMA) { | |
566 | token = next_token (&val, cfile); | |
567 | continue; | |
568 | } | |
569 | break; | |
570 | } | |
571 | } while (*fmt == 'A'); | |
572 | ||
573 | if (decl -> options [option -> code]) { | |
574 | parse_warn ("duplicate option code %d (%s).", | |
575 | option -> code, option -> name); | |
576 | } | |
577 | decl -> options [option -> code] = tree_cache (tree); | |
578 | } | |
579 | ||
089fb364 | 580 | /* timestamp :== TIMESTAMP date SEMI |
d7837182 TL |
581 | |
582 | Timestamps are actually not used in dhcpd.conf, which is a static file, | |
583 | but rather in the database file and the journal file. */ | |
584 | ||
585 | TIME parse_timestamp (cfile, bc) | |
586 | FILE *cfile; | |
587 | jmp_buf *bc; | |
588 | { | |
089fb364 TL |
589 | TIME rv; |
590 | char *val; | |
591 | int token; | |
592 | ||
593 | rv = parse_date (cfile, bc); | |
594 | token = next_token (&val, cfile); | |
595 | if (token != SEMI) { | |
596 | parse_warn ("semicolon expected"); | |
597 | skip_to_semi (cfile); | |
598 | longjmp (*bc, 1); | |
599 | } | |
600 | return rv; | |
d7837182 TL |
601 | } |
602 | ||
089fb364 | 603 | /* lease_decl :== LEASE ip_address lease_modifiers SEMI |
d7837182 TL |
604 | lease_modifiers :== <nil> |
605 | | lease_modifier | |
606 | | lease_modifier lease_modifiers | |
607 | lease_modifier :== STARTS date | |
608 | | ENDS date | |
609 | | UID hex_numbers | |
610 | | HOST identifier | |
611 | | CLASS identifier | |
612 | | TIMESTAMP number */ | |
613 | ||
614 | struct lease *parse_lease_statement (cfile, bc) | |
615 | FILE *cfile; | |
616 | jmp_buf *bc; | |
617 | { | |
618 | char *val; | |
619 | int token; | |
620 | unsigned char addr [4]; | |
621 | int len = sizeof addr; | |
622 | char *name; | |
623 | unsigned char *uid; | |
624 | int seenmask = 0; | |
625 | int seenbit; | |
626 | char tbuf [32]; | |
627 | char ubuf [1024]; | |
628 | static struct lease lease; | |
629 | ||
630 | /* Get the address for which the lease has been issued. */ | |
631 | parse_numeric_aggregate (cfile, bc, addr, &len, DOT, 10, 8); | |
089fb364 TL |
632 | memcpy (lease.ip_addr.iabuf, addr, len); |
633 | lease.ip_addr.len = len; | |
d7837182 TL |
634 | |
635 | do { | |
636 | token = next_token (&val, cfile); | |
637 | if (token == SEMI) | |
638 | break; | |
639 | strncpy (val, tbuf, sizeof tbuf); | |
640 | tbuf [(sizeof tbuf) - 1] = 0; | |
641 | ||
642 | /* Parse any of the times associated with the lease. */ | |
643 | if (token == STARTS || token == ENDS || token == TIMESTAMP) { | |
644 | TIME t; | |
645 | t = parse_date (cfile, bc); | |
646 | switch (token) { | |
647 | case STARTS: | |
648 | seenbit = 1; | |
649 | lease.starts = t; | |
650 | break; | |
651 | ||
652 | case ENDS: | |
653 | seenbit = 2; | |
654 | lease.ends = t; | |
655 | break; | |
656 | ||
657 | case TIMESTAMP: | |
658 | seenbit = 4; | |
659 | lease.timestamp = t; | |
660 | break; | |
661 | } | |
662 | } else { | |
663 | switch (token) { | |
664 | /* Colon-seperated hexadecimal octets... */ | |
665 | case UID: | |
666 | seenbit = 8; | |
667 | lease.uid_len = 0; | |
668 | parse_numeric_aggregate (cfile, bc, ubuf, | |
669 | &lease.uid_len, | |
670 | ':', 16, 8); | |
671 | lease.uid = (unsigned char *) | |
672 | malloc (lease.uid_len); | |
673 | if (!lease.uid) { | |
674 | error ("No memory for lease uid"); | |
675 | } | |
676 | memcpy (lease.uid, ubuf, lease.uid_len); | |
677 | break; | |
678 | ||
679 | case HOST: | |
680 | seenbit = 16; | |
681 | token = next_token (&val, cfile); | |
682 | if (!is_identifier (token)) { | |
683 | if (token != SEMI) | |
684 | skip_to_semi (cfile); | |
685 | longjmp (*bc, 1); | |
686 | } | |
687 | lease.host = | |
688 | find_host_by_name (val); | |
689 | if (!lease.host) | |
690 | parse_warn ("lease host ``%s'' is %s", | |
089fb364 | 691 | val, |
d7837182 TL |
692 | "no longer known."); |
693 | break; | |
694 | ||
695 | case CLASS: | |
696 | seenbit = 32; | |
697 | token = next_token (&val, cfile); | |
698 | if (!is_identifier (token)) { | |
699 | if (token != SEMI) | |
700 | skip_to_semi (cfile); | |
701 | longjmp (*bc, 1); | |
702 | } | |
703 | /* for now, we aren't using this. */ | |
704 | break; | |
705 | ||
706 | case HARDWARE: | |
707 | seenbit = 64; | |
708 | lease.hardware_addr | |
709 | = parse_hardware_addr (cfile, bc); | |
710 | break; | |
711 | ||
712 | default: | |
713 | if (token != SEMI) | |
714 | skip_to_semi (cfile); | |
715 | longjmp (*bc, 1); | |
716 | } | |
717 | } | |
718 | if (seenmask & seenbit) { | |
719 | parse_warn ("Too many %s declarations in lease %s\n", | |
089fb364 | 720 | tbuf, piaddr (lease.ip_addr)); |
d7837182 TL |
721 | } else |
722 | seenmask |= seenbit; | |
723 | } while (1); | |
724 | return &lease; | |
725 | } | |
726 | ||
089fb364 | 727 | /* address_range :== RANGE ip_address ip_address ip_address SEMI */ |
d7837182 TL |
728 | |
729 | void parse_address_range (cfile, bc) | |
730 | FILE *cfile; | |
731 | jmp_buf *bc; | |
732 | { | |
089fb364 | 733 | struct iaddr low, high, mask; |
d7837182 TL |
734 | unsigned char addr [4]; |
735 | int len = sizeof addr; | |
089fb364 TL |
736 | int token; |
737 | char *val; | |
d7837182 TL |
738 | |
739 | /* Get the bottom address in the range... */ | |
740 | parse_numeric_aggregate (cfile, bc, addr, &len, DOT, 10, 8); | |
089fb364 TL |
741 | memcpy (low.iabuf, addr, len); |
742 | low.len = len; | |
d7837182 TL |
743 | |
744 | /* Get the top address in the range... */ | |
745 | parse_numeric_aggregate (cfile, bc, addr, &len, DOT, 10, 8); | |
089fb364 TL |
746 | memcpy (high.iabuf, addr, len); |
747 | high.len = len; | |
d7837182 TL |
748 | |
749 | /* Get the netmask of the subnet containing the range... */ | |
750 | parse_numeric_aggregate (cfile, bc, addr, &len, DOT, 10, 8); | |
089fb364 TL |
751 | memcpy (mask.iabuf, addr, len); |
752 | mask.len = len; | |
753 | ||
754 | /* Snarf the semi... */ | |
755 | token = next_token (&val, cfile); | |
756 | if (token != SEMI) { | |
757 | parse_warn ("semicolon expected"); | |
758 | skip_to_semi (cfile); | |
759 | longjmp (*bc, 1); | |
760 | } | |
d7837182 TL |
761 | |
762 | /* Create the new address range... */ | |
763 | new_address_range (low, high, mask); | |
764 | } | |
765 | ||
766 | /* date :== NUMBER NUMBER/NUMBER/NUMBER NUMBER:NUMBER:NUMBER | |
767 | ||
768 | Dates are always in GMT; first number is day of week; next is | |
769 | year/month/day; next is hours:minutes:seconds on a 24-hour | |
770 | clock. */ | |
771 | ||
772 | TIME parse_date (cfile, bc) | |
773 | FILE *cfile; | |
774 | jmp_buf *bc; | |
775 | { | |
776 | TIME t; | |
777 | struct tm tm; | |
778 | char *val; | |
779 | int token; | |
780 | ||
781 | /* Day of week... */ | |
782 | token = next_token (&val, cfile); | |
783 | if (token != NUMBER) { | |
784 | parse_warn ("numeric day of week expected."); | |
785 | if (token != SEMI) | |
786 | skip_to_semi (cfile); | |
787 | longjmp (*bc, 1); | |
788 | } | |
089fb364 | 789 | tm.tm_wday = atoi (val); |
d7837182 TL |
790 | |
791 | /* Year... */ | |
792 | token = next_token (&val, cfile); | |
793 | if (token != NUMBER) { | |
794 | parse_warn ("numeric year expected."); | |
795 | if (token != SEMI) | |
796 | skip_to_semi (cfile); | |
797 | longjmp (*bc, 1); | |
798 | } | |
089fb364 | 799 | tm.tm_year = atoi (val); |
d7837182 TL |
800 | if (tm.tm_year > 1900) |
801 | tm.tm_year -= 1900; | |
802 | ||
803 | /* Slash seperating year from month... */ | |
804 | token = next_token (&val, cfile); | |
805 | if (token != SLASH) { | |
806 | parse_warn ("expected slash seperating year from month."); | |
807 | if (token != SEMI) | |
808 | skip_to_semi (cfile); | |
809 | longjmp (*bc, 1); | |
810 | } | |
811 | ||
812 | /* Month... */ | |
813 | token = next_token (&val, cfile); | |
814 | if (token != NUMBER) { | |
815 | parse_warn ("numeric month expected."); | |
816 | if (token != SEMI) | |
817 | skip_to_semi (cfile); | |
818 | longjmp (*bc, 1); | |
819 | } | |
089fb364 | 820 | tm.tm_mon = atoi (val); |
d7837182 TL |
821 | |
822 | /* Slash seperating month from day... */ | |
823 | token = next_token (&val, cfile); | |
824 | if (token != SLASH) { | |
825 | parse_warn ("expected slash seperating month from day."); | |
826 | if (token != SEMI) | |
827 | skip_to_semi (cfile); | |
828 | longjmp (*bc, 1); | |
829 | } | |
830 | ||
831 | /* Month... */ | |
832 | token = next_token (&val, cfile); | |
833 | if (token != NUMBER) { | |
834 | parse_warn ("numeric day of month expected."); | |
835 | if (token != SEMI) | |
836 | skip_to_semi (cfile); | |
837 | longjmp (*bc, 1); | |
838 | } | |
089fb364 | 839 | tm.tm_mday = atoi (val); |
d7837182 TL |
840 | |
841 | /* Hour... */ | |
842 | token = next_token (&val, cfile); | |
843 | if (token != NUMBER) { | |
844 | parse_warn ("numeric hour expected."); | |
845 | if (token != SEMI) | |
846 | skip_to_semi (cfile); | |
847 | longjmp (*bc, 1); | |
848 | } | |
089fb364 | 849 | tm.tm_hour = atoi (val); |
d7837182 TL |
850 | |
851 | /* Colon seperating hour from minute... */ | |
852 | token = next_token (&val, cfile); | |
853 | if (token != COLON) { | |
854 | parse_warn ("expected colon seperating hour from minute."); | |
855 | if (token != SEMI) | |
856 | skip_to_semi (cfile); | |
857 | longjmp (*bc, 1); | |
858 | } | |
859 | ||
860 | /* Minute... */ | |
861 | token = next_token (&val, cfile); | |
862 | if (token != NUMBER) { | |
863 | parse_warn ("numeric minute expected."); | |
864 | if (token != SEMI) | |
865 | skip_to_semi (cfile); | |
866 | longjmp (*bc, 1); | |
867 | } | |
089fb364 | 868 | tm.tm_min = atoi (val); |
d7837182 TL |
869 | |
870 | /* Colon seperating minute from second... */ | |
871 | token = next_token (&val, cfile); | |
872 | if (token != COLON) { | |
873 | parse_warn ("expected colon seperating hour from minute."); | |
874 | if (token != SEMI) | |
875 | skip_to_semi (cfile); | |
876 | longjmp (*bc, 1); | |
877 | } | |
878 | ||
879 | /* Minute... */ | |
880 | token = next_token (&val, cfile); | |
881 | if (token != NUMBER) { | |
882 | parse_warn ("numeric minute expected."); | |
883 | if (token != SEMI) | |
884 | skip_to_semi (cfile); | |
885 | longjmp (*bc, 1); | |
886 | } | |
089fb364 | 887 | tm.tm_sec = atoi (val); |
d7837182 TL |
888 | |
889 | tm.tm_zone = "GMT"; | |
890 | tm.tm_isdst = 0; | |
891 | tm.tm_gmtoff = 0; | |
892 | ||
893 | /* XXX */ /* We assume that mktime does not use tm_yday. */ | |
894 | tm.tm_yday = 0; | |
895 | ||
896 | return mktime (&tm); | |
897 | } | |
898 | ||
899 | /* No BNF for numeric aggregates - that's defined by the caller. What | |
900 | this function does is to parse a sequence of numbers seperated by | |
901 | the token specified in seperator. If max is zero, any number of | |
902 | numbers will be parsed; otherwise, exactly max numbers are | |
903 | expected. Base and size tell us how to internalize the numbers | |
904 | once they've been tokenized. */ | |
905 | ||
906 | unsigned char *parse_numeric_aggregate (cfile, bc, buf, | |
907 | max, seperator, base, size) | |
908 | FILE *cfile; | |
909 | jmp_buf *bc; | |
910 | unsigned char *buf; | |
911 | int *max; | |
912 | int seperator; | |
913 | int base; | |
914 | int size; | |
915 | { | |
916 | char *val; | |
917 | int token; | |
918 | unsigned char *bufp = buf, *s, *t; | |
919 | int count = 0; | |
920 | pair c = (pair)0; | |
921 | ||
922 | if (!bufp && *max) { | |
923 | bufp = (unsigned char *)malloc (*max * size / 8); | |
924 | if (!bufp) | |
925 | error ("can't allocate space for numeric aggregate"); | |
926 | } else | |
927 | s = bufp; | |
928 | ||
929 | do { | |
930 | if (count) { | |
931 | token = peek_token (&val, cfile); | |
932 | if (token != seperator) { | |
933 | if (!*max) | |
934 | break; | |
935 | parse_warn ("too few numbers."); | |
936 | skip_to_semi (cfile); | |
937 | longjmp (*bc, 1); | |
938 | } | |
939 | token = next_token (&val, cfile); | |
940 | } | |
941 | token = next_token (&val, cfile); | |
942 | /* Allow NUMBER_OR_ATOM if base is 16. */ | |
943 | if (token != NUMBER && | |
944 | (base != 16 || token != NUMBER_OR_ATOM)) { | |
945 | parse_warn ("expecting numeric value."); | |
946 | skip_to_semi (cfile); | |
947 | longjmp (*bc, 1); | |
948 | } | |
949 | /* If we can, convert the number now; otherwise, build | |
950 | a linked list of all the numbers. */ | |
951 | if (s) { | |
952 | convert_num (s, val, base, size); | |
953 | s += size / 8; | |
954 | } else { | |
955 | t = (char *)malloc (strlen (val) + 1); | |
956 | if (!t) | |
957 | error ("no temp space for number."); | |
958 | strcpy (t, val); | |
959 | c = cons (t, c); | |
960 | } | |
961 | } while (++count != *max); | |
962 | ||
963 | /* If we had to cons up a list, convert it now. */ | |
964 | if (c) { | |
965 | bufp = (unsigned char *)malloc (count * size / 8); | |
966 | if (!bufp) | |
967 | error ("can't allocate space for numeric aggregate."); | |
968 | s = bufp; | |
969 | *max = count; | |
970 | } | |
971 | while (c) { | |
972 | pair cdr = c -> cdr; | |
973 | convert_num (s, (char *)(c -> car), base, size); | |
974 | s += size / 8; | |
975 | /* Free up temp space. */ | |
976 | free (c -> car); | |
977 | free (c); | |
978 | c = cdr; | |
979 | } | |
980 | return bufp; | |
981 | } | |
982 | ||
983 | void convert_num (buf, str, base, size) | |
984 | unsigned char *buf; | |
985 | char *str; | |
986 | int base; | |
987 | int size; | |
988 | { | |
989 | char *ptr = str; | |
990 | int negative = 0; | |
991 | u_int32_t val = 0; | |
992 | int tval; | |
993 | int max; | |
994 | ||
995 | if (*ptr == '-') { | |
996 | negative = 1; | |
997 | ++ptr; | |
998 | } | |
999 | ||
1000 | /* If base wasn't specified, figure it out from the data. */ | |
1001 | if (!base) { | |
1002 | if (ptr [0] == '0') { | |
1003 | if (ptr [1] == 'x') { | |
1004 | base = 16; | |
1005 | ptr += 2; | |
1006 | } else if (isascii (ptr [1]) && isdigit (ptr [1])) { | |
1007 | base = 8; | |
1008 | ptr += 1; | |
1009 | } else { | |
1010 | base = 10; | |
1011 | } | |
1012 | } else { | |
1013 | base = 10; | |
1014 | } | |
1015 | } | |
1016 | ||
1017 | do { | |
1018 | tval = *ptr++; | |
1019 | /* XXX assumes ASCII... */ | |
1020 | if (tval >= 'a') | |
1021 | tval = tval - 'a' + 10; | |
1022 | else if (tval >= 'A') | |
1023 | tval = tval - 'A' + 10; | |
1024 | else if (tval >= '0') | |
1025 | tval -= '0'; | |
1026 | else { | |
1027 | warn ("Bogus number: %s.", str); | |
1028 | break; | |
1029 | } | |
1030 | if (tval >= base) { | |
1031 | warn ("Bogus number: %s: digit %d not in base %d\n", | |
1032 | str, tval, base); | |
1033 | break; | |
1034 | } | |
1035 | val = val * base + tval; | |
1036 | } while (*ptr); | |
1037 | ||
1038 | if (negative) | |
1039 | max = (1 << (size - 1)); | |
1040 | else | |
1041 | max = (1 << size) - 1; | |
1042 | if (val > max) { | |
1043 | switch (base) { | |
1044 | case 8: | |
1045 | warn ("value %s%lo exceeds max (%d) for precision.", | |
1046 | negative ? "-" : "", val, max); | |
1047 | break; | |
1048 | case 16: | |
1049 | warn ("value %s%lx exceeds max (%d) for precision.", | |
1050 | negative ? "-" : "", val, max); | |
1051 | break; | |
1052 | default: | |
1053 | warn ("value %s%ld exceeds max (%d) for precision.", | |
1054 | negative ? "-" : "", val, max); | |
1055 | break; | |
1056 | } | |
1057 | } | |
1058 | ||
1059 | if (negative) { | |
1060 | switch (size) { | |
1061 | case 8: | |
1062 | *buf = -(unsigned long)val; | |
1063 | break; | |
1064 | case 16: | |
1065 | putShort (buf, -(unsigned long)val); | |
1066 | break; | |
1067 | case 32: | |
1068 | putLong (buf, -(unsigned long)val); | |
1069 | break; | |
1070 | default: | |
1071 | warn ("Unexpected integer size: %d\n"); | |
1072 | break; | |
1073 | } | |
1074 | } else { | |
1075 | switch (size) { | |
1076 | case 8: | |
1077 | *buf = (u_int8_t)val; | |
1078 | break; | |
1079 | case 16: | |
1080 | putUShort (buf, (u_int16_t)val); | |
1081 | break; | |
1082 | case 32: | |
1083 | putULong (buf, val); | |
1084 | break; | |
1085 | default: | |
1086 | warn ("Unexpected integer size: %d\n"); | |
1087 | break; | |
1088 | } | |
1089 | } | |
1090 | } |