File: | src/sbin/dhclient/clparse.c |
Warning: | line 510, column 12 The left operand of '!=' is a garbage value |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* $OpenBSD: clparse.c,v 1.203 2021/02/27 17:44:58 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/stat.h> | |||
46 | #include <sys/types.h> | |||
47 | ||||
48 | #include <net/if.h> | |||
49 | #include <net/if_arp.h> | |||
50 | ||||
51 | #include <netinet/in.h> | |||
52 | #include <netinet/if_ether.h> | |||
53 | ||||
54 | #include <err.h> | |||
55 | #include <errno(*__errno()).h> | |||
56 | #include <fcntl.h> | |||
57 | #include <limits.h> | |||
58 | #include <signal.h> | |||
59 | #include <stdio.h> | |||
60 | #include <stdint.h> | |||
61 | #include <stdlib.h> | |||
62 | #include <string.h> | |||
63 | #include <unistd.h> | |||
64 | ||||
65 | #include "dhcp.h" | |||
66 | #include "dhcpd.h" | |||
67 | #include "dhctoken.h" | |||
68 | #include "log.h" | |||
69 | ||||
70 | void parse_conf_decl(FILE *, char *); | |||
71 | int parse_hex_octets(FILE *, unsigned int *, uint8_t **); | |||
72 | int parse_domain_list(FILE *, int *, char **); | |||
73 | int parse_option_list(FILE *, int *, uint8_t *); | |||
74 | int parse_interface(FILE *, char *); | |||
75 | int parse_lease(FILE *, struct client_lease **); | |||
76 | void parse_lease_decl(FILE *, struct client_lease *); | |||
77 | int parse_option(FILE *, int *, struct option_data *); | |||
78 | int parse_reject_statement(FILE *); | |||
79 | ||||
80 | void apply_actions(uint8_t *); | |||
81 | void set_default_client_identifier(struct ether_addr *); | |||
82 | void set_default_hostname(void); | |||
83 | ||||
84 | void | |||
85 | init_config(void) | |||
86 | { | |||
87 | struct option_data *option; | |||
88 | uint32_t expiry; | |||
89 | ||||
90 | config = calloc(1, sizeof(*config)); | |||
91 | if (config == NULL((void *)0)) | |||
92 | fatal("config"); | |||
93 | ||||
94 | TAILQ_INIT(&config->reject_list)do { (&config->reject_list)->tqh_first = ((void *)0 ); (&config->reject_list)->tqh_last = &(&config ->reject_list)->tqh_first; } while (0); | |||
95 | ||||
96 | /* Set some defaults. */ | |||
97 | config->link_interval = 10; /* secs before going daemon w/o lease */ | |||
98 | config->select_interval = 0; /* secs to wait for other OFFERs */ | |||
99 | config->retry_interval = 1; /* secs before asking for OFFER */ | |||
100 | #ifdef SMALL | |||
101 | config->backoff_cutoff = 2; /* max secs between packet retries */ | |||
102 | config->reboot_interval = 5; /* secs before giving up on reboot */ | |||
103 | config->offer_interval = 10; /* secs to wait for an OFFER */ | |||
104 | #else | |||
105 | config->backoff_cutoff = 10; /* max secs between packet retries */ | |||
106 | config->reboot_interval = 1; /* secs before giving up on reboot */ | |||
107 | config->offer_interval = 30; /* secs to wait for an OFFER */ | |||
108 | #endif | |||
109 | config->initial_interval = 1; /* secs before 1st retry */ | |||
110 | ||||
111 | /* All leases must supply a subnet mask. Classful defaults are dead, Jim. */ | |||
112 | config->required_options[config->required_option_count++] = DHO_SUBNET_MASK1; | |||
113 | ||||
114 | /* | |||
115 | * Set default lease length, which will determine default renewal | |||
116 | * and rebind times. | |||
117 | * | |||
118 | * XXX Thus applies to both BOOTP and DHCP leases. | |||
119 | * | |||
120 | * DHO_DHCP_LEASE_TIME (12 hours == 43200 seconds), | |||
121 | */ | |||
122 | option = &config->defaults[DHO_DHCP_LEASE_TIME51]; | |||
123 | option->data = malloc(4); | |||
124 | if (option->data == NULL((void *)0)) | |||
125 | fatal("default lease length"); | |||
126 | ||||
127 | config->default_actions[DHO_DHCP_LEASE_TIME51] = ACTION_DEFAULT; | |||
128 | option->len = 4; | |||
129 | expiry = htonl(43200)(__uint32_t)(__builtin_constant_p(43200) ? (__uint32_t)(((__uint32_t )(43200) & 0xff) << 24 | ((__uint32_t)(43200) & 0xff00) << 8 | ((__uint32_t)(43200) & 0xff0000) >> 8 | ((__uint32_t)(43200) & 0xff000000) >> 24) : __swap32md (43200)); | |||
130 | memcpy(option->data, &expiry, 4); | |||
131 | ||||
132 | config->requested_options | |||
133 | [config->requested_option_count++] = DHO_SUBNET_MASK1; | |||
134 | config->requested_options | |||
135 | [config->requested_option_count++] = DHO_BROADCAST_ADDRESS28; | |||
136 | config->requested_options | |||
137 | [config->requested_option_count++] = DHO_TIME_OFFSET2; | |||
138 | /* RFC 3442 says CLASSLESS_STATIC_ROUTES must be before ROUTERS! */ | |||
139 | config->requested_options | |||
140 | [config->requested_option_count++] = DHO_CLASSLESS_STATIC_ROUTES121; | |||
141 | config->requested_options | |||
142 | [config->requested_option_count++] = DHO_ROUTERS3; | |||
143 | config->requested_options | |||
144 | [config->requested_option_count++] = DHO_DOMAIN_NAME15; | |||
145 | config->requested_options | |||
146 | [config->requested_option_count++] = DHO_DOMAIN_SEARCH119; | |||
147 | config->requested_options | |||
148 | [config->requested_option_count++] = DHO_DOMAIN_NAME_SERVERS6; | |||
149 | config->requested_options | |||
150 | [config->requested_option_count++] = DHO_HOST_NAME12; | |||
151 | config->requested_options | |||
152 | [config->requested_option_count++] = DHO_BOOTFILE_NAME67; | |||
153 | config->requested_options | |||
154 | [config->requested_option_count++] = DHO_TFTP_SERVER66; | |||
155 | } | |||
156 | ||||
157 | /* | |||
158 | * conf-decls :== | |||
159 | * <nil> | |||
160 | * | conf-decl | |||
161 | * | conf-decls conf-decl | |||
162 | */ | |||
163 | void | |||
164 | read_conf(char *name, uint8_t *actions, struct ether_addr *hwaddr) | |||
165 | { | |||
166 | FILE *cfile; | |||
167 | int token; | |||
168 | ||||
169 | init_config(); | |||
170 | ||||
171 | if (path_dhclient_conf != NULL((void *)0)) { | |||
172 | cfile = fopen(path_dhclient_conf, "r"); | |||
173 | if (cfile == NULL((void *)0)) | |||
174 | fatal("fopen(%s)", path_dhclient_conf); | |||
175 | new_parse(path_dhclient_conf); | |||
176 | for (;;) { | |||
177 | token = peek_token(NULL((void *)0), cfile); | |||
178 | if (token == EOF(-1)) | |||
179 | break; | |||
180 | parse_conf_decl(cfile, name); | |||
181 | } | |||
182 | fclose(cfile); | |||
183 | } | |||
184 | ||||
185 | set_default_client_identifier(hwaddr); | |||
186 | set_default_hostname(); | |||
187 | apply_actions(actions); | |||
188 | } | |||
189 | ||||
190 | /* | |||
191 | * leases :== | |||
192 | * <nil> | |||
193 | * | lease | |||
194 | * | leases lease | |||
195 | */ | |||
196 | void | |||
197 | read_lease_db(struct client_lease_tq *lease_db) | |||
198 | { | |||
199 | struct client_lease *lease, *lp, *nlp; | |||
200 | FILE *cfile; | |||
201 | int i; | |||
202 | ||||
203 | TAILQ_INIT(lease_db)do { (lease_db)->tqh_first = ((void *)0); (lease_db)->tqh_last = &(lease_db)->tqh_first; } while (0); | |||
| ||||
204 | ||||
205 | if ((cfile = fopen(path_lease_db, "r")) == NULL((void *)0)) | |||
206 | return; | |||
207 | ||||
208 | new_parse(path_lease_db); | |||
209 | ||||
210 | i = DHO_DHCP_CLIENT_IDENTIFIER61; | |||
211 | while (feof(cfile)(!__isthreaded ? (((cfile)->_flags & 0x0020) != 0) : ( feof)(cfile)) == 0) { | |||
212 | if (parse_lease(cfile, &lease) == 0) | |||
213 | continue; | |||
214 | ||||
215 | /* | |||
216 | * The new lease will supersede a lease with the same | |||
217 | * ssid AND the same Client Identifier AND the same | |||
218 | * IP address. | |||
219 | */ | |||
220 | TAILQ_FOREACH_SAFE(lp, lease_db, next, nlp)for ((lp) = ((lease_db)->tqh_first); (lp) != ((void *)0) && ((nlp) = ((lp)->next.tqe_next), 1); (lp) = (nlp)) { | |||
221 | if (lp->ssid_len != lease->ssid_len) | |||
222 | continue; | |||
223 | if (memcmp(lp->ssid, lease->ssid, lp->ssid_len) != 0) | |||
224 | continue; | |||
225 | if ((lease->options[i].len != 0) && | |||
226 | ((lp->options[i].len != lease->options[i].len) || | |||
227 | memcmp(lp->options[i].data, lease->options[i].data, | |||
228 | lp->options[i].len) != 0)) | |||
229 | continue; | |||
230 | if (lp->address.s_addr != lease->address.s_addr) | |||
231 | continue; | |||
232 | ||||
233 | TAILQ_REMOVE(lease_db, lp, next)do { if (((lp)->next.tqe_next) != ((void *)0)) (lp)->next .tqe_next->next.tqe_prev = (lp)->next.tqe_prev; else (lease_db )->tqh_last = (lp)->next.tqe_prev; *(lp)->next.tqe_prev = (lp)->next.tqe_next; ; ; } while (0); | |||
234 | free_client_lease(lp); | |||
235 | } | |||
236 | ||||
237 | if (lease->epoch == 0) | |||
238 | time(&lease->epoch); | |||
239 | TAILQ_INSERT_TAIL(lease_db, lease, next)do { (lease)->next.tqe_next = ((void *)0); (lease)->next .tqe_prev = (lease_db)->tqh_last; *(lease_db)->tqh_last = (lease); (lease_db)->tqh_last = &(lease)->next.tqe_next ; } while (0); | |||
240 | } | |||
241 | ||||
242 | fclose(cfile); | |||
243 | } | |||
244 | ||||
245 | /* | |||
246 | * conf-decl :== | |||
247 | * APPEND option SEMI | |||
248 | * | BACKOFF_CUTOFF number SEMI | |||
249 | * | DEFAULT option SEMI | |||
250 | * | FILENAME string SEMI | |||
251 | * | FIXED_ADDR ip-address SEMI | |||
252 | * | IGNORE option-name-list SEMI | |||
253 | * | INITIAL_INTERVAL number SEMI | |||
254 | * | INTERFACE interface | |||
255 | * | LINK_TIMEOUT number SEMI | |||
256 | * | NEXT_SERVER string SEMI | |||
257 | * | PREPEND option SEMI | |||
258 | * | REBOOT number SEMI | |||
259 | * | REJECT ip-address SEMI | |||
260 | * | REQUEST option-name-list SEMI | |||
261 | * | REQUIRE option-name-list SEMI | |||
262 | * | RETRY number SEMI | |||
263 | * | SELECT_TIMEOUT number SEMI | |||
264 | * | SEND option SEMI | |||
265 | * | SERVER_NAME string SEMI | |||
266 | * | SUPERSEDE option SEMI | |||
267 | * | TIMEOUT number SEMI | |||
268 | */ | |||
269 | void | |||
270 | parse_conf_decl(FILE *cfile, char *name) | |||
271 | { | |||
272 | uint8_t list[DHO_COUNT256]; | |||
273 | char *val; | |||
274 | enum actions *p; | |||
275 | int action, count, i, token; | |||
276 | ||||
277 | token = next_token(NULL((void *)0), cfile); | |||
278 | ||||
279 | switch (token) { | |||
280 | case TOK_APPEND290: | |||
281 | if (parse_option(cfile, &i, config->defaults) == 0) | |||
282 | return; | |||
283 | action = code_to_action(i, ACTION_APPEND); | |||
284 | if (action == ACTION_DEFAULT) | |||
285 | parse_warn("'append' treated as 'default'"); | |||
286 | config->default_actions[i] = action; | |||
287 | break; | |||
288 | case TOK_BACKOFF_CUTOFF287: | |||
289 | if (parse_number(cfile, &config->backoff_cutoff, 0, INT32_MAX0x7fffffff) | |||
290 | == 0) | |||
291 | return; | |||
292 | break; | |||
293 | case TOK_DEFAULT282: | |||
294 | if (parse_option(cfile, &i, config->defaults) == 0) | |||
295 | return; | |||
296 | config->default_actions[i] = ACTION_DEFAULT; | |||
297 | break; | |||
298 | case TOK_FILENAME257: | |||
299 | if (parse_string(cfile, &val) == 0) | |||
300 | return; | |||
301 | free(config->filename); | |||
302 | config->filename = val; | |||
303 | break; | |||
304 | case TOK_FIXED_ADDR259: | |||
305 | if (parse_ip_addr(cfile, &config->address) == 0) | |||
306 | return; | |||
307 | break; | |||
308 | case TOK_IGNORE295: | |||
309 | memset(list, 0, sizeof(list)); | |||
310 | count = 0; | |||
311 | if (parse_option_list(cfile, &count, list) == 0) | |||
312 | return; | |||
313 | p = config->default_actions; | |||
314 | if (count == 0) { | |||
315 | for (i = 0; i < DHO_COUNT256; i++) | |||
316 | if (p[i] == ACTION_IGNORE) | |||
317 | p[i] = ACTION_USELEASE; | |||
318 | } else { | |||
319 | for (i = 0; i < count; i++) | |||
320 | p[list[i]] = ACTION_IGNORE; | |||
321 | } | |||
322 | break; | |||
323 | case TOK_INITIAL_INTERVAL288: | |||
324 | if (parse_number(cfile, &config->initial_interval, 0, INT32_MAX0x7fffffff) | |||
325 | == 0) | |||
326 | return; | |||
327 | break; | |||
328 | case TOK_INTERFACE276: | |||
329 | parse_interface(cfile, name); | |||
330 | return; | |||
331 | case TOK_LINK_TIMEOUT294: | |||
332 | if (parse_number(cfile, &config->link_interval, 0, INT32_MAX0x7fffffff) | |||
333 | == 0) | |||
334 | return; | |||
335 | break; | |||
336 | case TOK_NEXT_SERVER275: | |||
337 | if (parse_ip_addr(cfile, &config->next_server) == 0) | |||
338 | return; | |||
339 | break; | |||
340 | case TOK_PREPEND291: | |||
341 | if (parse_option(cfile, &i, config->defaults) == 0) | |||
342 | return; | |||
343 | action = code_to_action(i, ACTION_PREPEND); | |||
344 | if (action == ACTION_SUPERSEDE) | |||
345 | parse_warn("'prepend' treated as 'supersede'"); | |||
346 | config->default_actions[i] = action; | |||
347 | break; | |||
348 | case TOK_REBOOT286: | |||
349 | if (parse_number(cfile, &config->reboot_interval, 0, INT32_MAX0x7fffffff) | |||
350 | == 0) | |||
351 | return; | |||
352 | break; | |||
353 | case TOK_REJECT292: | |||
354 | if (parse_reject_statement(cfile) == 0) | |||
355 | return; | |||
356 | break; | |||
357 | case TOK_REQUEST270: | |||
358 | if (parse_option_list(cfile, &config->requested_option_count, | |||
359 | config->requested_options) == 0) | |||
360 | return; | |||
361 | break; | |||
362 | case TOK_REQUIRE271: | |||
363 | if (parse_option_list(cfile, &config->required_option_count, | |||
364 | config->required_options) == 0) | |||
365 | return; | |||
366 | break; | |||
367 | case TOK_RETRY273: | |||
368 | if (parse_number(cfile, &config->retry_interval, 0, INT32_MAX0x7fffffff) | |||
369 | == 0) | |||
370 | return; | |||
371 | break; | |||
372 | case TOK_SELECT_TIMEOUT274: | |||
373 | if (parse_number(cfile, &config->select_interval, 0, INT32_MAX0x7fffffff) | |||
374 | == 0) | |||
375 | return; | |||
376 | break; | |||
377 | case TOK_SEND269: | |||
378 | if (parse_option(cfile, &i, config->send_options) == 0) | |||
379 | return; | |||
380 | break; | |||
381 | case TOK_SERVER_NAME267: | |||
382 | if (parse_string(cfile, &val) == 0) | |||
383 | return; | |||
384 | free(config->server_name); | |||
385 | config->server_name = val; | |||
386 | break; | |||
387 | case TOK_SUPERSEDE289: | |||
388 | if (parse_option(cfile, &i, config->defaults) == 0) | |||
389 | return; | |||
390 | config->default_actions[i] = ACTION_SUPERSEDE; | |||
391 | break; | |||
392 | case TOK_TIMEOUT272: | |||
393 | if (parse_number(cfile, &config->offer_interval, 0, INT32_MAX0x7fffffff) | |||
394 | == 0) | |||
395 | return; | |||
396 | break; | |||
397 | case TOK_USELEASE298: | |||
398 | memset(list, 0, sizeof(list)); | |||
399 | count = 0; | |||
400 | if (parse_option_list(cfile, &count, list) == 0) | |||
401 | return; | |||
402 | p = config->default_actions; | |||
403 | if (count == 0) { | |||
404 | for (i = 0; i < DHO_COUNT256; i++) { | |||
405 | free(config->defaults[i].data); | |||
406 | config->defaults[i].data = NULL((void *)0); | |||
407 | config->defaults[i].len = 0; | |||
408 | p[i] = ACTION_USELEASE; | |||
409 | } | |||
410 | } else { | |||
411 | for (i = 0; i < count; i++) { | |||
412 | free(config->defaults[list[i]].data); | |||
413 | config->defaults[list[i]].data = NULL((void *)0); | |||
414 | config->defaults[list[i]].len = 0; | |||
415 | p[list[i]] = ACTION_USELEASE; | |||
416 | } | |||
417 | } | |||
418 | break; | |||
419 | default: | |||
420 | parse_warn("expecting statement."); | |||
421 | skip_to_semi(cfile); | |||
422 | return; | |||
423 | } | |||
424 | ||||
425 | parse_semi(cfile); | |||
426 | } | |||
427 | ||||
428 | int | |||
429 | parse_hex_octets(FILE *cfile, unsigned int *len, uint8_t **buf) | |||
430 | { | |||
431 | static uint8_t octets[1500]; | |||
432 | char *val, *ep; | |||
433 | unsigned long ulval; | |||
434 | unsigned int i; | |||
435 | int token; | |||
436 | ||||
437 | i = 0; | |||
438 | do { | |||
439 | token = next_token(&val, cfile); | |||
440 | ||||
441 | errno(*__errno()) = 0; | |||
442 | ulval = strtoul(val, &ep, 16); | |||
443 | if ((val[0] == '\0' || *ep != '\0') || | |||
444 | (errno(*__errno()) == ERANGE34 && ulval == ULONG_MAX(9223372036854775807L *2UL+1UL)) || | |||
445 | (ulval > UINT8_MAX0xff)) | |||
446 | break; | |||
447 | octets[i++] = ulval; | |||
448 | ||||
449 | if (peek_token(NULL((void *)0), cfile) == ';') { | |||
450 | *buf = malloc(i); | |||
451 | if (*buf == NULL((void *)0)) | |||
452 | break; | |||
453 | memcpy(*buf, octets, i); | |||
454 | *len = i; | |||
455 | return 1; | |||
456 | } | |||
457 | if (i == sizeof(octets)) | |||
458 | break; | |||
459 | token = next_token(NULL((void *)0), cfile); | |||
460 | } while (token == ':'); | |||
461 | ||||
462 | parse_warn("expecting colon delimited list of hex octets."); | |||
463 | ||||
464 | if (token != ';') | |||
465 | skip_to_semi(cfile); | |||
466 | ||||
467 | return 0; | |||
468 | } | |||
469 | ||||
470 | int | |||
471 | parse_domain_list(FILE *cfile, int *len, char **dp) | |||
472 | { | |||
473 | uint8_t buf[DHCP_DOMAIN_SEARCH_LEN1024]; | |||
474 | char *domain; | |||
475 | int count, token; | |||
476 | ||||
477 | memset(buf, 0, sizeof(buf)); | |||
478 | count = 0; | |||
479 | ||||
480 | do { | |||
481 | if (parse_string(cfile, &domain) == 0) | |||
482 | return 0; | |||
483 | ||||
484 | count++; | |||
485 | if (count
| |||
486 | parse_warn("more than 6 search domains"); | |||
487 | break; | |||
488 | } | |||
489 | ||||
490 | if (count
| |||
491 | strlcat(buf, " ", sizeof(buf)); | |||
492 | if (strlcat(buf, domain, sizeof(buf)) >= sizeof(buf)) { | |||
493 | parse_warn("domain list too long"); | |||
494 | break; | |||
495 | } | |||
496 | ||||
497 | token = peek_token(NULL((void *)0), cfile); | |||
498 | if (token == ';') { | |||
499 | *dp = strdup(buf); | |||
500 | if (*dp == NULL((void *)0)) | |||
501 | fatal("domain name list"); | |||
502 | *len = strlen(*dp); | |||
503 | return 1; | |||
504 | } | |||
505 | token = next_token(NULL((void *)0), cfile); | |||
506 | if (token != ',') | |||
507 | parse_warn("';' or ',' expected"); | |||
508 | } while (token == ','); | |||
509 | ||||
510 | if (token != ';') | |||
| ||||
511 | skip_to_semi(cfile); | |||
512 | ||||
513 | return 0; | |||
514 | } | |||
515 | ||||
516 | /* | |||
517 | * option-list :== | |||
518 | * <nil> | |||
519 | * | option-name | |||
520 | * | option-list COMMA option-name | |||
521 | */ | |||
522 | int | |||
523 | parse_option_list(FILE *cfile, int *count, uint8_t *optlist) | |||
524 | { | |||
525 | uint8_t list[DHO_COUNT256]; | |||
526 | unsigned int ix, j; | |||
527 | int i; | |||
528 | int token; | |||
529 | char *val; | |||
530 | ||||
531 | /* Empty list of option names is allowed, to re-init optlist. */ | |||
532 | if (peek_token(NULL((void *)0), cfile) == ';') { | |||
533 | memset(optlist, DHO_PAD0, sizeof(list)); | |||
534 | *count = 0; | |||
535 | return 1; | |||
536 | } | |||
537 | ||||
538 | memset(list, 0, sizeof(list)); | |||
539 | memcpy(list, optlist, *count); | |||
540 | ix = *count; | |||
541 | do { | |||
542 | /* Next token must be an option name. */ | |||
543 | token = next_token(&val, cfile); | |||
544 | i = name_to_code(val); | |||
545 | if (i == DHO_END255) | |||
546 | break; | |||
547 | ||||
548 | /* Avoid storing duplicate options in the list. */ | |||
549 | for (j = 0; j < ix && list[j] != i; j++) | |||
550 | ; | |||
551 | if (j == ix) | |||
552 | list[ix++] = i; | |||
553 | ||||
554 | if (peek_token(NULL((void *)0), cfile) == ';') { | |||
555 | memcpy(optlist, list, sizeof(list)); | |||
556 | *count = ix; | |||
557 | return 1; | |||
558 | } | |||
559 | token = next_token(NULL((void *)0), cfile); | |||
560 | } while (token == ','); | |||
561 | ||||
562 | parse_warn("expecting comma delimited list of option names."); | |||
563 | ||||
564 | if (token != ';') | |||
565 | skip_to_semi(cfile); | |||
566 | ||||
567 | return 0; | |||
568 | } | |||
569 | ||||
570 | /* | |||
571 | * interface :== | |||
572 | * string LBRACE conf-decls RBRACE | |||
573 | */ | |||
574 | int | |||
575 | parse_interface(FILE *cfile, char *name) | |||
576 | { | |||
577 | char *val; | |||
578 | int token; | |||
579 | ||||
580 | token = next_token(&val, cfile); | |||
581 | if (token != TOK_STRING262) { | |||
582 | parse_warn("expecting string."); | |||
583 | if (token != ';') | |||
584 | skip_to_semi(cfile); | |||
585 | return 0; | |||
586 | } | |||
587 | ||||
588 | if (strcmp(name, val) != 0) { | |||
589 | skip_to_semi(cfile); | |||
590 | return 1; | |||
591 | } | |||
592 | ||||
593 | token = next_token(&val, cfile); | |||
594 | if (token != '{') { | |||
595 | parse_warn("expecting '{'."); | |||
596 | if (token != ';') | |||
597 | skip_to_semi(cfile); | |||
598 | return 0; | |||
599 | } | |||
600 | ||||
601 | for (;;) { | |||
602 | token = peek_token(&val, cfile); | |||
603 | if (token == EOF(-1)) { | |||
604 | parse_warn("unterminated interface declaration."); | |||
605 | return 0; | |||
606 | } | |||
607 | if (token == '}') { | |||
608 | token = next_token(NULL((void *)0), cfile); | |||
609 | return 1; | |||
610 | } | |||
611 | parse_conf_decl(cfile, name); | |||
612 | } | |||
613 | ||||
614 | return 0; | |||
615 | } | |||
616 | ||||
617 | /* | |||
618 | * lease :== LEASE RBRACE lease-decls LBRACE | |||
619 | * | |||
620 | * lease-decls :== | |||
621 | * <nil> | |||
622 | * | lease-decl | |||
623 | * | lease-decls lease-decl | |||
624 | */ | |||
625 | int | |||
626 | parse_lease(FILE *cfile, struct client_lease **lp) | |||
627 | { | |||
628 | struct client_lease *lease; | |||
629 | int token; | |||
630 | ||||
631 | token = next_token(NULL((void *)0), cfile); | |||
632 | if (token == EOF(-1)) | |||
633 | return 0; | |||
634 | if (token != TOK_LEASE266) { | |||
635 | parse_warn("expecting lease"); | |||
636 | if (token != ';') | |||
637 | skip_to_semi(cfile); | |||
638 | return 0; | |||
639 | } | |||
640 | ||||
641 | token = next_token(NULL((void *)0), cfile); | |||
642 | if (token != '{') { | |||
643 | parse_warn("expecting '{'."); | |||
644 | if (token != ';') | |||
645 | skip_to_semi(cfile); | |||
646 | return 0; | |||
647 | } | |||
648 | ||||
649 | lease = calloc(1, sizeof(*lease)); | |||
650 | if (lease == NULL((void *)0)) | |||
651 | fatal("lease"); | |||
652 | ||||
653 | for (;;) { | |||
654 | token = peek_token(NULL((void *)0), cfile); | |||
655 | if (token == EOF(-1)) { | |||
656 | parse_warn("unterminated lease."); | |||
657 | free_client_lease(lease); | |||
658 | break; | |||
659 | } | |||
660 | if (token == '}') { | |||
661 | token = next_token(NULL((void *)0), cfile); | |||
662 | *lp = lease; | |||
663 | return 1; | |||
664 | } | |||
665 | parse_lease_decl(cfile, lease); | |||
666 | } | |||
667 | ||||
668 | return 0; | |||
669 | } | |||
670 | ||||
671 | /* | |||
672 | * lease-decl :== | |||
673 | * BOOTP SEMI | |||
674 | * | EPOCH number SEMI | |||
675 | * | EXPIRE <skip to semi> SEMI | |||
676 | * | FILENAME string SEMI | |||
677 | * | FIXED_ADDR ip_address SEMI | |||
678 | * | INTERFACE string SEMI | |||
679 | * | NEXT_SERVER string SEMI | |||
680 | * | OPTION option SEMI | |||
681 | * | REBIND <skip to semi> SEMI | |||
682 | * | RENEW <skip to semi> SEMI | |||
683 | * | SERVER_NAME string SEMI | |||
684 | * | SSID string SEMI | |||
685 | */ | |||
686 | void | |||
687 | parse_lease_decl(FILE *cfile, struct client_lease *lease) | |||
688 | { | |||
689 | char *val; | |||
690 | unsigned int len; | |||
691 | int i, token; | |||
692 | ||||
693 | token = next_token(&val, cfile); | |||
694 | ||||
695 | switch (token) { | |||
696 | case TOK_BOOTP280: | |||
697 | /* 'bootp' is just a comment. See BOOTP_LEASE(). */ | |||
698 | break; | |||
699 | case TOK_EPOCH297: | |||
700 | if (parse_number(cfile, &lease->epoch, INT64_MIN(-0x7fffffffffffffffLL - 1), INT64_MAX0x7fffffffffffffffLL) | |||
701 | == 0) | |||
702 | return; | |||
703 | break; | |||
704 | case TOK_EXPIRE279: | |||
705 | /* 'expire' is just a comment. See 'epoch'. */ | |||
706 | skip_to_semi(cfile); | |||
707 | return; | |||
708 | case TOK_FILENAME257: | |||
709 | if (parse_string(cfile, &val) == 0) | |||
710 | return; | |||
711 | free(lease->filename); | |||
712 | lease->filename = val; | |||
713 | break; | |||
714 | case TOK_FIXED_ADDR259: | |||
715 | if (parse_ip_addr(cfile, &lease->address) == 0) | |||
716 | return; | |||
717 | break; | |||
718 | case TOK_INTERFACE276: | |||
719 | /* 'interface' is just a comment. */ | |||
720 | skip_to_semi(cfile); | |||
721 | return; | |||
722 | case TOK_NEXT_SERVER275: | |||
723 | if (parse_ip_addr(cfile, &lease->next_server) == 0) | |||
724 | return; | |||
725 | break; | |||
726 | case TOK_OPTION260: | |||
727 | if (parse_option(cfile, &i, lease->options) == 0) | |||
728 | return; | |||
729 | break; | |||
730 | case TOK_REBIND278: | |||
731 | case TOK_RENEW277: | |||
732 | /* 'rebind' & 'renew' are just comments. See 'epoch'. */ | |||
733 | skip_to_semi(cfile); | |||
734 | return; | |||
735 | case TOK_SERVER_NAME267: | |||
736 | if (parse_string(cfile, &val) == 0) | |||
737 | return; | |||
738 | free(lease->server_name); | |||
739 | lease->server_name = val; | |||
740 | break; | |||
741 | case TOK_SSID296: | |||
742 | if (parse_string(cfile, &val) == 0) | |||
743 | return; | |||
744 | len = strlen(val); | |||
745 | if (len > sizeof(lease->ssid)) { | |||
746 | free(val); | |||
747 | parse_warn("ssid > 32 bytes"); | |||
748 | skip_to_semi(cfile); | |||
749 | return; | |||
750 | } | |||
751 | memset(lease->ssid, 0, sizeof(lease->ssid)); | |||
752 | memcpy(lease->ssid, val, len); | |||
753 | free(val); | |||
754 | lease->ssid_len = len; | |||
755 | break; | |||
756 | default: | |||
757 | parse_warn("expecting lease declaration."); | |||
758 | skip_to_semi(cfile); | |||
759 | return; | |||
760 | } | |||
761 | ||||
762 | parse_semi(cfile); | |||
763 | } | |||
764 | ||||
765 | /* | |||
766 | * option :== | |||
767 | * option-name option-value | |||
768 | * | |||
769 | * option-value :== | |||
770 | * text | |||
771 | * | hex-octets | |||
772 | * | signed-32 | |||
773 | * | unsigned-32 | |||
774 | * | unsigned-16 | |||
775 | * | unsigned-8 | |||
776 | * | flag | |||
777 | * | ip-address | |||
778 | * | ip-address-array | |||
779 | * | ip-address-pair-array | |||
780 | * | uint16-array | |||
781 | * | cidr-ip-address-array | |||
782 | */ | |||
783 | int | |||
784 | parse_option(FILE *cfile, int *code, struct option_data *options) | |||
785 | { | |||
786 | uint8_t hunkbuf[1024], cidr[5], buf[4]; | |||
787 | struct in_addr ip_addr; | |||
788 | uint8_t *dp; | |||
789 | char *fmt, *val; | |||
790 | long long number; | |||
791 | unsigned int hunkix = 0; | |||
792 | int i, freedp, len, token; | |||
793 | ||||
794 | token = next_token(&val, cfile); | |||
795 | i = name_to_code(val); | |||
796 | if (i == DHO_END255) { | |||
797 | parse_warn("expecting option name."); | |||
798 | skip_to_semi(cfile); | |||
799 | return 0; | |||
800 | } | |||
801 | ||||
802 | /* Parse the option data. */ | |||
803 | do { | |||
804 | for (fmt = code_to_format(i); *fmt != '\0'; fmt++) { | |||
805 | if (*fmt == 'A') | |||
806 | break; | |||
807 | freedp = 0; | |||
808 | switch (*fmt) { | |||
809 | case 'X': | |||
810 | if (peek_token(NULL((void *)0), cfile) == TOK_STRING262) { | |||
811 | if (parse_string(cfile, (char **)&dp) | |||
812 | == 0) | |||
813 | return 0; | |||
814 | len = strlen(dp); | |||
815 | } else if (parse_hex_octets(cfile, &len, &dp) | |||
816 | == 0) | |||
817 | return 0; | |||
818 | freedp = 1; | |||
819 | break; | |||
820 | case 't': /* Text string. */ | |||
821 | if (parse_string(cfile, (char **)&dp) == 0) | |||
822 | return 0; | |||
823 | len = strlen(dp); | |||
824 | freedp = 1; | |||
825 | break; | |||
826 | case 'I': /* IP address. */ | |||
827 | if (parse_ip_addr(cfile, &ip_addr) == 0) | |||
828 | return 0; | |||
829 | len = sizeof(ip_addr); | |||
830 | dp = (uint8_t *)&ip_addr; | |||
831 | break; | |||
832 | case 'l': /* Signed 32-bit integer. */ | |||
833 | if (parse_number(cfile, &number, INT32_MIN(-0x7fffffff - 1), | |||
834 | INT32_MAX0x7fffffff) == 0) | |||
835 | return 0; | |||
836 | number = htobe64(number)(__uint64_t)(__builtin_constant_p(number) ? (__uint64_t)((((__uint64_t )(number) & 0xff) << 56) | ((__uint64_t)(number) & 0xff00ULL) << 40 | ((__uint64_t)(number) & 0xff0000ULL ) << 24 | ((__uint64_t)(number) & 0xff000000ULL) << 8 | ((__uint64_t)(number) & 0xff00000000ULL) >> 8 | ((__uint64_t)(number) & 0xff0000000000ULL) >> 24 | ((__uint64_t)(number) & 0xff000000000000ULL) >> 40 | ((__uint64_t)(number) & 0xff00000000000000ULL) >> 56) : __swap64md(number)); | |||
837 | len = sizeof(int32_t); | |||
838 | memcpy(buf, (char *)&number + (sizeof(number) - len), len); | |||
839 | dp = buf; | |||
840 | break; | |||
841 | case 'L': /* Unsigned 32-bit integer. */ | |||
842 | if (parse_number(cfile, &number, 0, UINT32_MAX0xffffffffU) == 0) | |||
843 | return 0; | |||
844 | number = htobe64(number)(__uint64_t)(__builtin_constant_p(number) ? (__uint64_t)((((__uint64_t )(number) & 0xff) << 56) | ((__uint64_t)(number) & 0xff00ULL) << 40 | ((__uint64_t)(number) & 0xff0000ULL ) << 24 | ((__uint64_t)(number) & 0xff000000ULL) << 8 | ((__uint64_t)(number) & 0xff00000000ULL) >> 8 | ((__uint64_t)(number) & 0xff0000000000ULL) >> 24 | ((__uint64_t)(number) & 0xff000000000000ULL) >> 40 | ((__uint64_t)(number) & 0xff00000000000000ULL) >> 56) : __swap64md(number)); | |||
845 | len = sizeof(uint32_t); | |||
846 | memcpy(buf, (char *)&number + (sizeof(number) - len), len); | |||
847 | dp = buf; | |||
848 | break; | |||
849 | case 'S': /* Unsigned 16-bit integer. */ | |||
850 | if (parse_number(cfile, &number, 0, UINT16_MAX0xffff) == 0) | |||
851 | return 0; | |||
852 | number = htobe64(number)(__uint64_t)(__builtin_constant_p(number) ? (__uint64_t)((((__uint64_t )(number) & 0xff) << 56) | ((__uint64_t)(number) & 0xff00ULL) << 40 | ((__uint64_t)(number) & 0xff0000ULL ) << 24 | ((__uint64_t)(number) & 0xff000000ULL) << 8 | ((__uint64_t)(number) & 0xff00000000ULL) >> 8 | ((__uint64_t)(number) & 0xff0000000000ULL) >> 24 | ((__uint64_t)(number) & 0xff000000000000ULL) >> 40 | ((__uint64_t)(number) & 0xff00000000000000ULL) >> 56) : __swap64md(number)); | |||
853 | len = sizeof(uint16_t); | |||
854 | memcpy(buf, (char *)&number + (sizeof(number) - len), len); | |||
855 | dp = buf; | |||
856 | break; | |||
857 | case 'B': /* Unsigned 8-bit integer. */ | |||
858 | if (parse_number(cfile, &number, 0, UINT8_MAX0xff) == 0) | |||
859 | return 0; | |||
860 | buf[0] = number; | |||
861 | len = 1; | |||
862 | dp = buf; | |||
863 | break; | |||
864 | case 'f': /* Boolean flag. */ | |||
865 | if (parse_boolean(cfile, buf) == 0) | |||
866 | return 0; | |||
867 | len = 1; | |||
868 | dp = buf; | |||
869 | break; | |||
870 | case 'C': | |||
871 | if (parse_cidr(cfile, cidr) == 0) | |||
872 | return 0; | |||
873 | len = 1 + (cidr[0] + 7) / 8; | |||
874 | dp = cidr; | |||
875 | break; | |||
876 | case 'D': | |||
877 | if (peek_token(NULL((void *)0), cfile) == TOK_STRING262) { | |||
878 | if (parse_domain_list(cfile, &len, | |||
879 | (char **)&dp) == 0) | |||
880 | return 0; | |||
881 | } else { | |||
882 | if (parse_hex_octets(cfile, &len, &dp) | |||
883 | == 0) | |||
884 | return 0; | |||
885 | val = rfc1035_as_string(dp, len); | |||
886 | free(dp); | |||
887 | dp = strdup(val); | |||
888 | if (dp == NULL((void *)0)) | |||
889 | fatal("RFC1035 hex octets"); | |||
890 | len = strlen(dp); | |||
891 | } | |||
892 | freedp = 1; | |||
893 | break; | |||
894 | default: | |||
895 | log_warnx("%s: bad format %c in " | |||
896 | "parse_option_param", log_procname, *fmt); | |||
897 | skip_to_semi(cfile); | |||
898 | return 0; | |||
899 | } | |||
900 | if (dp != NULL((void *)0) && len > 0) { | |||
901 | if (hunkix + len > sizeof(hunkbuf)) { | |||
902 | if (freedp == 1) | |||
903 | free(dp); | |||
904 | parse_warn("option data buffer " | |||
905 | "overflow"); | |||
906 | skip_to_semi(cfile); | |||
907 | return 0; | |||
908 | } | |||
909 | memcpy(&hunkbuf[hunkix], dp, len); | |||
910 | hunkix += len; | |||
911 | if (freedp == 1) | |||
912 | free(dp); | |||
913 | } | |||
914 | } | |||
915 | token = peek_token(NULL((void *)0), cfile); | |||
916 | if (*fmt == 'A' && token == ',') | |||
917 | token = next_token(NULL((void *)0), cfile); | |||
918 | } while (*fmt == 'A' && token == ','); | |||
919 | ||||
920 | free(options[i].data); | |||
921 | options[i].data = malloc(hunkix); | |||
922 | if (options[i].data == NULL((void *)0)) | |||
923 | fatal("option data"); | |||
924 | memcpy(options[i].data, hunkbuf, hunkix); | |||
925 | options[i].len = hunkix; | |||
926 | ||||
927 | *code = i; | |||
928 | ||||
929 | return 1; | |||
930 | } | |||
931 | ||||
932 | int | |||
933 | parse_reject_statement(FILE *cfile) | |||
934 | { | |||
935 | struct in_addr addr; | |||
936 | struct reject_elem *elem; | |||
937 | ||||
938 | if (parse_ip_addr(cfile, &addr) == 0) | |||
939 | return 0; | |||
940 | ||||
941 | TAILQ_FOREACH(elem, &config->reject_list, next)for((elem) = ((&config->reject_list)->tqh_first); ( elem) != ((void *)0); (elem) = ((elem)->next.tqe_next)) { | |||
942 | if (elem->addr.s_addr == addr.s_addr) | |||
943 | return 1; | |||
944 | } | |||
945 | ||||
946 | elem = malloc(sizeof(*elem)); | |||
947 | if (elem == NULL((void *)0)) | |||
948 | fatal("reject address"); | |||
949 | elem->addr = addr; | |||
950 | TAILQ_INSERT_TAIL(&config->reject_list, elem, next)do { (elem)->next.tqe_next = ((void *)0); (elem)->next. tqe_prev = (&config->reject_list)->tqh_last; *(& config->reject_list)->tqh_last = (elem); (&config-> reject_list)->tqh_last = &(elem)->next.tqe_next; } while (0); | |||
951 | ||||
952 | return 1; | |||
953 | } | |||
954 | ||||
955 | void | |||
956 | apply_actions(uint8_t *actions) | |||
957 | { | |||
958 | int i; | |||
959 | ||||
960 | for (i = 0; i < DHO_END255; i++) { | |||
961 | switch (actions[i]) { | |||
962 | case ACTION_IGNORE: | |||
963 | config->default_actions[i] = ACTION_IGNORE; | |||
964 | free(config->defaults[i].data); | |||
965 | config->defaults[i].data = NULL((void *)0); | |||
966 | config->defaults[i].len = 0; | |||
967 | break; | |||
968 | default: | |||
969 | break; | |||
970 | } | |||
971 | } | |||
972 | } | |||
973 | ||||
974 | void | |||
975 | set_default_client_identifier(struct ether_addr *hwaddr) | |||
976 | { | |||
977 | struct option_data *opt; | |||
978 | ||||
979 | /* | |||
980 | * Check both len && data so | |||
981 | * | |||
982 | * send dhcp-client-identifier ""; | |||
983 | * | |||
984 | * can be used to suppress sending the default client | |||
985 | * identifier. | |||
986 | */ | |||
987 | opt = &config->send_options[DHO_DHCP_CLIENT_IDENTIFIER61]; | |||
988 | if (opt->len == 0 && opt->data == NULL((void *)0)) { | |||
989 | opt->data = calloc(1, ETHER_ADDR_LEN6 + 1); | |||
990 | if (opt->data == NULL((void *)0)) | |||
991 | fatal("default client identifier"); | |||
992 | opt->data[0] = HTYPE_ETHER1; | |||
993 | memcpy(&opt->data[1], hwaddr->ether_addr_octet, | |||
994 | ETHER_ADDR_LEN6); | |||
995 | opt->len = ETHER_ADDR_LEN6 + 1; | |||
996 | } | |||
997 | } | |||
998 | ||||
999 | void | |||
1000 | set_default_hostname(void) | |||
1001 | { | |||
1002 | char hn[HOST_NAME_MAX255 + 1], *p; | |||
1003 | struct option_data *opt; | |||
1004 | int rslt; | |||
1005 | ||||
1006 | /* | |||
1007 | * Check both len && data so | |||
1008 | * | |||
1009 | * send host-name ""; | |||
1010 | * | |||
1011 | * can be used to suppress sending the default host | |||
1012 | * name. | |||
1013 | */ | |||
1014 | opt = &config->send_options[DHO_HOST_NAME12]; | |||
1015 | if (opt->len == 0 && opt->data == NULL((void *)0)) { | |||
1016 | rslt = gethostname(hn, sizeof(hn)); | |||
1017 | if (rslt == -1) { | |||
1018 | log_warn("host-name"); | |||
1019 | return; | |||
1020 | } | |||
1021 | p = strchr(hn, '.'); | |||
1022 | if (p != NULL((void *)0)) | |||
1023 | *p = '\0'; | |||
1024 | opt->data = strdup(hn); | |||
1025 | if (opt->data == NULL((void *)0)) | |||
1026 | fatal("default host-name"); | |||
1027 | opt->len = strlen(opt->data); | |||
1028 | } | |||
1029 | } |