1 |
|
|
/* $OpenBSD: confpars.c,v 1.33 2017/04/24 14:58:36 krw Exp $ */ |
2 |
|
|
|
3 |
|
|
/* |
4 |
|
|
* Copyright (c) 1995, 1996, 1997 The Internet Software Consortium. |
5 |
|
|
* All rights reserved. |
6 |
|
|
* |
7 |
|
|
* Redistribution and use in source and binary forms, with or without |
8 |
|
|
* modification, are permitted provided that the following conditions |
9 |
|
|
* are met: |
10 |
|
|
* |
11 |
|
|
* 1. Redistributions of source code must retain the above copyright |
12 |
|
|
* notice, this list of conditions and the following disclaimer. |
13 |
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
14 |
|
|
* notice, this list of conditions and the following disclaimer in the |
15 |
|
|
* documentation and/or other materials provided with the distribution. |
16 |
|
|
* 3. Neither the name of The Internet Software Consortium nor the names |
17 |
|
|
* of its contributors may be used to endorse or promote products derived |
18 |
|
|
* from this software without specific prior written permission. |
19 |
|
|
* |
20 |
|
|
* THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND |
21 |
|
|
* CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, |
22 |
|
|
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
23 |
|
|
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
24 |
|
|
* DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR |
25 |
|
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
26 |
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
27 |
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
28 |
|
|
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
29 |
|
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
30 |
|
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT |
31 |
|
|
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
32 |
|
|
* SUCH DAMAGE. |
33 |
|
|
* |
34 |
|
|
* This software has been written for the Internet Software Consortium |
35 |
|
|
* by Ted Lemon <mellon@fugue.com> in cooperation with Vixie |
36 |
|
|
* Enterprises. To learn more about the Internet Software Consortium, |
37 |
|
|
* see ``http://www.vix.com/isc''. To learn more about Vixie |
38 |
|
|
* Enterprises, see ``http://www.vix.com''. |
39 |
|
|
*/ |
40 |
|
|
|
41 |
|
|
#include <sys/types.h> |
42 |
|
|
#include <sys/socket.h> |
43 |
|
|
|
44 |
|
|
#include <net/if.h> |
45 |
|
|
|
46 |
|
|
#include <netdb.h> |
47 |
|
|
#include <stdio.h> |
48 |
|
|
#include <stdlib.h> |
49 |
|
|
#include <string.h> |
50 |
|
|
|
51 |
|
|
#include "dhcp.h" |
52 |
|
|
#include "tree.h" |
53 |
|
|
#include "dhcpd.h" |
54 |
|
|
#include "dhctoken.h" |
55 |
|
|
#include "log.h" |
56 |
|
|
|
57 |
|
|
int parse_cidr(FILE *, unsigned char *, unsigned char *); |
58 |
|
|
|
59 |
|
|
/* |
60 |
|
|
* conf-file :== parameters declarations EOF |
61 |
|
|
* parameters :== <nil> | parameter | parameters parameter |
62 |
|
|
* declarations :== <nil> | declaration | declarations declaration |
63 |
|
|
*/ |
64 |
|
|
int |
65 |
|
|
readconf(void) |
66 |
|
|
{ |
67 |
|
|
FILE *cfile; |
68 |
|
|
char *val; |
69 |
|
|
int token; |
70 |
|
|
int declaration = 0; |
71 |
|
|
|
72 |
|
|
new_parse(path_dhcpd_conf); |
73 |
|
|
|
74 |
|
|
/* Set up the initial dhcp option universe. */ |
75 |
|
|
initialize_universes(); |
76 |
|
|
|
77 |
|
|
/* Set up the global defaults. */ |
78 |
|
|
root_group.default_lease_time = 43200; /* 12 hours. */ |
79 |
|
|
root_group.max_lease_time = 86400; /* 24 hours. */ |
80 |
|
|
root_group.bootp_lease_cutoff = MAX_TIME; |
81 |
|
|
root_group.boot_unknown_clients = 1; |
82 |
|
|
root_group.allow_bootp = 1; |
83 |
|
|
root_group.allow_booting = 1; |
84 |
|
|
root_group.authoritative = 1; |
85 |
|
|
root_group.echo_client_id = 1; |
86 |
|
|
|
87 |
|
|
if ((cfile = fopen(path_dhcpd_conf, "r")) == NULL) |
88 |
|
|
fatal("Can't open %s", path_dhcpd_conf); |
89 |
|
|
|
90 |
|
|
do { |
91 |
|
|
token = peek_token(&val, cfile); |
92 |
|
|
if (token == EOF) |
93 |
|
|
break; |
94 |
|
|
declaration = parse_statement(cfile, &root_group, |
95 |
|
|
ROOT_GROUP, |
96 |
|
|
NULL, |
97 |
|
|
declaration); |
98 |
|
|
} while (1); |
99 |
|
|
token = next_token(&val, cfile); /* Clear the peek buffer */ |
100 |
|
|
fclose(cfile); |
101 |
|
|
|
102 |
|
|
return !warnings_occurred; |
103 |
|
|
} |
104 |
|
|
|
105 |
|
|
/* |
106 |
|
|
* lease-file :== lease-declarations EOF |
107 |
|
|
* lease-statments :== <nil> |
108 |
|
|
* | lease-declaration |
109 |
|
|
* | lease-declarations lease-declaration |
110 |
|
|
*/ |
111 |
|
|
void |
112 |
|
|
read_leases(void) |
113 |
|
|
{ |
114 |
|
|
FILE *cfile; |
115 |
|
|
char *val; |
116 |
|
|
int token; |
117 |
|
|
|
118 |
|
|
new_parse(path_dhcpd_db); |
119 |
|
|
|
120 |
|
|
/* |
121 |
|
|
* Open the lease file. If we can't open it, fail. The reason |
122 |
|
|
* for this is that although on initial startup, the absence of |
123 |
|
|
* a lease file is perfectly benign, if dhcpd has been running |
124 |
|
|
* and this file is absent, it means that dhcpd tried and failed |
125 |
|
|
* to rewrite the lease database. If we proceed and the |
126 |
|
|
* problem which caused the rewrite to fail has been fixed, but no |
127 |
|
|
* human has corrected the database problem, then we are left |
128 |
|
|
* thinking that no leases have been assigned to anybody, which |
129 |
|
|
* could create severe network chaos. |
130 |
|
|
*/ |
131 |
|
|
if ((cfile = fopen(path_dhcpd_db, "r")) == NULL) { |
132 |
|
|
log_warn("Can't open lease database (%s)", path_dhcpd_db); |
133 |
|
|
log_warnx("check for failed database rewrite attempt!"); |
134 |
|
|
log_warnx("Please read the dhcpd.leases manual page if you"); |
135 |
|
|
fatalx("don't know what to do about this."); |
136 |
|
|
} |
137 |
|
|
|
138 |
|
|
do { |
139 |
|
|
token = next_token(&val, cfile); |
140 |
|
|
if (token == EOF) |
141 |
|
|
break; |
142 |
|
|
if (token != TOK_LEASE) { |
143 |
|
|
log_warnx("Corrupt lease file - possible data loss!"); |
144 |
|
|
skip_to_semi(cfile); |
145 |
|
|
} else { |
146 |
|
|
struct lease *lease; |
147 |
|
|
lease = parse_lease_declaration(cfile); |
148 |
|
|
if (lease) |
149 |
|
|
enter_lease(lease); |
150 |
|
|
else |
151 |
|
|
parse_warn("possibly corrupt lease file"); |
152 |
|
|
} |
153 |
|
|
|
154 |
|
|
} while (1); |
155 |
|
|
fclose(cfile); |
156 |
|
|
} |
157 |
|
|
|
158 |
|
|
/* |
159 |
|
|
* statement :== parameter | declaration |
160 |
|
|
* |
161 |
|
|
* parameter :== timestamp |
162 |
|
|
* | DEFAULT_LEASE_TIME lease_time |
163 |
|
|
* | MAX_LEASE_TIME lease_time |
164 |
|
|
* | DYNAMIC_BOOTP_LEASE_CUTOFF date |
165 |
|
|
* | DYNAMIC_BOOTP_LEASE_LENGTH lease_time |
166 |
|
|
* | BOOT_UNKNOWN_CLIENTS boolean |
167 |
|
|
* | GET_LEASE_HOSTNAMES boolean |
168 |
|
|
* | USE_HOST_DECL_NAME boolean |
169 |
|
|
* | NEXT_SERVER ip-addr-or-hostname SEMI |
170 |
|
|
* | option_parameter |
171 |
|
|
* | SERVER-IDENTIFIER ip-addr-or-hostname SEMI |
172 |
|
|
* | FILENAME string-parameter |
173 |
|
|
* | SERVER_NAME string-parameter |
174 |
|
|
* | hardware-parameter |
175 |
|
|
* | fixed-address-parameter |
176 |
|
|
* | ALLOW allow-deny-keyword |
177 |
|
|
* | DENY allow-deny-keyword |
178 |
|
|
* | USE_LEASE_ADDR_FOR_DEFAULT_ROUTE boolean |
179 |
|
|
* |
180 |
|
|
* declaration :== host-declaration |
181 |
|
|
* | group-declaration |
182 |
|
|
* | shared-network-declaration |
183 |
|
|
* | subnet-declaration |
184 |
|
|
* | VENDOR_CLASS class-declaration |
185 |
|
|
* | USER_CLASS class-declaration |
186 |
|
|
* | RANGE address-range-declaration |
187 |
|
|
*/ |
188 |
|
|
int |
189 |
|
|
parse_statement(FILE *cfile, struct group *group, int type, |
190 |
|
|
struct host_decl *host_decl, int declaration) |
191 |
|
|
{ |
192 |
|
|
int token; |
193 |
|
|
char *val; |
194 |
|
|
struct shared_network *share; |
195 |
|
|
char *n; |
196 |
|
|
struct tree *tree; |
197 |
|
|
struct tree_cache *cache; |
198 |
|
|
struct hardware hardware; |
199 |
|
|
|
200 |
|
|
switch (next_token(&val, cfile)) { |
201 |
|
|
case TOK_HOST: |
202 |
|
|
if (type != HOST_DECL) |
203 |
|
|
parse_host_declaration(cfile, group); |
204 |
|
|
else { |
205 |
|
|
parse_warn("host declarations not allowed here."); |
206 |
|
|
skip_to_semi(cfile); |
207 |
|
|
} |
208 |
|
|
return 1; |
209 |
|
|
|
210 |
|
|
case TOK_GROUP: |
211 |
|
|
if (type != HOST_DECL) |
212 |
|
|
parse_group_declaration(cfile, group); |
213 |
|
|
else { |
214 |
|
|
parse_warn("host declarations not allowed here."); |
215 |
|
|
skip_to_semi(cfile); |
216 |
|
|
} |
217 |
|
|
return 1; |
218 |
|
|
|
219 |
|
|
case TOK_TIMESTAMP: |
220 |
|
|
break; |
221 |
|
|
|
222 |
|
|
case TOK_SHARED_NETWORK: |
223 |
|
|
if (type == SHARED_NET_DECL || |
224 |
|
|
type == HOST_DECL || |
225 |
|
|
type == SUBNET_DECL) { |
226 |
|
|
parse_warn("shared-network parameters not %s.", |
227 |
|
|
"allowed here"); |
228 |
|
|
skip_to_semi(cfile); |
229 |
|
|
break; |
230 |
|
|
} |
231 |
|
|
|
232 |
|
|
parse_shared_net_declaration(cfile, group); |
233 |
|
|
return 1; |
234 |
|
|
|
235 |
|
|
case TOK_SUBNET: |
236 |
|
|
if (type == HOST_DECL || type == SUBNET_DECL) { |
237 |
|
|
parse_warn("subnet declarations not allowed here."); |
238 |
|
|
skip_to_semi(cfile); |
239 |
|
|
return 1; |
240 |
|
|
} |
241 |
|
|
|
242 |
|
|
/* If we're in a subnet declaration, just do the parse. */ |
243 |
|
|
if (group->shared_network) { |
244 |
|
|
parse_subnet_declaration(cfile, |
245 |
|
|
group->shared_network); |
246 |
|
|
break; |
247 |
|
|
} |
248 |
|
|
|
249 |
|
|
/* |
250 |
|
|
* Otherwise, cons up a fake shared network structure |
251 |
|
|
* and populate it with the lone subnet. |
252 |
|
|
*/ |
253 |
|
|
|
254 |
|
|
share = calloc(1, sizeof(struct shared_network)); |
255 |
|
|
if (!share) |
256 |
|
|
fatalx("No memory for shared subnet"); |
257 |
|
|
share->group = clone_group(group, "parse_statement:subnet"); |
258 |
|
|
share->group->shared_network = share; |
259 |
|
|
|
260 |
|
|
parse_subnet_declaration(cfile, share); |
261 |
|
|
|
262 |
|
|
/* share->subnets is the subnet we just parsed. */ |
263 |
|
|
if (share->subnets) { |
264 |
|
|
share->interface = |
265 |
|
|
share->subnets->interface; |
266 |
|
|
|
267 |
|
|
/* Make the shared network name from network number. */ |
268 |
|
|
n = piaddr(share->subnets->net); |
269 |
|
|
share->name = strdup(n); |
270 |
|
|
if (share->name == NULL) |
271 |
|
|
fatalx("no memory for subnet name"); |
272 |
|
|
|
273 |
|
|
/* |
274 |
|
|
* Copy the authoritative parameter from the subnet, |
275 |
|
|
* since there is no opportunity to declare it here. |
276 |
|
|
*/ |
277 |
|
|
share->group->authoritative = |
278 |
|
|
share->subnets->group->authoritative; |
279 |
|
|
enter_shared_network(share); |
280 |
|
|
} |
281 |
|
|
return 1; |
282 |
|
|
|
283 |
|
|
case TOK_VENDOR_CLASS: |
284 |
|
|
parse_class_declaration(cfile, group, 0); |
285 |
|
|
return 1; |
286 |
|
|
|
287 |
|
|
case TOK_USER_CLASS: |
288 |
|
|
parse_class_declaration(cfile, group, 1); |
289 |
|
|
return 1; |
290 |
|
|
|
291 |
|
|
case TOK_DEFAULT_LEASE_TIME: |
292 |
|
|
parse_lease_time(cfile, &group->default_lease_time); |
293 |
|
|
break; |
294 |
|
|
|
295 |
|
|
case TOK_MAX_LEASE_TIME: |
296 |
|
|
parse_lease_time(cfile, &group->max_lease_time); |
297 |
|
|
break; |
298 |
|
|
|
299 |
|
|
case TOK_DYNAMIC_BOOTP_LEASE_CUTOFF: |
300 |
|
|
group->bootp_lease_cutoff = parse_date(cfile); |
301 |
|
|
break; |
302 |
|
|
|
303 |
|
|
case TOK_DYNAMIC_BOOTP_LEASE_LENGTH: |
304 |
|
|
parse_lease_time(cfile, &group->bootp_lease_length); |
305 |
|
|
break; |
306 |
|
|
|
307 |
|
|
case TOK_BOOT_UNKNOWN_CLIENTS: |
308 |
|
|
if (type == HOST_DECL) |
309 |
|
|
parse_warn("boot-unknown-clients not allowed here."); |
310 |
|
|
group->boot_unknown_clients = parse_boolean(cfile); |
311 |
|
|
break; |
312 |
|
|
|
313 |
|
|
case TOK_GET_LEASE_HOSTNAMES: |
314 |
|
|
if (type == HOST_DECL) |
315 |
|
|
parse_warn("get-lease-hostnames not allowed here."); |
316 |
|
|
group->get_lease_hostnames = parse_boolean(cfile); |
317 |
|
|
break; |
318 |
|
|
|
319 |
|
|
case TOK_ALWAYS_REPLY_RFC1048: |
320 |
|
|
group->always_reply_rfc1048 = parse_boolean(cfile); |
321 |
|
|
break; |
322 |
|
|
|
323 |
|
|
case TOK_ECHO_CLIENT_ID: |
324 |
|
|
group->echo_client_id = parse_boolean(cfile); |
325 |
|
|
break; |
326 |
|
|
|
327 |
|
|
case TOK_USE_HOST_DECL_NAMES: |
328 |
|
|
if (type == HOST_DECL) |
329 |
|
|
parse_warn("use-host-decl-names not allowed here."); |
330 |
|
|
group->use_host_decl_names = parse_boolean(cfile); |
331 |
|
|
break; |
332 |
|
|
|
333 |
|
|
case TOK_USE_LEASE_ADDR_FOR_DEFAULT_ROUTE: |
334 |
|
|
group->use_lease_addr_for_default_route = |
335 |
|
|
parse_boolean(cfile); |
336 |
|
|
break; |
337 |
|
|
|
338 |
|
|
case TOK_TOKEN_NOT: |
339 |
|
|
token = next_token(&val, cfile); |
340 |
|
|
switch (token) { |
341 |
|
|
case TOK_AUTHORITATIVE: |
342 |
|
|
if (type == HOST_DECL) |
343 |
|
|
parse_warn("authority makes no sense here."); |
344 |
|
|
group->authoritative = 0; |
345 |
|
|
parse_semi(cfile); |
346 |
|
|
break; |
347 |
|
|
default: |
348 |
|
|
parse_warn("expecting assertion"); |
349 |
|
|
skip_to_semi(cfile); |
350 |
|
|
break; |
351 |
|
|
} |
352 |
|
|
break; |
353 |
|
|
|
354 |
|
|
case TOK_AUTHORITATIVE: |
355 |
|
|
if (type == HOST_DECL) |
356 |
|
|
parse_warn("authority makes no sense here."); |
357 |
|
|
group->authoritative = 1; |
358 |
|
|
parse_semi(cfile); |
359 |
|
|
break; |
360 |
|
|
|
361 |
|
|
case TOK_NEXT_SERVER: |
362 |
|
|
tree = parse_ip_addr_or_hostname(cfile, 0); |
363 |
|
|
if (!tree) |
364 |
|
|
break; |
365 |
|
|
cache = tree_cache(tree); |
366 |
|
|
if (!tree_evaluate (cache)) |
367 |
|
|
fatalx("next-server is not known"); |
368 |
|
|
group->next_server.len = 4; |
369 |
|
|
memcpy(group->next_server.iabuf, |
370 |
|
|
cache->value, group->next_server.len); |
371 |
|
|
parse_semi(cfile); |
372 |
|
|
break; |
373 |
|
|
|
374 |
|
|
case TOK_OPTION: |
375 |
|
|
parse_option_param(cfile, group); |
376 |
|
|
break; |
377 |
|
|
|
378 |
|
|
case TOK_SERVER_IDENTIFIER: |
379 |
|
|
tree = parse_ip_addr_or_hostname(cfile, 0); |
380 |
|
|
if (!tree) |
381 |
|
|
return declaration; |
382 |
|
|
group->options[DHO_DHCP_SERVER_IDENTIFIER] = tree_cache(tree); |
383 |
|
|
token = next_token(&val, cfile); |
384 |
|
|
break; |
385 |
|
|
|
386 |
|
|
case TOK_FILENAME: |
387 |
|
|
group->filename = parse_string(cfile); |
388 |
|
|
break; |
389 |
|
|
|
390 |
|
|
case TOK_SERVER_NAME: |
391 |
|
|
group->server_name = parse_string(cfile); |
392 |
|
|
break; |
393 |
|
|
|
394 |
|
|
case TOK_HARDWARE: |
395 |
|
|
parse_hardware_param(cfile, &hardware); |
396 |
|
|
if (host_decl) |
397 |
|
|
host_decl->interface = hardware; |
398 |
|
|
else |
399 |
|
|
parse_warn("hardware address parameter %s", |
400 |
|
|
"not allowed here."); |
401 |
|
|
break; |
402 |
|
|
|
403 |
|
|
case TOK_FIXED_ADDR: |
404 |
|
|
cache = parse_fixed_addr_param(cfile); |
405 |
|
|
if (host_decl) |
406 |
|
|
host_decl->fixed_addr = cache; |
407 |
|
|
else |
408 |
|
|
parse_warn("fixed-address parameter not %s", |
409 |
|
|
"allowed here."); |
410 |
|
|
break; |
411 |
|
|
|
412 |
|
|
case TOK_RANGE: |
413 |
|
|
if (type != SUBNET_DECL || !group->subnet) { |
414 |
|
|
parse_warn("range declaration not allowed here."); |
415 |
|
|
skip_to_semi(cfile); |
416 |
|
|
return declaration; |
417 |
|
|
} |
418 |
|
|
parse_address_range(cfile, group->subnet); |
419 |
|
|
return declaration; |
420 |
|
|
|
421 |
|
|
case TOK_ALLOW: |
422 |
|
|
parse_allow_deny(cfile, group, 1); |
423 |
|
|
break; |
424 |
|
|
|
425 |
|
|
case TOK_DENY: |
426 |
|
|
parse_allow_deny(cfile, group, 0); |
427 |
|
|
break; |
428 |
|
|
|
429 |
|
|
default: |
430 |
|
|
if (declaration) |
431 |
|
|
parse_warn("expecting a declaration."); |
432 |
|
|
else |
433 |
|
|
parse_warn("expecting a parameter or declaration."); |
434 |
|
|
skip_to_semi(cfile); |
435 |
|
|
return declaration; |
436 |
|
|
} |
437 |
|
|
|
438 |
|
|
if (declaration) { |
439 |
|
|
parse_warn("parameters not allowed after first declaration."); |
440 |
|
|
return 1; |
441 |
|
|
} |
442 |
|
|
|
443 |
|
|
return 0; |
444 |
|
|
} |
445 |
|
|
|
446 |
|
|
/* |
447 |
|
|
* allow-deny-keyword :== BOOTP |
448 |
|
|
* | BOOTING |
449 |
|
|
* | DYNAMIC_BOOTP |
450 |
|
|
* | UNKNOWN_CLIENTS |
451 |
|
|
*/ |
452 |
|
|
void |
453 |
|
|
parse_allow_deny(FILE *cfile, struct group *group, int flag) |
454 |
|
|
{ |
455 |
|
|
int token; |
456 |
|
|
char *val; |
457 |
|
|
|
458 |
|
|
token = next_token(&val, cfile); |
459 |
|
|
switch (token) { |
460 |
|
|
case TOK_BOOTP: |
461 |
|
|
group->allow_bootp = flag; |
462 |
|
|
break; |
463 |
|
|
|
464 |
|
|
case TOK_BOOTING: |
465 |
|
|
group->allow_booting = flag; |
466 |
|
|
break; |
467 |
|
|
|
468 |
|
|
case TOK_DYNAMIC_BOOTP: |
469 |
|
|
group->dynamic_bootp = flag; |
470 |
|
|
break; |
471 |
|
|
|
472 |
|
|
case TOK_UNKNOWN_CLIENTS: |
473 |
|
|
group->boot_unknown_clients = flag; |
474 |
|
|
break; |
475 |
|
|
|
476 |
|
|
default: |
477 |
|
|
parse_warn("expecting allow/deny key"); |
478 |
|
|
skip_to_semi(cfile); |
479 |
|
|
return; |
480 |
|
|
} |
481 |
|
|
parse_semi(cfile); |
482 |
|
|
} |
483 |
|
|
|
484 |
|
|
/* |
485 |
|
|
* boolean :== ON SEMI | OFF SEMI | TRUE SEMI | FALSE SEMI |
486 |
|
|
*/ |
487 |
|
|
int |
488 |
|
|
parse_boolean(FILE *cfile) |
489 |
|
|
{ |
490 |
|
|
char *val; |
491 |
|
|
int rv; |
492 |
|
|
|
493 |
|
|
next_token(&val, cfile); |
494 |
|
|
if (!strcasecmp (val, "true") || !strcasecmp (val, "on")) |
495 |
|
|
rv = 1; |
496 |
|
|
else if (!strcasecmp (val, "false") || !strcasecmp (val, "off")) |
497 |
|
|
rv = 0; |
498 |
|
|
else { |
499 |
|
|
parse_warn("boolean value (true/false/on/off) expected"); |
500 |
|
|
skip_to_semi(cfile); |
501 |
|
|
return 0; |
502 |
|
|
} |
503 |
|
|
parse_semi(cfile); |
504 |
|
|
return rv; |
505 |
|
|
} |
506 |
|
|
|
507 |
|
|
/* |
508 |
|
|
* Expect a left brace; if there isn't one, skip over the rest of the |
509 |
|
|
* statement and return zero; otherwise, return 1. |
510 |
|
|
*/ |
511 |
|
|
int |
512 |
|
|
parse_lbrace(FILE *cfile) |
513 |
|
|
{ |
514 |
|
|
int token; |
515 |
|
|
char *val; |
516 |
|
|
|
517 |
|
|
token = next_token(&val, cfile); |
518 |
|
|
if (token != '{') { |
519 |
|
|
parse_warn("expecting left brace."); |
520 |
|
|
skip_to_semi(cfile); |
521 |
|
|
return 0; |
522 |
|
|
} |
523 |
|
|
return 1; |
524 |
|
|
} |
525 |
|
|
|
526 |
|
|
|
527 |
|
|
/* |
528 |
|
|
* host-declaration :== hostname '{' parameters declarations '}' |
529 |
|
|
*/ |
530 |
|
|
void |
531 |
|
|
parse_host_declaration(FILE *cfile, struct group *group) |
532 |
|
|
{ |
533 |
|
|
char *val; |
534 |
|
|
int token; |
535 |
|
|
struct host_decl *host; |
536 |
|
|
char *name = parse_host_name(cfile); |
537 |
|
|
int declaration = 0; |
538 |
|
|
|
539 |
|
|
if (!name) |
540 |
|
|
return; |
541 |
|
|
|
542 |
|
|
host = calloc(1, sizeof (struct host_decl)); |
543 |
|
|
if (!host) |
544 |
|
|
fatalx("can't allocate host decl struct %s.", name); |
545 |
|
|
|
546 |
|
|
host->name = name; |
547 |
|
|
host->group = clone_group(group, "parse_host_declaration"); |
548 |
|
|
|
549 |
|
|
if (!parse_lbrace(cfile)) { |
550 |
|
|
free(host->name); |
551 |
|
|
free(host->group); |
552 |
|
|
free(host); |
553 |
|
|
return; |
554 |
|
|
} |
555 |
|
|
|
556 |
|
|
do { |
557 |
|
|
token = peek_token(&val, cfile); |
558 |
|
|
if (token == '}') { |
559 |
|
|
token = next_token(&val, cfile); |
560 |
|
|
break; |
561 |
|
|
} |
562 |
|
|
if (token == EOF) { |
563 |
|
|
token = next_token(&val, cfile); |
564 |
|
|
parse_warn("unexpected end of file"); |
565 |
|
|
break; |
566 |
|
|
} |
567 |
|
|
declaration = parse_statement(cfile, host->group, |
568 |
|
|
HOST_DECL, host, declaration); |
569 |
|
|
} while (1); |
570 |
|
|
|
571 |
|
|
if (!host->group->options[DHO_HOST_NAME] && |
572 |
|
|
host->group->use_host_decl_names) { |
573 |
|
|
host->group->options[DHO_HOST_NAME] = |
574 |
|
|
new_tree_cache("parse_host_declaration"); |
575 |
|
|
if (!host->group->options[DHO_HOST_NAME]) |
576 |
|
|
fatalx("can't allocate a tree cache for hostname."); |
577 |
|
|
host->group->options[DHO_HOST_NAME]->len = |
578 |
|
|
strlen(name); |
579 |
|
|
host->group->options[DHO_HOST_NAME]->value = |
580 |
|
|
(unsigned char *)name; |
581 |
|
|
host->group->options[DHO_HOST_NAME]->buf_size = |
582 |
|
|
host->group->options[DHO_HOST_NAME]->len; |
583 |
|
|
host->group->options[DHO_HOST_NAME]->timeout = -1; |
584 |
|
|
host->group->options[DHO_HOST_NAME]->tree = |
585 |
|
|
NULL; |
586 |
|
|
} |
587 |
|
|
|
588 |
|
|
enter_host(host); |
589 |
|
|
} |
590 |
|
|
|
591 |
|
|
/* |
592 |
|
|
* class-declaration :== STRING '{' parameters declarations '}' |
593 |
|
|
*/ |
594 |
|
|
void |
595 |
|
|
parse_class_declaration(FILE *cfile, struct group *group, int type) |
596 |
|
|
{ |
597 |
|
|
char *val; |
598 |
|
|
int token; |
599 |
|
|
struct class *class; |
600 |
|
|
int declaration = 0; |
601 |
|
|
|
602 |
|
|
token = next_token(&val, cfile); |
603 |
|
|
if (token != TOK_STRING) { |
604 |
|
|
parse_warn("Expecting class name"); |
605 |
|
|
skip_to_semi(cfile); |
606 |
|
|
return; |
607 |
|
|
} |
608 |
|
|
|
609 |
|
|
class = add_class (type, val); |
610 |
|
|
if (!class) |
611 |
|
|
fatalx("No memory for class %s.", val); |
612 |
|
|
class->group = clone_group(group, "parse_class_declaration"); |
613 |
|
|
|
614 |
|
|
if (!parse_lbrace(cfile)) { |
615 |
|
|
free(class->name); |
616 |
|
|
free(class->group); |
617 |
|
|
free(class); |
618 |
|
|
return; |
619 |
|
|
} |
620 |
|
|
|
621 |
|
|
do { |
622 |
|
|
token = peek_token(&val, cfile); |
623 |
|
|
if (token == '}') { |
624 |
|
|
token = next_token(&val, cfile); |
625 |
|
|
break; |
626 |
|
|
} else if (token == EOF) { |
627 |
|
|
token = next_token(&val, cfile); |
628 |
|
|
parse_warn("unexpected end of file"); |
629 |
|
|
break; |
630 |
|
|
} else { |
631 |
|
|
declaration = parse_statement(cfile, class->group, |
632 |
|
|
CLASS_DECL, NULL, declaration); |
633 |
|
|
} |
634 |
|
|
} while (1); |
635 |
|
|
} |
636 |
|
|
|
637 |
|
|
/* |
638 |
|
|
* shared-network-declaration :== |
639 |
|
|
* hostname LBRACE declarations parameters RBRACE |
640 |
|
|
*/ |
641 |
|
|
void |
642 |
|
|
parse_shared_net_declaration(FILE *cfile, struct group *group) |
643 |
|
|
{ |
644 |
|
|
char *val; |
645 |
|
|
int token; |
646 |
|
|
struct shared_network *share; |
647 |
|
|
char *name; |
648 |
|
|
int declaration = 0; |
649 |
|
|
|
650 |
|
|
share = calloc(1, sizeof(struct shared_network)); |
651 |
|
|
if (!share) |
652 |
|
|
fatalx("No memory for shared subnet"); |
653 |
|
|
share->leases = NULL; |
654 |
|
|
share->last_lease = NULL; |
655 |
|
|
share->insertion_point = NULL; |
656 |
|
|
share->next = NULL; |
657 |
|
|
share->interface = NULL; |
658 |
|
|
share->group = clone_group(group, "parse_shared_net_declaration"); |
659 |
|
|
share->group->shared_network = share; |
660 |
|
|
|
661 |
|
|
/* Get the name of the shared network. */ |
662 |
|
|
token = peek_token(&val, cfile); |
663 |
|
|
if (token == TOK_STRING) { |
664 |
|
|
token = next_token(&val, cfile); |
665 |
|
|
|
666 |
|
|
if (val[0] == 0) { |
667 |
|
|
parse_warn("zero-length shared network name"); |
668 |
|
|
val = "<no-name-given>"; |
669 |
|
|
} |
670 |
|
|
name = strdup(val); |
671 |
|
|
if (name == NULL) |
672 |
|
|
fatalx("no memory for shared network name"); |
673 |
|
|
} else { |
674 |
|
|
name = parse_host_name(cfile); |
675 |
|
|
if (!name) { |
676 |
|
|
free(share->group); |
677 |
|
|
free(share); |
678 |
|
|
return; |
679 |
|
|
} |
680 |
|
|
} |
681 |
|
|
share->name = name; |
682 |
|
|
|
683 |
|
|
if (!parse_lbrace(cfile)) { |
684 |
|
|
free(share->group); |
685 |
|
|
free(share->name); |
686 |
|
|
free(share); |
687 |
|
|
return; |
688 |
|
|
} |
689 |
|
|
|
690 |
|
|
do { |
691 |
|
|
token = peek_token(&val, cfile); |
692 |
|
|
if (token == '}') { |
693 |
|
|
token = next_token(&val, cfile); |
694 |
|
|
if (!share->subnets) { |
695 |
|
|
free(share->group); |
696 |
|
|
free(share->name); |
697 |
|
|
free(share); |
698 |
|
|
parse_warn("empty shared-network decl"); |
699 |
|
|
return; |
700 |
|
|
} |
701 |
|
|
enter_shared_network(share); |
702 |
|
|
return; |
703 |
|
|
} else if (token == EOF) { |
704 |
|
|
token = next_token(&val, cfile); |
705 |
|
|
parse_warn("unexpected end of file"); |
706 |
|
|
break; |
707 |
|
|
} |
708 |
|
|
|
709 |
|
|
declaration = parse_statement(cfile, share->group, |
710 |
|
|
SHARED_NET_DECL, NULL, declaration); |
711 |
|
|
} while (1); |
712 |
|
|
} |
713 |
|
|
|
714 |
|
|
/* |
715 |
|
|
* subnet-declaration :== |
716 |
|
|
* net NETMASK netmask RBRACE parameters declarations LBRACE |
717 |
|
|
*/ |
718 |
|
|
void |
719 |
|
|
parse_subnet_declaration(FILE *cfile, struct shared_network *share) |
720 |
|
|
{ |
721 |
|
|
char *val; |
722 |
|
|
int token; |
723 |
|
|
struct subnet *subnet, *t, *u; |
724 |
|
|
struct iaddr iaddr; |
725 |
|
|
unsigned char addr[4]; |
726 |
|
|
int len = sizeof addr; |
727 |
|
|
int declaration = 0; |
728 |
|
|
|
729 |
|
|
subnet = calloc(1, sizeof(struct subnet)); |
730 |
|
|
if (!subnet) |
731 |
|
|
fatalx("No memory for new subnet"); |
732 |
|
|
subnet->shared_network = share; |
733 |
|
|
subnet->group = clone_group(share->group, "parse_subnet_declaration"); |
734 |
|
|
subnet->group->subnet = subnet; |
735 |
|
|
|
736 |
|
|
/* Get the network number. */ |
737 |
|
|
if (!parse_numeric_aggregate(cfile, addr, &len, '.', 10, 8)) { |
738 |
|
|
free(subnet->group); |
739 |
|
|
free(subnet); |
740 |
|
|
return; |
741 |
|
|
} |
742 |
|
|
memcpy(iaddr.iabuf, addr, len); |
743 |
|
|
iaddr.len = len; |
744 |
|
|
subnet->net = iaddr; |
745 |
|
|
|
746 |
|
|
token = next_token(&val, cfile); |
747 |
|
|
if (token != TOK_NETMASK) { |
748 |
|
|
free(subnet->group); |
749 |
|
|
free(subnet); |
750 |
|
|
parse_warn("Expecting netmask"); |
751 |
|
|
skip_to_semi(cfile); |
752 |
|
|
return; |
753 |
|
|
} |
754 |
|
|
|
755 |
|
|
/* Get the netmask. */ |
756 |
|
|
if (!parse_numeric_aggregate(cfile, addr, &len, '.', 10, 8)) { |
757 |
|
|
free(subnet->group); |
758 |
|
|
free(subnet); |
759 |
|
|
return; |
760 |
|
|
} |
761 |
|
|
memcpy(iaddr.iabuf, addr, len); |
762 |
|
|
iaddr.len = len; |
763 |
|
|
subnet->netmask = iaddr; |
764 |
|
|
|
765 |
|
|
/* Save only the subnet number. */ |
766 |
|
|
subnet->net = subnet_number(subnet->net, subnet->netmask); |
767 |
|
|
|
768 |
|
|
enter_subnet(subnet); |
769 |
|
|
|
770 |
|
|
if (!parse_lbrace(cfile)) |
771 |
|
|
return; |
772 |
|
|
|
773 |
|
|
do { |
774 |
|
|
token = peek_token(&val, cfile); |
775 |
|
|
if (token == '}') { |
776 |
|
|
token = next_token(&val, cfile); |
777 |
|
|
break; |
778 |
|
|
} else if (token == EOF) { |
779 |
|
|
token = next_token(&val, cfile); |
780 |
|
|
parse_warn("unexpected end of file"); |
781 |
|
|
break; |
782 |
|
|
} |
783 |
|
|
declaration = parse_statement(cfile, subnet->group, |
784 |
|
|
SUBNET_DECL, NULL, declaration); |
785 |
|
|
} while (1); |
786 |
|
|
|
787 |
|
|
/* |
788 |
|
|
* If this subnet supports dynamic bootp, flag it so in the |
789 |
|
|
* shared_network containing it. |
790 |
|
|
*/ |
791 |
|
|
if (subnet->group->dynamic_bootp) |
792 |
|
|
share->group->dynamic_bootp = 1; |
793 |
|
|
|
794 |
|
|
/* Add the subnet to the list of subnets in this shared net. */ |
795 |
|
|
if (!share->subnets) |
796 |
|
|
share->subnets = subnet; |
797 |
|
|
else { |
798 |
|
|
u = NULL; |
799 |
|
|
for (t = share->subnets; t; t = t->next_sibling) { |
800 |
|
|
if (subnet_inner_than(subnet, t, 0)) { |
801 |
|
|
if (u) |
802 |
|
|
u->next_sibling = subnet; |
803 |
|
|
else |
804 |
|
|
share->subnets = subnet; |
805 |
|
|
subnet->next_sibling = t; |
806 |
|
|
return; |
807 |
|
|
} |
808 |
|
|
u = t; |
809 |
|
|
} |
810 |
|
|
u->next_sibling = subnet; |
811 |
|
|
} |
812 |
|
|
} |
813 |
|
|
|
814 |
|
|
/* |
815 |
|
|
* group-declaration :== RBRACE parameters declarations LBRACE |
816 |
|
|
*/ |
817 |
|
|
void |
818 |
|
|
parse_group_declaration(FILE *cfile, struct group *group) |
819 |
|
|
{ |
820 |
|
|
char *val; |
821 |
|
|
int token; |
822 |
|
|
struct group *g; |
823 |
|
|
int declaration = 0; |
824 |
|
|
|
825 |
|
|
g = clone_group(group, "parse_group_declaration"); |
826 |
|
|
|
827 |
|
|
if (!parse_lbrace(cfile)) { |
828 |
|
|
free(g); |
829 |
|
|
return; |
830 |
|
|
} |
831 |
|
|
|
832 |
|
|
do { |
833 |
|
|
token = peek_token(&val, cfile); |
834 |
|
|
if (token == '}') { |
835 |
|
|
token = next_token(&val, cfile); |
836 |
|
|
break; |
837 |
|
|
} else if (token == EOF) { |
838 |
|
|
token = next_token(&val, cfile); |
839 |
|
|
parse_warn("unexpected end of file"); |
840 |
|
|
break; |
841 |
|
|
} |
842 |
|
|
declaration = parse_statement(cfile, g, GROUP_DECL, NULL, |
843 |
|
|
declaration); |
844 |
|
|
} while (1); |
845 |
|
|
} |
846 |
|
|
|
847 |
|
|
/* |
848 |
|
|
* cidr :== ip-address "/" bit-count |
849 |
|
|
* ip-address :== NUMBER [ DOT NUMBER [ DOT NUMBER [ DOT NUMBER ] ] ] |
850 |
|
|
* bit-count :== 0..32 |
851 |
|
|
*/ |
852 |
|
|
int |
853 |
|
|
parse_cidr(FILE *cfile, unsigned char *addr, unsigned char *prefix) |
854 |
|
|
{ |
855 |
|
|
const char *errstr; |
856 |
|
|
char *val; |
857 |
|
|
int token; |
858 |
|
|
int len = 4; |
859 |
|
|
|
860 |
|
|
token = peek_token(&val, cfile); |
861 |
|
|
|
862 |
|
|
if (!parse_numeric_aggregate(cfile, addr, &len, '.', 10, 8)) { |
863 |
|
|
parse_warn("Expecting CIDR subnet"); |
864 |
|
|
goto nocidr; |
865 |
|
|
} |
866 |
|
|
|
867 |
|
|
token = next_token(&val, cfile); |
868 |
|
|
if (token != '/') { |
869 |
|
|
parse_warn("Expecting '/'"); |
870 |
|
|
goto nocidr; |
871 |
|
|
} |
872 |
|
|
|
873 |
|
|
token = next_token(&val, cfile); |
874 |
|
|
|
875 |
|
|
if (token == TOK_NUMBER_OR_NAME) |
876 |
|
|
*prefix = strtonum(val, 0, 32, &errstr); |
877 |
|
|
|
878 |
|
|
if (token != TOK_NUMBER_OR_NAME || errstr) { |
879 |
|
|
*prefix = 0; |
880 |
|
|
parse_warn("Expecting CIDR prefix length, got '%s'", val); |
881 |
|
|
goto nocidr; |
882 |
|
|
} |
883 |
|
|
|
884 |
|
|
return 1; |
885 |
|
|
|
886 |
|
|
nocidr: |
887 |
|
|
if (token != ';') |
888 |
|
|
skip_to_semi(cfile); |
889 |
|
|
return 0; |
890 |
|
|
} |
891 |
|
|
|
892 |
|
|
/* |
893 |
|
|
* ip-addr-or-hostname :== ip-address | hostname |
894 |
|
|
* ip-address :== NUMBER DOT NUMBER DOT NUMBER DOT NUMBER |
895 |
|
|
* |
896 |
|
|
* Parse an ip address or a hostname. If uniform is zero, put in |
897 |
|
|
* a TREE_LIMIT node to catch hostnames that evaluate to more than |
898 |
|
|
* one IP address. |
899 |
|
|
*/ |
900 |
|
|
struct tree * |
901 |
|
|
parse_ip_addr_or_hostname(FILE *cfile, int uniform) |
902 |
|
|
{ |
903 |
|
|
char *val; |
904 |
|
|
int token; |
905 |
|
|
unsigned char addr[4]; |
906 |
|
|
int len = sizeof addr; |
907 |
|
|
char *name; |
908 |
|
|
struct tree *rv; |
909 |
|
|
struct hostent *h; |
910 |
|
|
|
911 |
|
|
name = NULL; |
912 |
|
|
h = NULL; |
913 |
|
|
|
914 |
|
|
token = peek_token(&val, cfile); |
915 |
|
|
if (is_identifier(token)) { |
916 |
|
|
name = parse_host_name(cfile); |
917 |
|
|
if (name) |
918 |
|
|
h = gethostbyname(name); |
919 |
|
|
if (name && h) { |
920 |
|
|
rv = tree_const(h->h_addr_list[0], h->h_length); |
921 |
|
|
if (!uniform) |
922 |
|
|
rv = tree_limit(rv, 4); |
923 |
|
|
return rv; |
924 |
|
|
} |
925 |
|
|
} |
926 |
|
|
|
927 |
|
|
if (token == TOK_NUMBER_OR_NAME) { |
928 |
|
|
if (!parse_numeric_aggregate(cfile, addr, &len, '.', 10, 8)) { |
929 |
|
|
parse_warn("%s (%d): expecting IP address or hostname", |
930 |
|
|
val, token); |
931 |
|
|
return NULL; |
932 |
|
|
} |
933 |
|
|
rv = tree_const(addr, len); |
934 |
|
|
} else { |
935 |
|
|
if (token != '{' && token != '}') |
936 |
|
|
token = next_token(&val, cfile); |
937 |
|
|
parse_warn("%s (%d): expecting IP address or hostname", |
938 |
|
|
val, token); |
939 |
|
|
if (token != ';') |
940 |
|
|
skip_to_semi(cfile); |
941 |
|
|
return NULL; |
942 |
|
|
} |
943 |
|
|
|
944 |
|
|
return rv; |
945 |
|
|
} |
946 |
|
|
|
947 |
|
|
|
948 |
|
|
/* |
949 |
|
|
* fixed-addr-parameter :== ip-addrs-or-hostnames SEMI |
950 |
|
|
* ip-addrs-or-hostnames :== ip-addr-or-hostname |
951 |
|
|
* | ip-addrs-or-hostnames ip-addr-or-hostname |
952 |
|
|
*/ |
953 |
|
|
struct tree_cache * |
954 |
|
|
parse_fixed_addr_param(FILE *cfile) |
955 |
|
|
{ |
956 |
|
|
char *val; |
957 |
|
|
int token; |
958 |
|
|
struct tree *tree = NULL; |
959 |
|
|
struct tree *tmp; |
960 |
|
|
|
961 |
|
|
do { |
962 |
|
|
tmp = parse_ip_addr_or_hostname(cfile, 0); |
963 |
|
|
if (tree) |
964 |
|
|
tree = tree_concat(tree, tmp); |
965 |
|
|
else |
966 |
|
|
tree = tmp; |
967 |
|
|
token = peek_token(&val, cfile); |
968 |
|
|
if (token == ',') |
969 |
|
|
token = next_token(&val, cfile); |
970 |
|
|
} while (token == ','); |
971 |
|
|
|
972 |
|
|
if (!parse_semi(cfile)) |
973 |
|
|
return NULL; |
974 |
|
|
return tree_cache(tree); |
975 |
|
|
} |
976 |
|
|
|
977 |
|
|
/* |
978 |
|
|
* option_parameter :== identifier DOT identifier <syntax> SEMI |
979 |
|
|
* | identifier <syntax> SEMI |
980 |
|
|
* |
981 |
|
|
* Option syntax is handled specially through format strings, so it |
982 |
|
|
* would be painful to come up with BNF for it. However, it always |
983 |
|
|
* starts as above and ends in a SEMI. |
984 |
|
|
*/ |
985 |
|
|
void |
986 |
|
|
parse_option_param(FILE *cfile, struct group *group) |
987 |
|
|
{ |
988 |
|
|
char *val; |
989 |
|
|
int token; |
990 |
|
|
unsigned char buf[4]; |
991 |
|
|
unsigned char cprefix; |
992 |
|
|
char *vendor; |
993 |
|
|
char *fmt; |
994 |
|
|
struct universe *universe; |
995 |
|
|
struct option *option; |
996 |
|
|
struct tree *tree = NULL; |
997 |
|
|
struct tree *t; |
998 |
|
|
|
999 |
|
|
token = next_token(&val, cfile); |
1000 |
|
|
if (!is_identifier(token)) { |
1001 |
|
|
parse_warn("expecting identifier after option keyword."); |
1002 |
|
|
if (token != ';') |
1003 |
|
|
skip_to_semi(cfile); |
1004 |
|
|
return; |
1005 |
|
|
} |
1006 |
|
|
vendor = strdup(val); |
1007 |
|
|
if (vendor == NULL) |
1008 |
|
|
fatalx("no memory for vendor token."); |
1009 |
|
|
token = peek_token(&val, cfile); |
1010 |
|
|
if (token == '.') { |
1011 |
|
|
/* Go ahead and take the DOT token. */ |
1012 |
|
|
token = next_token(&val, cfile); |
1013 |
|
|
|
1014 |
|
|
/* The next token should be an identifier. */ |
1015 |
|
|
token = next_token(&val, cfile); |
1016 |
|
|
if (!is_identifier(token)) { |
1017 |
|
|
parse_warn("expecting identifier after '.'"); |
1018 |
|
|
if (token != ';') |
1019 |
|
|
skip_to_semi(cfile); |
1020 |
|
|
free(vendor); |
1021 |
|
|
return; |
1022 |
|
|
} |
1023 |
|
|
|
1024 |
|
|
/* |
1025 |
|
|
* Look up the option name hash table for the specified |
1026 |
|
|
* vendor. |
1027 |
|
|
*/ |
1028 |
|
|
universe = ((struct universe *)hash_lookup(&universe_hash, |
1029 |
|
|
(unsigned char *)vendor, 0)); |
1030 |
|
|
/* |
1031 |
|
|
* If it's not there, we can't parse the rest of the |
1032 |
|
|
* declaration. |
1033 |
|
|
*/ |
1034 |
|
|
if (!universe) { |
1035 |
|
|
parse_warn("no vendor named %s.", vendor); |
1036 |
|
|
skip_to_semi(cfile); |
1037 |
|
|
free(vendor); |
1038 |
|
|
return; |
1039 |
|
|
} |
1040 |
|
|
} else { |
1041 |
|
|
/* |
1042 |
|
|
* Use the default hash table, which contains all the |
1043 |
|
|
* standard dhcp option names. |
1044 |
|
|
*/ |
1045 |
|
|
val = vendor; |
1046 |
|
|
universe = &dhcp_universe; |
1047 |
|
|
} |
1048 |
|
|
|
1049 |
|
|
/* Look up the actual option info. */ |
1050 |
|
|
option = (struct option *)hash_lookup(universe->hash, |
1051 |
|
|
(unsigned char *)val, 0); |
1052 |
|
|
|
1053 |
|
|
/* If we didn't get an option structure, it's an undefined option. */ |
1054 |
|
|
if (!option) { |
1055 |
|
|
if (val == vendor) |
1056 |
|
|
parse_warn("no option named %s", val); |
1057 |
|
|
else |
1058 |
|
|
parse_warn("no option named %s for vendor %s", |
1059 |
|
|
val, vendor); |
1060 |
|
|
skip_to_semi(cfile); |
1061 |
|
|
free(vendor); |
1062 |
|
|
return; |
1063 |
|
|
} |
1064 |
|
|
|
1065 |
|
|
/* Free the initial identifier token. */ |
1066 |
|
|
free(vendor); |
1067 |
|
|
|
1068 |
|
|
/* Parse the option data. */ |
1069 |
|
|
do { |
1070 |
|
|
/* |
1071 |
|
|
* Set a flag if this is an array of a simple type (i.e., |
1072 |
|
|
* not an array of pairs of IP addresses, or something |
1073 |
|
|
* like that. |
1074 |
|
|
*/ |
1075 |
|
|
int uniform = option->format[1] == 'A'; |
1076 |
|
|
|
1077 |
|
|
for (fmt = option->format; *fmt; fmt++) { |
1078 |
|
|
if (*fmt == 'A') |
1079 |
|
|
break; |
1080 |
|
|
switch (*fmt) { |
1081 |
|
|
case 'X': |
1082 |
|
|
token = peek_token(&val, cfile); |
1083 |
|
|
if (token == TOK_NUMBER_OR_NAME) { |
1084 |
|
|
do { |
1085 |
|
|
token = next_token |
1086 |
|
|
(&val, cfile); |
1087 |
|
|
if (token != |
1088 |
|
|
TOK_NUMBER_OR_NAME) { |
1089 |
|
|
parse_warn("expecting " |
1090 |
|
|
"hex number."); |
1091 |
|
|
if (token != ';') |
1092 |
|
|
skip_to_semi( |
1093 |
|
|
cfile); |
1094 |
|
|
return; |
1095 |
|
|
} |
1096 |
|
|
convert_num(buf, val, 16, 8); |
1097 |
|
|
tree = tree_concat(tree, |
1098 |
|
|
tree_const(buf, 1)); |
1099 |
|
|
token = peek_token(&val, |
1100 |
|
|
cfile); |
1101 |
|
|
if (token == ':') |
1102 |
|
|
token = |
1103 |
|
|
next_token(&val, |
1104 |
|
|
cfile); |
1105 |
|
|
} while (token == ':'); |
1106 |
|
|
} else if (token == TOK_STRING) { |
1107 |
|
|
token = next_token(&val, cfile); |
1108 |
|
|
tree = tree_concat(tree, |
1109 |
|
|
tree_const((unsigned char *)val, |
1110 |
|
|
strlen(val))); |
1111 |
|
|
} else { |
1112 |
|
|
parse_warn("expecting string %s.", |
1113 |
|
|
"or hexadecimal data"); |
1114 |
|
|
skip_to_semi(cfile); |
1115 |
|
|
return; |
1116 |
|
|
} |
1117 |
|
|
break; |
1118 |
|
|
|
1119 |
|
|
case 't': /* Text string. */ |
1120 |
|
|
token = next_token(&val, cfile); |
1121 |
|
|
if (token != TOK_STRING |
1122 |
|
|
&& !is_identifier(token)) { |
1123 |
|
|
parse_warn("expecting string."); |
1124 |
|
|
if (token != ';') |
1125 |
|
|
skip_to_semi(cfile); |
1126 |
|
|
return; |
1127 |
|
|
} |
1128 |
|
|
tree = tree_concat(tree, |
1129 |
|
|
tree_const((unsigned char *)val, |
1130 |
|
|
strlen(val))); |
1131 |
|
|
break; |
1132 |
|
|
|
1133 |
|
|
case 'I': /* IP address or hostname. */ |
1134 |
|
|
t = parse_ip_addr_or_hostname(cfile, uniform); |
1135 |
|
|
if (!t) |
1136 |
|
|
return; |
1137 |
|
|
tree = tree_concat(tree, t); |
1138 |
|
|
break; |
1139 |
|
|
|
1140 |
|
|
case 'L': /* Unsigned 32-bit integer. */ |
1141 |
|
|
case 'l': /* Signed 32-bit integer. */ |
1142 |
|
|
token = next_token(&val, cfile); |
1143 |
|
|
if (token != TOK_NUMBER && token != |
1144 |
|
|
TOK_NUMBER_OR_NAME) { |
1145 |
|
|
parse_warn("expecting number."); |
1146 |
|
|
if (token != ';') |
1147 |
|
|
skip_to_semi(cfile); |
1148 |
|
|
return; |
1149 |
|
|
} |
1150 |
|
|
convert_num(buf, val, 0, 32); |
1151 |
|
|
tree = tree_concat(tree, tree_const(buf, 4)); |
1152 |
|
|
break; |
1153 |
|
|
case 's': /* Signed 16-bit integer. */ |
1154 |
|
|
case 'S': /* Unsigned 16-bit integer. */ |
1155 |
|
|
token = next_token(&val, cfile); |
1156 |
|
|
if (token != TOK_NUMBER && token != |
1157 |
|
|
TOK_NUMBER_OR_NAME) { |
1158 |
|
|
parse_warn("expecting number."); |
1159 |
|
|
if (token != ';') |
1160 |
|
|
skip_to_semi(cfile); |
1161 |
|
|
return; |
1162 |
|
|
} |
1163 |
|
|
convert_num(buf, val, 0, 16); |
1164 |
|
|
tree = tree_concat(tree, tree_const(buf, 2)); |
1165 |
|
|
break; |
1166 |
|
|
case 'b': /* Signed 8-bit integer. */ |
1167 |
|
|
case 'B': /* Unsigned 8-bit integer. */ |
1168 |
|
|
token = next_token(&val, cfile); |
1169 |
|
|
if (token != TOK_NUMBER && token != |
1170 |
|
|
TOK_NUMBER_OR_NAME) { |
1171 |
|
|
parse_warn("expecting number."); |
1172 |
|
|
if (token != ';') |
1173 |
|
|
skip_to_semi(cfile); |
1174 |
|
|
return; |
1175 |
|
|
} |
1176 |
|
|
convert_num(buf, val, 0, 8); |
1177 |
|
|
tree = tree_concat(tree, tree_const(buf, 1)); |
1178 |
|
|
break; |
1179 |
|
|
case 'f': /* Boolean flag. */ |
1180 |
|
|
token = next_token(&val, cfile); |
1181 |
|
|
if (!is_identifier(token)) { |
1182 |
|
|
parse_warn("expecting identifier."); |
1183 |
|
|
if (token != ';') |
1184 |
|
|
skip_to_semi(cfile); |
1185 |
|
|
return; |
1186 |
|
|
} |
1187 |
|
|
if (!strcasecmp(val, "true") |
1188 |
|
|
|| !strcasecmp(val, "on")) |
1189 |
|
|
buf[0] = 1; |
1190 |
|
|
else if (!strcasecmp(val, "false") |
1191 |
|
|
|| !strcasecmp(val, "off")) |
1192 |
|
|
buf[0] = 0; |
1193 |
|
|
else { |
1194 |
|
|
parse_warn("expecting boolean."); |
1195 |
|
|
if (token != ';') |
1196 |
|
|
skip_to_semi(cfile); |
1197 |
|
|
return; |
1198 |
|
|
} |
1199 |
|
|
tree = tree_concat(tree, tree_const(buf, 1)); |
1200 |
|
|
break; |
1201 |
|
|
case 'C': |
1202 |
|
|
if (!parse_cidr(cfile, buf, &cprefix)) |
1203 |
|
|
return; |
1204 |
|
|
tree = tree_concat(tree, tree_const(&cprefix, |
1205 |
|
|
sizeof(cprefix))); |
1206 |
|
|
if (cprefix > 0) |
1207 |
|
|
tree = tree_concat(tree, tree_const( |
1208 |
|
|
buf, (cprefix + 7) / 8)); |
1209 |
|
|
break; |
1210 |
|
|
default: |
1211 |
|
|
log_warnx("Bad format %c in " |
1212 |
|
|
"parse_option_param.", *fmt); |
1213 |
|
|
skip_to_semi(cfile); |
1214 |
|
|
return; |
1215 |
|
|
} |
1216 |
|
|
} |
1217 |
|
|
if (*fmt == 'A') { |
1218 |
|
|
token = peek_token(&val, cfile); |
1219 |
|
|
if (token == ',') { |
1220 |
|
|
token = next_token(&val, cfile); |
1221 |
|
|
continue; |
1222 |
|
|
} |
1223 |
|
|
break; |
1224 |
|
|
} |
1225 |
|
|
} while (*fmt == 'A'); |
1226 |
|
|
|
1227 |
|
|
token = next_token(&val, cfile); |
1228 |
|
|
if (token != ';') { |
1229 |
|
|
parse_warn("semicolon expected."); |
1230 |
|
|
skip_to_semi(cfile); |
1231 |
|
|
return; |
1232 |
|
|
} |
1233 |
|
|
group->options[option->code] = tree_cache(tree); |
1234 |
|
|
} |
1235 |
|
|
|
1236 |
|
|
/* |
1237 |
|
|
* lease_declaration :== LEASE ip_address LBRACE lease_parameters RBRACE |
1238 |
|
|
* |
1239 |
|
|
* lease_parameters :== <nil> |
1240 |
|
|
* | lease_parameter |
1241 |
|
|
* | lease_parameters lease_parameter |
1242 |
|
|
* |
1243 |
|
|
* lease_parameter :== STARTS date |
1244 |
|
|
* | ENDS date |
1245 |
|
|
* | TIMESTAMP date |
1246 |
|
|
* | HARDWARE hardware-parameter |
1247 |
|
|
* | UID hex_numbers SEMI |
1248 |
|
|
* | HOSTNAME hostname SEMI |
1249 |
|
|
* | CLIENT_HOSTNAME hostname SEMI |
1250 |
|
|
* | CLASS identifier SEMI |
1251 |
|
|
* | DYNAMIC_BOOTP SEMI |
1252 |
|
|
*/ |
1253 |
|
|
struct lease * |
1254 |
|
|
parse_lease_declaration(FILE *cfile) |
1255 |
|
|
{ |
1256 |
|
|
char *val; |
1257 |
|
|
int token; |
1258 |
|
|
unsigned char addr[4]; |
1259 |
|
|
int len = sizeof addr; |
1260 |
|
|
int seenmask = 0; |
1261 |
|
|
int seenbit; |
1262 |
|
|
char tbuf[32]; |
1263 |
|
|
static struct lease lease; |
1264 |
|
|
|
1265 |
|
|
/* Zap the lease structure. */ |
1266 |
|
|
memset(&lease, 0, sizeof lease); |
1267 |
|
|
|
1268 |
|
|
/* Get the address for which the lease has been issued. */ |
1269 |
|
|
if (!parse_numeric_aggregate(cfile, addr, &len, '.', 10, 8)) |
1270 |
|
|
return NULL; |
1271 |
|
|
memcpy(lease.ip_addr.iabuf, addr, len); |
1272 |
|
|
lease.ip_addr.len = len; |
1273 |
|
|
|
1274 |
|
|
if (!parse_lbrace(cfile)) |
1275 |
|
|
return NULL; |
1276 |
|
|
|
1277 |
|
|
do { |
1278 |
|
|
token = next_token(&val, cfile); |
1279 |
|
|
if (token == '}') |
1280 |
|
|
break; |
1281 |
|
|
else if (token == EOF) { |
1282 |
|
|
parse_warn("unexpected end of file"); |
1283 |
|
|
break; |
1284 |
|
|
} |
1285 |
|
|
strlcpy(tbuf, val, sizeof tbuf); |
1286 |
|
|
|
1287 |
|
|
/* Parse any of the times associated with the lease. */ |
1288 |
|
|
if (token == TOK_STARTS || token == TOK_ENDS || token == |
1289 |
|
|
TOK_TIMESTAMP) { |
1290 |
|
|
time_t t; |
1291 |
|
|
t = parse_date(cfile); |
1292 |
|
|
switch (token) { |
1293 |
|
|
case TOK_STARTS: |
1294 |
|
|
seenbit = 1; |
1295 |
|
|
lease.starts = t; |
1296 |
|
|
break; |
1297 |
|
|
|
1298 |
|
|
case TOK_ENDS: |
1299 |
|
|
seenbit = 2; |
1300 |
|
|
lease.ends = t; |
1301 |
|
|
break; |
1302 |
|
|
|
1303 |
|
|
case TOK_TIMESTAMP: |
1304 |
|
|
seenbit = 4; |
1305 |
|
|
lease.timestamp = t; |
1306 |
|
|
break; |
1307 |
|
|
|
1308 |
|
|
default: |
1309 |
|
|
/*NOTREACHED*/ |
1310 |
|
|
seenbit = 0; |
1311 |
|
|
break; |
1312 |
|
|
} |
1313 |
|
|
} else { |
1314 |
|
|
switch (token) { |
1315 |
|
|
/* Colon-separated hexadecimal octets. */ |
1316 |
|
|
case TOK_UID: |
1317 |
|
|
seenbit = 8; |
1318 |
|
|
token = peek_token(&val, cfile); |
1319 |
|
|
if (token == TOK_STRING) { |
1320 |
|
|
token = next_token(&val, cfile); |
1321 |
|
|
lease.uid_len = strlen(val); |
1322 |
|
|
lease.uid = malloc(lease.uid_len); |
1323 |
|
|
if (!lease.uid) { |
1324 |
|
|
log_warnx("no space for uid"); |
1325 |
|
|
return NULL; |
1326 |
|
|
} |
1327 |
|
|
memcpy(lease.uid, val, lease.uid_len); |
1328 |
|
|
parse_semi(cfile); |
1329 |
|
|
} else { |
1330 |
|
|
lease.uid_len = 0; |
1331 |
|
|
lease.uid = |
1332 |
|
|
parse_numeric_aggregate(cfile, |
1333 |
|
|
NULL, &lease.uid_len, ':', 16, 8); |
1334 |
|
|
if (!lease.uid) { |
1335 |
|
|
log_warnx("no space for uid"); |
1336 |
|
|
return NULL; |
1337 |
|
|
} |
1338 |
|
|
if (lease.uid_len == 0) { |
1339 |
|
|
lease.uid = NULL; |
1340 |
|
|
parse_warn("zero-length uid"); |
1341 |
|
|
seenbit = 0; |
1342 |
|
|
break; |
1343 |
|
|
} |
1344 |
|
|
} |
1345 |
|
|
if (!lease.uid) |
1346 |
|
|
fatalx("No memory for lease uid"); |
1347 |
|
|
break; |
1348 |
|
|
|
1349 |
|
|
case TOK_CLASS: |
1350 |
|
|
seenbit = 32; |
1351 |
|
|
token = next_token(&val, cfile); |
1352 |
|
|
if (!is_identifier(token)) { |
1353 |
|
|
if (token != ';') |
1354 |
|
|
skip_to_semi(cfile); |
1355 |
|
|
return NULL; |
1356 |
|
|
} |
1357 |
|
|
/* for now, we aren't using this. */ |
1358 |
|
|
break; |
1359 |
|
|
|
1360 |
|
|
case TOK_HARDWARE: |
1361 |
|
|
seenbit = 64; |
1362 |
|
|
parse_hardware_param(cfile, |
1363 |
|
|
&lease.hardware_addr); |
1364 |
|
|
break; |
1365 |
|
|
|
1366 |
|
|
case TOK_DYNAMIC_BOOTP: |
1367 |
|
|
seenbit = 128; |
1368 |
|
|
lease.flags |= BOOTP_LEASE; |
1369 |
|
|
break; |
1370 |
|
|
|
1371 |
|
|
case TOK_ABANDONED: |
1372 |
|
|
seenbit = 256; |
1373 |
|
|
lease.flags |= ABANDONED_LEASE; |
1374 |
|
|
break; |
1375 |
|
|
|
1376 |
|
|
case TOK_HOSTNAME: |
1377 |
|
|
seenbit = 512; |
1378 |
|
|
token = peek_token(&val, cfile); |
1379 |
|
|
if (token == TOK_STRING) |
1380 |
|
|
lease.hostname = parse_string(cfile); |
1381 |
|
|
else |
1382 |
|
|
lease.hostname = |
1383 |
|
|
parse_host_name(cfile); |
1384 |
|
|
if (!lease.hostname) { |
1385 |
|
|
seenbit = 0; |
1386 |
|
|
return NULL; |
1387 |
|
|
} |
1388 |
|
|
break; |
1389 |
|
|
|
1390 |
|
|
case TOK_CLIENT_HOSTNAME: |
1391 |
|
|
seenbit = 1024; |
1392 |
|
|
token = peek_token(&val, cfile); |
1393 |
|
|
if (token == TOK_STRING) |
1394 |
|
|
lease.client_hostname = |
1395 |
|
|
parse_string(cfile); |
1396 |
|
|
else |
1397 |
|
|
lease.client_hostname = |
1398 |
|
|
parse_host_name(cfile); |
1399 |
|
|
break; |
1400 |
|
|
|
1401 |
|
|
default: |
1402 |
|
|
skip_to_semi(cfile); |
1403 |
|
|
seenbit = 0; |
1404 |
|
|
return NULL; |
1405 |
|
|
} |
1406 |
|
|
|
1407 |
|
|
if (token != TOK_HARDWARE && token != TOK_STRING) { |
1408 |
|
|
token = next_token(&val, cfile); |
1409 |
|
|
if (token != ';') { |
1410 |
|
|
parse_warn("semicolon expected."); |
1411 |
|
|
skip_to_semi(cfile); |
1412 |
|
|
return NULL; |
1413 |
|
|
} |
1414 |
|
|
} |
1415 |
|
|
} |
1416 |
|
|
if (seenmask & seenbit) { |
1417 |
|
|
parse_warn("Too many %s parameters in lease %s\n", |
1418 |
|
|
tbuf, piaddr(lease.ip_addr)); |
1419 |
|
|
} else |
1420 |
|
|
seenmask |= seenbit; |
1421 |
|
|
|
1422 |
|
|
} while (1); |
1423 |
|
|
return &lease; |
1424 |
|
|
} |
1425 |
|
|
|
1426 |
|
|
/* |
1427 |
|
|
* address-range-declaration :== ip-address ip-address SEMI |
1428 |
|
|
* | DYNAMIC_BOOTP ip-address ip-address SEMI |
1429 |
|
|
*/ |
1430 |
|
|
void |
1431 |
|
|
parse_address_range(FILE *cfile, struct subnet *subnet) |
1432 |
|
|
{ |
1433 |
|
|
struct iaddr low, high; |
1434 |
|
|
unsigned char addr[4]; |
1435 |
|
|
int len = sizeof addr, token, dynamic = 0; |
1436 |
|
|
char *val; |
1437 |
|
|
|
1438 |
|
|
if ((token = peek_token(&val, cfile)) == TOK_DYNAMIC_BOOTP) { |
1439 |
|
|
token = next_token(&val, cfile); |
1440 |
|
|
subnet->group->dynamic_bootp = dynamic = 1; |
1441 |
|
|
} |
1442 |
|
|
|
1443 |
|
|
/* Get the bottom address in the range. */ |
1444 |
|
|
if (!parse_numeric_aggregate(cfile, addr, &len, '.', 10, 8)) |
1445 |
|
|
return; |
1446 |
|
|
memcpy(low.iabuf, addr, len); |
1447 |
|
|
low.len = len; |
1448 |
|
|
|
1449 |
|
|
/* Only one address? */ |
1450 |
|
|
token = peek_token(&val, cfile); |
1451 |
|
|
if (token == ';') |
1452 |
|
|
high = low; |
1453 |
|
|
else { |
1454 |
|
|
/* Get the top address in the range. */ |
1455 |
|
|
if (!parse_numeric_aggregate(cfile, addr, &len, '.', 10, 8)) |
1456 |
|
|
return; |
1457 |
|
|
memcpy(high.iabuf, addr, len); |
1458 |
|
|
high.len = len; |
1459 |
|
|
} |
1460 |
|
|
|
1461 |
|
|
token = next_token(&val, cfile); |
1462 |
|
|
if (token != ';') { |
1463 |
|
|
parse_warn("semicolon expected."); |
1464 |
|
|
skip_to_semi(cfile); |
1465 |
|
|
return; |
1466 |
|
|
} |
1467 |
|
|
|
1468 |
|
|
/* Create the new address range. */ |
1469 |
|
|
new_address_range(low, high, subnet, dynamic); |
1470 |
|
|
} |