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