1 |
|
|
/* $OpenBSD: clparse.c,v 1.129 2017/09/20 19:21:00 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 <sys/queue.h> |
44 |
|
|
#include <sys/socket.h> |
45 |
|
|
#include <sys/types.h> |
46 |
|
|
|
47 |
|
|
#include <net/if.h> |
48 |
|
|
#include <net/if_arp.h> |
49 |
|
|
|
50 |
|
|
#include <netinet/in.h> |
51 |
|
|
#include <netinet/if_ether.h> |
52 |
|
|
|
53 |
|
|
#include <signal.h> |
54 |
|
|
#include <stdio.h> |
55 |
|
|
#include <stdlib.h> |
56 |
|
|
#include <string.h> |
57 |
|
|
|
58 |
|
|
#include "dhcp.h" |
59 |
|
|
#include "dhcpd.h" |
60 |
|
|
#include "dhctoken.h" |
61 |
|
|
#include "log.h" |
62 |
|
|
|
63 |
|
|
void parse_client_statement(FILE *, char *); |
64 |
|
|
int parse_X(FILE *, uint8_t *, int); |
65 |
|
|
int parse_option_list(FILE *, uint8_t *, size_t); |
66 |
|
|
void parse_interface_declaration(FILE *, char *); |
67 |
|
|
struct client_lease *parse_client_lease_statement(FILE *, char *); |
68 |
|
|
void parse_client_lease_declaration(FILE *, struct client_lease *, char *); |
69 |
|
|
int parse_option_decl(FILE *, struct option_data *); |
70 |
|
|
void parse_reject_statement(FILE *); |
71 |
|
|
void add_lease(struct client_lease_tq *, struct client_lease *); |
72 |
|
|
|
73 |
|
|
void |
74 |
|
|
add_lease(struct client_lease_tq *tq, struct client_lease *lease) |
75 |
|
|
{ |
76 |
|
|
struct client_lease *lp, *nlp; |
77 |
|
|
|
78 |
|
|
if (lease == NULL) |
79 |
|
|
return; |
80 |
|
|
|
81 |
|
|
/* |
82 |
|
|
* The new lease will supersede a lease with the same ssid |
83 |
|
|
* AND the same Client Identifier AND the same |
84 |
|
|
* IP address. |
85 |
|
|
*/ |
86 |
|
|
TAILQ_FOREACH_SAFE(lp, tq, next, nlp) { |
87 |
|
|
if (lp->ssid_len != lease->ssid_len) |
88 |
|
|
continue; |
89 |
|
|
if (memcmp(lp->ssid, lease->ssid, lp->ssid_len) != 0) |
90 |
|
|
continue; |
91 |
|
|
if ((lease->options[DHO_DHCP_CLIENT_IDENTIFIER].len != 0) && |
92 |
|
|
((lp->options[DHO_DHCP_CLIENT_IDENTIFIER].len != |
93 |
|
|
lease->options[DHO_DHCP_CLIENT_IDENTIFIER].len) || |
94 |
|
|
memcmp(lp->options[DHO_DHCP_CLIENT_IDENTIFIER].data, |
95 |
|
|
lease->options[DHO_DHCP_CLIENT_IDENTIFIER].data, |
96 |
|
|
lp->options[DHO_DHCP_CLIENT_IDENTIFIER].len))) |
97 |
|
|
continue; |
98 |
|
|
if (lp->address.s_addr != lease->address.s_addr) |
99 |
|
|
continue; |
100 |
|
|
|
101 |
|
|
TAILQ_REMOVE(tq, lp, next); |
102 |
|
|
free_client_lease(lp); |
103 |
|
|
} |
104 |
|
|
|
105 |
|
|
TAILQ_INSERT_TAIL(tq, lease, next); |
106 |
|
|
} |
107 |
|
|
|
108 |
|
|
/* |
109 |
|
|
* client-conf-file :== client-declarations EOF |
110 |
|
|
* client-declarations :== <nil> |
111 |
|
|
* | client-declaration |
112 |
|
|
* | client-declarations client-declaration |
113 |
|
|
*/ |
114 |
|
|
void |
115 |
|
|
read_client_conf(char *name) |
116 |
|
|
{ |
117 |
|
|
FILE *cfile; |
118 |
|
|
int token; |
119 |
|
|
|
120 |
|
|
new_parse(path_dhclient_conf); |
121 |
|
|
|
122 |
|
|
TAILQ_INIT(&config->static_leases); |
123 |
|
|
TAILQ_INIT(&config->reject_list); |
124 |
|
|
|
125 |
|
|
/* Set some defaults. */ |
126 |
|
|
config->link_timeout = 10; /* secs before going daemon w/o link */ |
127 |
|
|
config->timeout = 30; /* secs to wait for an OFFER */ |
128 |
|
|
config->select_interval = 0; /* secs to wait for other OFFERs */ |
129 |
|
|
config->reboot_timeout = 1; /* secs before giving up on reboot */ |
130 |
|
|
config->retry_interval = 1; /* secs before asking for OFFER */ |
131 |
|
|
config->backoff_cutoff = 10; /* max secs between packet retries */ |
132 |
|
|
config->initial_interval = 1; /* secs before 1st retry */ |
133 |
|
|
|
134 |
|
|
config->requested_options |
135 |
|
|
[config->requested_option_count++] = DHO_SUBNET_MASK; |
136 |
|
|
config->requested_options |
137 |
|
|
[config->requested_option_count++] = DHO_BROADCAST_ADDRESS; |
138 |
|
|
config->requested_options |
139 |
|
|
[config->requested_option_count++] = DHO_TIME_OFFSET; |
140 |
|
|
/* RFC 3442 says CLASSLESS_STATIC_ROUTES must be before ROUTERS! */ |
141 |
|
|
config->requested_options |
142 |
|
|
[config->requested_option_count++] = DHO_CLASSLESS_STATIC_ROUTES; |
143 |
|
|
config->requested_options |
144 |
|
|
[config->requested_option_count++] = DHO_ROUTERS; |
145 |
|
|
config->requested_options |
146 |
|
|
[config->requested_option_count++] = DHO_DOMAIN_NAME; |
147 |
|
|
config->requested_options |
148 |
|
|
[config->requested_option_count++] = DHO_DOMAIN_SEARCH; |
149 |
|
|
config->requested_options |
150 |
|
|
[config->requested_option_count++] = DHO_DOMAIN_NAME_SERVERS; |
151 |
|
|
config->requested_options |
152 |
|
|
[config->requested_option_count++] = DHO_HOST_NAME; |
153 |
|
|
config->requested_options |
154 |
|
|
[config->requested_option_count++] = DHO_BOOTFILE_NAME; |
155 |
|
|
config->requested_options |
156 |
|
|
[config->requested_option_count++] = DHO_TFTP_SERVER; |
157 |
|
|
|
158 |
|
|
if ((cfile = fopen(path_dhclient_conf, "r")) != NULL) { |
159 |
|
|
do { |
160 |
|
|
token = peek_token(NULL, cfile); |
161 |
|
|
if (token == EOF) |
162 |
|
|
break; |
163 |
|
|
parse_client_statement(cfile, name); |
164 |
|
|
} while (1); |
165 |
|
|
fclose(cfile); |
166 |
|
|
} |
167 |
|
|
} |
168 |
|
|
|
169 |
|
|
/* |
170 |
|
|
* lease-file :== client-lease-statements EOF |
171 |
|
|
* client-lease-statements :== <nil> |
172 |
|
|
* | client-lease-statements LEASE client-lease-statement |
173 |
|
|
*/ |
174 |
|
|
void |
175 |
|
|
read_client_leases(char *name, struct client_lease_tq *tq) |
176 |
|
|
{ |
177 |
|
|
FILE *cfile; |
178 |
|
|
int token; |
179 |
|
|
|
180 |
|
|
TAILQ_INIT(tq); |
181 |
|
|
|
182 |
|
|
if ((cfile = fopen(path_dhclient_db, "r")) == NULL) |
183 |
|
|
return; |
184 |
|
|
|
185 |
|
|
new_parse(path_dhclient_db); |
186 |
|
|
|
187 |
|
|
do { |
188 |
|
|
token = next_token(NULL, cfile); |
189 |
|
|
if (token == EOF) |
190 |
|
|
break; |
191 |
|
|
if (token != TOK_LEASE) { |
192 |
|
|
log_warnx("%s: expecting lease", log_procname); |
193 |
|
|
break; |
194 |
|
|
} |
195 |
|
|
add_lease(tq, parse_client_lease_statement(cfile, name)); |
196 |
|
|
} while (1); |
197 |
|
|
fclose(cfile); |
198 |
|
|
} |
199 |
|
|
|
200 |
|
|
/* |
201 |
|
|
* client-declaration :== |
202 |
|
|
* TOK_SEND option-decl | |
203 |
|
|
* TOK_DEFAULT option-decl | |
204 |
|
|
* TOK_SUPERSEDE option-decl | |
205 |
|
|
* TOK_APPEND option-decl | |
206 |
|
|
* TOK_PREPEND option-decl | |
207 |
|
|
* TOK_REQUEST option-list | |
208 |
|
|
* TOK_REQUIRE option-list | |
209 |
|
|
* TOK_IGNORE option-list | |
210 |
|
|
* TOK_TIMEOUT number | |
211 |
|
|
* TOK_RETRY number | |
212 |
|
|
* TOK_SELECT_TIMEOUT number | |
213 |
|
|
* TOK_REBOOT number | |
214 |
|
|
* TOK_BACKOFF_CUTOFF number | |
215 |
|
|
* TOK_INITIAL_INTERVAL number | |
216 |
|
|
* interface-declaration | |
217 |
|
|
* TOK_LEASE client-lease-statement | |
218 |
|
|
* TOK_REJECT reject-statement |
219 |
|
|
*/ |
220 |
|
|
void |
221 |
|
|
parse_client_statement(FILE *cfile, char *name) |
222 |
|
|
{ |
223 |
|
|
uint8_t optlist[DHO_COUNT]; |
224 |
|
|
char *string; |
225 |
|
|
int code, count, token; |
226 |
|
|
|
227 |
|
|
token = next_token(NULL, cfile); |
228 |
|
|
|
229 |
|
|
switch (token) { |
230 |
|
|
case TOK_SEND: |
231 |
|
|
parse_option_decl(cfile, &config->send_options[0]); |
232 |
|
|
break; |
233 |
|
|
case TOK_DEFAULT: |
234 |
|
|
code = parse_option_decl(cfile, &config->defaults[0]); |
235 |
|
|
if (code != -1) |
236 |
|
|
config->default_actions[code] = ACTION_DEFAULT; |
237 |
|
|
break; |
238 |
|
|
case TOK_SUPERSEDE: |
239 |
|
|
code = parse_option_decl(cfile, &config->defaults[0]); |
240 |
|
|
if (code != -1) |
241 |
|
|
config->default_actions[code] = ACTION_SUPERSEDE; |
242 |
|
|
break; |
243 |
|
|
case TOK_APPEND: |
244 |
|
|
code = parse_option_decl(cfile, &config->defaults[0]); |
245 |
|
|
if (code != -1) |
246 |
|
|
config->default_actions[code] = ACTION_APPEND; |
247 |
|
|
break; |
248 |
|
|
case TOK_PREPEND: |
249 |
|
|
code = parse_option_decl(cfile, &config->defaults[0]); |
250 |
|
|
if (code != -1) |
251 |
|
|
config->default_actions[code] = ACTION_PREPEND; |
252 |
|
|
break; |
253 |
|
|
case TOK_REQUEST: |
254 |
|
|
count = parse_option_list(cfile, optlist, sizeof(optlist)); |
255 |
|
|
if (count != -1) { |
256 |
|
|
config->requested_option_count = count; |
257 |
|
|
memcpy(config->requested_options, optlist, |
258 |
|
|
sizeof(config->requested_options)); |
259 |
|
|
} |
260 |
|
|
break; |
261 |
|
|
case TOK_REQUIRE: |
262 |
|
|
count = parse_option_list(cfile, optlist, sizeof(optlist)); |
263 |
|
|
if (count != -1) { |
264 |
|
|
config->required_option_count = count; |
265 |
|
|
memcpy(config->required_options, optlist, |
266 |
|
|
sizeof(config->required_options)); |
267 |
|
|
} |
268 |
|
|
break; |
269 |
|
|
case TOK_IGNORE: |
270 |
|
|
count = parse_option_list(cfile, optlist, sizeof(optlist)); |
271 |
|
|
if (count != -1) { |
272 |
|
|
config->ignored_option_count = count; |
273 |
|
|
memcpy(config->ignored_options, optlist, |
274 |
|
|
sizeof(config->ignored_options)); |
275 |
|
|
} |
276 |
|
|
break; |
277 |
|
|
case TOK_LINK_TIMEOUT: |
278 |
|
|
parse_lease_time(cfile, &config->link_timeout); |
279 |
|
|
break; |
280 |
|
|
case TOK_TIMEOUT: |
281 |
|
|
parse_lease_time(cfile, &config->timeout); |
282 |
|
|
break; |
283 |
|
|
case TOK_RETRY: |
284 |
|
|
parse_lease_time(cfile, &config->retry_interval); |
285 |
|
|
break; |
286 |
|
|
case TOK_SELECT_TIMEOUT: |
287 |
|
|
parse_lease_time(cfile, &config->select_interval); |
288 |
|
|
break; |
289 |
|
|
case TOK_REBOOT: |
290 |
|
|
parse_lease_time(cfile, &config->reboot_timeout); |
291 |
|
|
break; |
292 |
|
|
case TOK_BACKOFF_CUTOFF: |
293 |
|
|
parse_lease_time(cfile, &config->backoff_cutoff); |
294 |
|
|
break; |
295 |
|
|
case TOK_INITIAL_INTERVAL: |
296 |
|
|
parse_lease_time(cfile, &config->initial_interval); |
297 |
|
|
break; |
298 |
|
|
case TOK_INTERFACE: |
299 |
|
|
parse_interface_declaration(cfile, name); |
300 |
|
|
break; |
301 |
|
|
case TOK_LEASE: |
302 |
|
|
add_lease(&config->static_leases, |
303 |
|
|
parse_client_lease_statement(cfile, name)); |
304 |
|
|
break; |
305 |
|
|
case TOK_REJECT: |
306 |
|
|
parse_reject_statement(cfile); |
307 |
|
|
break; |
308 |
|
|
case TOK_FILENAME: |
309 |
|
|
string = parse_string(cfile, NULL); |
310 |
|
|
free(config->filename); |
311 |
|
|
config->filename = string; |
312 |
|
|
parse_semi(cfile); |
313 |
|
|
break; |
314 |
|
|
case TOK_SERVER_NAME: |
315 |
|
|
string = parse_string(cfile, NULL); |
316 |
|
|
free(config->server_name); |
317 |
|
|
config->server_name = string; |
318 |
|
|
parse_semi(cfile); |
319 |
|
|
break; |
320 |
|
|
case TOK_FIXED_ADDR: |
321 |
|
|
if (parse_ip_addr(cfile, &config->address) != 0) |
322 |
|
|
parse_semi(cfile); |
323 |
|
|
break; |
324 |
|
|
case TOK_NEXT_SERVER: |
325 |
|
|
if (parse_ip_addr(cfile, &config->next_server) != 0) |
326 |
|
|
parse_semi(cfile); |
327 |
|
|
break; |
328 |
|
|
default: |
329 |
|
|
parse_warn("expecting statement."); |
330 |
|
|
if (token != ';') |
331 |
|
|
skip_to_semi(cfile); |
332 |
|
|
break; |
333 |
|
|
} |
334 |
|
|
} |
335 |
|
|
|
336 |
|
|
int |
337 |
|
|
parse_X(FILE *cfile, uint8_t *buf, int max) |
338 |
|
|
{ |
339 |
|
|
int token; |
340 |
|
|
char *val; |
341 |
|
|
int len; |
342 |
|
|
|
343 |
|
|
token = peek_token(&val, cfile); |
344 |
|
|
if (token == TOK_NUMBER_OR_NAME) { |
345 |
|
|
len = 0; |
346 |
|
|
for (token = ':'; token == ':'; |
347 |
|
|
token = next_token(NULL, cfile)) { |
348 |
|
|
if (parse_hex(cfile, &buf[len]) == 0) |
349 |
|
|
break; |
350 |
|
|
if (++len == max) |
351 |
|
|
break; |
352 |
|
|
if (peek_token(NULL, cfile) == ';') |
353 |
|
|
return len; |
354 |
|
|
} |
355 |
|
|
if (token != ':') { |
356 |
|
|
parse_warn("expecting ':'."); |
357 |
|
|
skip_to_semi(cfile); |
358 |
|
|
return -1; |
359 |
|
|
} else { |
360 |
|
|
parse_warn("expecting hex value."); |
361 |
|
|
skip_to_semi(cfile); |
362 |
|
|
return -1; |
363 |
|
|
} |
364 |
|
|
} else if (token == TOK_STRING) { |
365 |
|
|
token = next_token(&val, cfile); |
366 |
|
|
len = strlen(val); |
367 |
|
|
if (len + 1 > max) { |
368 |
|
|
parse_warn("string constant too long."); |
369 |
|
|
skip_to_semi(cfile); |
370 |
|
|
return -1; |
371 |
|
|
} |
372 |
|
|
memcpy(buf, val, len + 1); |
373 |
|
|
} else { |
374 |
|
|
token = next_token(NULL, cfile); |
375 |
|
|
parse_warn("expecting string or hex data."); |
376 |
|
|
if (token != ';') |
377 |
|
|
skip_to_semi(cfile); |
378 |
|
|
return -1; |
379 |
|
|
} |
380 |
|
|
return len; |
381 |
|
|
} |
382 |
|
|
|
383 |
|
|
/* |
384 |
|
|
* option-list :== option_name | |
385 |
|
|
* option_list COMMA option_name |
386 |
|
|
*/ |
387 |
|
|
int |
388 |
|
|
parse_option_list(FILE *cfile, uint8_t *list, size_t sz) |
389 |
|
|
{ |
390 |
|
|
unsigned int ix, j; |
391 |
|
|
int i; |
392 |
|
|
int token; |
393 |
|
|
char *val; |
394 |
|
|
|
395 |
|
|
memset(list, DHO_PAD, sz); |
396 |
|
|
ix = 0; |
397 |
|
|
do { |
398 |
|
|
token = next_token(&val, cfile); |
399 |
|
|
if (token == ';' && ix == 0) { |
400 |
|
|
/* Empty list. */ |
401 |
|
|
return 0; |
402 |
|
|
} |
403 |
|
|
if (is_identifier(token) == 0) { |
404 |
|
|
parse_warn("expecting option name."); |
405 |
|
|
goto syntaxerror; |
406 |
|
|
} |
407 |
|
|
/* |
408 |
|
|
* 0 (DHO_PAD) and 255 (DHO_END) are not valid in option |
409 |
|
|
* lists. They are not really options and it makes no sense |
410 |
|
|
* to request, require or ignore them. |
411 |
|
|
*/ |
412 |
|
|
|
413 |
|
|
i = name_to_code(val); |
414 |
|
|
if (i == DHO_END) { |
415 |
|
|
parse_warn("expecting option name."); |
416 |
|
|
goto syntaxerror; |
417 |
|
|
} |
418 |
|
|
if (ix == sz) { |
419 |
|
|
parse_warn("too many options."); |
420 |
|
|
goto syntaxerror; |
421 |
|
|
} |
422 |
|
|
/* Avoid storing duplicate options in the list. */ |
423 |
|
|
for (j = 0; j < ix && list[j] != i; j++) |
424 |
|
|
; |
425 |
|
|
if (j == ix) |
426 |
|
|
list[ix++] = i; |
427 |
|
|
token = peek_token(NULL, cfile); |
428 |
|
|
if (token == ',') |
429 |
|
|
token = next_token(NULL, cfile); |
430 |
|
|
} while (token == ','); |
431 |
|
|
|
432 |
|
|
if (parse_semi(cfile) != 0) |
433 |
|
|
return ix; |
434 |
|
|
|
435 |
|
|
syntaxerror: |
436 |
|
|
if (token != ';') |
437 |
|
|
skip_to_semi(cfile); |
438 |
|
|
return -1; |
439 |
|
|
} |
440 |
|
|
|
441 |
|
|
/* |
442 |
|
|
* interface-declaration :== |
443 |
|
|
* INTERFACE string LBRACE client-declarations RBRACE |
444 |
|
|
*/ |
445 |
|
|
void |
446 |
|
|
parse_interface_declaration(FILE *cfile, char *name) |
447 |
|
|
{ |
448 |
|
|
char *val; |
449 |
|
|
int token; |
450 |
|
|
|
451 |
|
|
token = next_token(&val, cfile); |
452 |
|
|
if (token != TOK_STRING) { |
453 |
|
|
parse_warn("expecting string."); |
454 |
|
|
if (token != ';') |
455 |
|
|
skip_to_semi(cfile); |
456 |
|
|
return; |
457 |
|
|
} |
458 |
|
|
|
459 |
|
|
if (strcmp(name, val) != 0) { |
460 |
|
|
skip_to_semi(cfile); |
461 |
|
|
return; |
462 |
|
|
} |
463 |
|
|
|
464 |
|
|
token = next_token(&val, cfile); |
465 |
|
|
if (token != '{') { |
466 |
|
|
parse_warn("expecting '{'."); |
467 |
|
|
if (token != ';') |
468 |
|
|
skip_to_semi(cfile); |
469 |
|
|
return; |
470 |
|
|
} |
471 |
|
|
|
472 |
|
|
do { |
473 |
|
|
token = peek_token(&val, cfile); |
474 |
|
|
if (token == EOF) { |
475 |
|
|
parse_warn("unterminated interface declaration."); |
476 |
|
|
return; |
477 |
|
|
} |
478 |
|
|
if (token == '}') |
479 |
|
|
break; |
480 |
|
|
parse_client_statement(cfile, name); |
481 |
|
|
} while (1); |
482 |
|
|
token = next_token(&val, cfile); |
483 |
|
|
} |
484 |
|
|
|
485 |
|
|
/* |
486 |
|
|
* client-lease-statement :== |
487 |
|
|
* RBRACE client-lease-declarations LBRACE |
488 |
|
|
* |
489 |
|
|
* client-lease-declarations :== |
490 |
|
|
* <nil> | |
491 |
|
|
* client-lease-declaration | |
492 |
|
|
* client-lease-declarations client-lease-declaration |
493 |
|
|
*/ |
494 |
|
|
struct client_lease * |
495 |
|
|
parse_client_lease_statement(FILE *cfile, char *name) |
496 |
|
|
{ |
497 |
|
|
struct client_lease *lease; |
498 |
|
|
int token; |
499 |
|
|
|
500 |
|
|
token = next_token(NULL, cfile); |
501 |
|
|
if (token != '{') { |
502 |
|
|
parse_warn("expecting '{'."); |
503 |
|
|
if (token != ';') |
504 |
|
|
skip_to_semi(cfile); |
505 |
|
|
return NULL; |
506 |
|
|
} |
507 |
|
|
|
508 |
|
|
lease = calloc(1, sizeof(*lease)); |
509 |
|
|
if (lease == NULL) |
510 |
|
|
fatal("lease"); |
511 |
|
|
|
512 |
|
|
do { |
513 |
|
|
token = peek_token(NULL, cfile); |
514 |
|
|
if (token == EOF) { |
515 |
|
|
parse_warn("unterminated lease declaration."); |
516 |
|
|
free_client_lease(lease); |
517 |
|
|
return NULL; |
518 |
|
|
} |
519 |
|
|
if (token == '}') |
520 |
|
|
break; |
521 |
|
|
parse_client_lease_declaration(cfile, lease, name); |
522 |
|
|
} while (1); |
523 |
|
|
token = next_token(NULL, cfile); |
524 |
|
|
|
525 |
|
|
return lease; |
526 |
|
|
} |
527 |
|
|
|
528 |
|
|
/* |
529 |
|
|
* client-lease-declaration :== |
530 |
|
|
* BOOTP | |
531 |
|
|
* INTERFACE string | |
532 |
|
|
* FIXED_ADDR ip_address | |
533 |
|
|
* FILENAME string | |
534 |
|
|
* SERVER_NAME string | |
535 |
|
|
* OPTION option-decl | |
536 |
|
|
* RENEW time-decl | |
537 |
|
|
* REBIND time-decl | |
538 |
|
|
* EXPIRE time-decl |
539 |
|
|
*/ |
540 |
|
|
void |
541 |
|
|
parse_client_lease_declaration(FILE *cfile, struct client_lease *lease, |
542 |
|
|
char *name) |
543 |
|
|
{ |
544 |
|
|
char *val; |
545 |
|
|
unsigned int len; |
546 |
|
|
int token; |
547 |
|
|
|
548 |
|
|
token = next_token(&val, cfile); |
549 |
|
|
|
550 |
|
|
switch (token) { |
551 |
|
|
case TOK_BOOTP: |
552 |
|
|
/* 'bootp' is just a comment. See BOOTP_LEASE(). */ |
553 |
|
|
break; |
554 |
|
|
case TOK_INTERFACE: |
555 |
|
|
token = next_token(&val, cfile); |
556 |
|
|
if (token != TOK_STRING) { |
557 |
|
|
parse_warn("expecting string."); |
558 |
|
|
if (token != ';') |
559 |
|
|
skip_to_semi(cfile); |
560 |
|
|
return; |
561 |
|
|
} |
562 |
|
|
if (strcmp(name, val) != 0) { |
563 |
|
|
if (lease->is_static == 0) |
564 |
|
|
parse_warn("wrong interface name."); |
565 |
|
|
skip_to_semi(cfile); |
566 |
|
|
return; |
567 |
|
|
} |
568 |
|
|
break; |
569 |
|
|
case TOK_FIXED_ADDR: |
570 |
|
|
if (parse_ip_addr(cfile, &lease->address) == 0) |
571 |
|
|
return; |
572 |
|
|
break; |
573 |
|
|
case TOK_NEXT_SERVER: |
574 |
|
|
if (parse_ip_addr(cfile, &lease->next_server) == 0) |
575 |
|
|
return; |
576 |
|
|
break; |
577 |
|
|
case TOK_FILENAME: |
578 |
|
|
lease->filename = parse_string(cfile, NULL); |
579 |
|
|
break; |
580 |
|
|
case TOK_SERVER_NAME: |
581 |
|
|
lease->server_name = parse_string(cfile, NULL); |
582 |
|
|
break; |
583 |
|
|
case TOK_SSID: |
584 |
|
|
val = parse_string(cfile, &len); |
585 |
|
|
if (val && len <= sizeof(lease->ssid)) { |
586 |
|
|
memset(lease->ssid, 0, sizeof(lease->ssid)); |
587 |
|
|
memcpy(lease->ssid, val, len); |
588 |
|
|
lease->ssid_len = len; |
589 |
|
|
} |
590 |
|
|
free(val); |
591 |
|
|
break; |
592 |
|
|
case TOK_RENEW: |
593 |
|
|
lease->renewal = parse_date(cfile); |
594 |
|
|
return; |
595 |
|
|
case TOK_REBIND: |
596 |
|
|
lease->rebind = parse_date(cfile); |
597 |
|
|
return; |
598 |
|
|
case TOK_EXPIRE: |
599 |
|
|
lease->expiry = parse_date(cfile); |
600 |
|
|
return; |
601 |
|
|
case TOK_OPTION: |
602 |
|
|
parse_option_decl(cfile, lease->options); |
603 |
|
|
return; |
604 |
|
|
default: |
605 |
|
|
parse_warn("expecting lease declaration."); |
606 |
|
|
if (token != ';') |
607 |
|
|
skip_to_semi(cfile); |
608 |
|
|
return; |
609 |
|
|
} |
610 |
|
|
|
611 |
|
|
parse_semi(cfile); |
612 |
|
|
} |
613 |
|
|
|
614 |
|
|
int |
615 |
|
|
parse_option_decl(FILE *cfile, struct option_data *options) |
616 |
|
|
{ |
617 |
|
|
char *val; |
618 |
|
|
int token; |
619 |
|
|
uint8_t buf[4]; |
620 |
|
|
uint8_t cidr[5]; |
621 |
|
|
uint8_t hunkbuf[1024]; |
622 |
|
|
unsigned int hunkix = 0; |
623 |
|
|
char *fmt; |
624 |
|
|
struct in_addr ip_addr; |
625 |
|
|
uint8_t *dp; |
626 |
|
|
int len, code; |
627 |
|
|
int nul_term = 0; |
628 |
|
|
|
629 |
|
|
token = next_token(&val, cfile); |
630 |
|
|
if (is_identifier(token) == 0) { |
631 |
|
|
parse_warn("expecting identifier."); |
632 |
|
|
if (token != ';') |
633 |
|
|
skip_to_semi(cfile); |
634 |
|
|
return -1; |
635 |
|
|
} |
636 |
|
|
|
637 |
|
|
/* Look up the actual option info. */ |
638 |
|
|
code = name_to_code(val); |
639 |
|
|
if (code == DHO_END) { |
640 |
|
|
parse_warn("unknown option name."); |
641 |
|
|
skip_to_semi(cfile); |
642 |
|
|
return -1; |
643 |
|
|
} |
644 |
|
|
|
645 |
|
|
/* Parse the option data. */ |
646 |
|
|
do { |
647 |
|
|
for (fmt = code_to_format(code); *fmt; fmt++) { |
648 |
|
|
if (*fmt == 'A') |
649 |
|
|
break; |
650 |
|
|
switch (*fmt) { |
651 |
|
|
case 'X': |
652 |
|
|
len = parse_X(cfile, &hunkbuf[hunkix], |
653 |
|
|
sizeof(hunkbuf) - hunkix); |
654 |
|
|
if (len == -1) |
655 |
|
|
return -1; |
656 |
|
|
hunkix += len; |
657 |
|
|
dp = NULL; |
658 |
|
|
break; |
659 |
|
|
case 't': /* Text string. */ |
660 |
|
|
val = parse_string(cfile, &len); |
661 |
|
|
if (val == NULL) |
662 |
|
|
return -1; |
663 |
|
|
if (hunkix + len + 1 > sizeof(hunkbuf)) { |
664 |
|
|
parse_warn("option data buffer " |
665 |
|
|
"overflow"); |
666 |
|
|
skip_to_semi(cfile); |
667 |
|
|
return -1; |
668 |
|
|
} |
669 |
|
|
memcpy(&hunkbuf[hunkix], val, len + 1); |
670 |
|
|
nul_term = 1; |
671 |
|
|
hunkix += len; |
672 |
|
|
free(val); |
673 |
|
|
dp = NULL; |
674 |
|
|
break; |
675 |
|
|
case 'I': /* IP address. */ |
676 |
|
|
if (parse_ip_addr(cfile, &ip_addr) == 0) |
677 |
|
|
return -1; |
678 |
|
|
len = sizeof(ip_addr); |
679 |
|
|
dp = (uint8_t *)&ip_addr; |
680 |
|
|
break; |
681 |
|
|
case 'l': /* Signed 32-bit integer. */ |
682 |
|
|
if (parse_decimal(cfile, buf, 'l') == 0) { |
683 |
|
|
parse_warn("expecting signed 32-bit " |
684 |
|
|
"integer."); |
685 |
|
|
skip_to_semi(cfile); |
686 |
|
|
return -1; |
687 |
|
|
} |
688 |
|
|
len = 4; |
689 |
|
|
dp = buf; |
690 |
|
|
break; |
691 |
|
|
case 'L': /* Unsigned 32-bit integer. */ |
692 |
|
|
if (parse_decimal(cfile, buf, 'L') == 0) { |
693 |
|
|
parse_warn("expecting unsigned 32-bit " |
694 |
|
|
"integer."); |
695 |
|
|
skip_to_semi(cfile); |
696 |
|
|
return -1; |
697 |
|
|
} |
698 |
|
|
len = 4; |
699 |
|
|
dp = buf; |
700 |
|
|
break; |
701 |
|
|
case 'S': /* Unsigned 16-bit integer. */ |
702 |
|
|
if (parse_decimal(cfile, buf, 'S') == 0) { |
703 |
|
|
parse_warn("expecting unsigned 16-bit " |
704 |
|
|
"integer."); |
705 |
|
|
skip_to_semi(cfile); |
706 |
|
|
return -1; |
707 |
|
|
} |
708 |
|
|
len = 2; |
709 |
|
|
dp = buf; |
710 |
|
|
break; |
711 |
|
|
case 'B': /* Unsigned 8-bit integer. */ |
712 |
|
|
if (parse_decimal(cfile, buf, 'B') == 0) { |
713 |
|
|
parse_warn("expecting unsigned 8-bit " |
714 |
|
|
"integer."); |
715 |
|
|
skip_to_semi(cfile); |
716 |
|
|
return -1; |
717 |
|
|
} |
718 |
|
|
len = 1; |
719 |
|
|
dp = buf; |
720 |
|
|
break; |
721 |
|
|
case 'f': /* Boolean flag. */ |
722 |
|
|
if (parse_boolean(cfile, buf) == 0) { |
723 |
|
|
parse_warn("expecting boolean."); |
724 |
|
|
skip_to_semi(cfile); |
725 |
|
|
return -1; |
726 |
|
|
} |
727 |
|
|
len = 1; |
728 |
|
|
dp = buf; |
729 |
|
|
break; |
730 |
|
|
case 'C': |
731 |
|
|
if (parse_cidr(cfile, cidr) == 0) |
732 |
|
|
return -1; |
733 |
|
|
len = 1 + (cidr[0] + 7) / 8; |
734 |
|
|
dp = cidr; |
735 |
|
|
break; |
736 |
|
|
default: |
737 |
|
|
log_warnx("%s: bad format %c in " |
738 |
|
|
"parse_option_param", log_procname, *fmt); |
739 |
|
|
skip_to_semi(cfile); |
740 |
|
|
return -1; |
741 |
|
|
} |
742 |
|
|
if (dp != NULL && len > 0) { |
743 |
|
|
if (hunkix + len > sizeof(hunkbuf)) { |
744 |
|
|
parse_warn("option data buffer " |
745 |
|
|
"overflow"); |
746 |
|
|
skip_to_semi(cfile); |
747 |
|
|
return -1; |
748 |
|
|
} |
749 |
|
|
memcpy(&hunkbuf[hunkix], dp, len); |
750 |
|
|
hunkix += len; |
751 |
|
|
} |
752 |
|
|
} |
753 |
|
|
token = peek_token(NULL, cfile); |
754 |
|
|
if (*fmt == 'A' && token == ',') |
755 |
|
|
token = next_token(NULL, cfile); |
756 |
|
|
} while (*fmt == 'A' && token == ','); |
757 |
|
|
|
758 |
|
|
if (parse_semi(cfile) == 0) |
759 |
|
|
return -1; |
760 |
|
|
|
761 |
|
|
options[code].data = malloc(hunkix + nul_term); |
762 |
|
|
if (options[code].data == NULL) |
763 |
|
|
fatal("option data"); |
764 |
|
|
memcpy(options[code].data, hunkbuf, hunkix + nul_term); |
765 |
|
|
options[code].len = hunkix; |
766 |
|
|
return code; |
767 |
|
|
} |
768 |
|
|
|
769 |
|
|
void |
770 |
|
|
parse_reject_statement(FILE *cfile) |
771 |
|
|
{ |
772 |
|
|
struct in_addr addr; |
773 |
|
|
struct reject_elem *elem; |
774 |
|
|
int token; |
775 |
|
|
|
776 |
|
|
do { |
777 |
|
|
if (parse_ip_addr(cfile, &addr) == 0) |
778 |
|
|
return; |
779 |
|
|
|
780 |
|
|
elem = malloc(sizeof(*elem)); |
781 |
|
|
if (elem == NULL) |
782 |
|
|
fatal("reject address"); |
783 |
|
|
|
784 |
|
|
elem->addr = addr; |
785 |
|
|
TAILQ_INSERT_TAIL(&config->reject_list, elem, next); |
786 |
|
|
|
787 |
|
|
token = peek_token(NULL, cfile); |
788 |
|
|
if (token == ',') |
789 |
|
|
token = next_token(NULL, cfile); |
790 |
|
|
} while (token == ','); |
791 |
|
|
|
792 |
|
|
parse_semi(cfile); |
793 |
|
|
} |