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