Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sbin/dhclient/clparse.c
39475 views
1
/* $OpenBSD: clparse.c,v 1.18 2004/09/15 18:15:18 henning Exp $ */
2
3
/* Parser for dhclient config and lease files... */
4
5
/*-
6
* SPDX-License-Identifier: BSD-3-Clause
7
*
8
* Copyright (c) 1997 The Internet Software Consortium.
9
* All rights reserved.
10
*
11
* Redistribution and use in source and binary forms, with or without
12
* modification, are permitted provided that the following conditions
13
* are met:
14
*
15
* 1. Redistributions of source code must retain the above copyright
16
* notice, this list of conditions and the following disclaimer.
17
* 2. Redistributions in binary form must reproduce the above copyright
18
* notice, this list of conditions and the following disclaimer in the
19
* documentation and/or other materials provided with the distribution.
20
* 3. Neither the name of The Internet Software Consortium nor the names
21
* of its contributors may be used to endorse or promote products derived
22
* from this software without specific prior written permission.
23
*
24
* THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
25
* CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
26
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
27
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
28
* DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
29
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
32
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
33
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
34
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
35
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36
* SUCH DAMAGE.
37
*
38
* This software has been written for the Internet Software Consortium
39
* by Ted Lemon <[email protected]> in cooperation with Vixie
40
* Enterprises. To learn more about the Internet Software Consortium,
41
* see ``http://www.vix.com/isc''. To learn more about Vixie
42
* Enterprises, see ``http://www.vix.com''.
43
*/
44
45
#include <sys/cdefs.h>
46
#include "dhcpd.h"
47
#include "dhctoken.h"
48
49
struct client_config top_level_config;
50
static struct interface_info *dummy_interfaces;
51
52
static char client_script_name[] = "/sbin/dhclient-script";
53
54
/*
55
* client-conf-file :== client-declarations EOF
56
* client-declarations :== <nil>
57
* | client-declaration
58
* | client-declarations client-declaration
59
*/
60
int
61
read_client_conf(void)
62
{
63
FILE *cfile;
64
char *val;
65
int token;
66
struct client_config *config;
67
68
new_parse(path_dhclient_conf);
69
70
/* Set up the initial dhcp option universe. */
71
initialize_universes();
72
73
/* Initialize the top level client configuration. */
74
memset(&top_level_config, 0, sizeof(top_level_config));
75
76
/* Set some defaults... */
77
top_level_config.vlan_pcp = 0;
78
top_level_config.timeout = 60;
79
top_level_config.select_interval = 0;
80
top_level_config.reboot_timeout = 10;
81
top_level_config.retry_interval = 300;
82
top_level_config.backoff_cutoff = 15;
83
top_level_config.initial_interval = 3;
84
top_level_config.bootp_policy = ACCEPT;
85
top_level_config.script_name = client_script_name;
86
top_level_config.requested_options
87
[top_level_config.requested_option_count++] = DHO_SUBNET_MASK;
88
top_level_config.requested_options
89
[top_level_config.requested_option_count++] = DHO_BROADCAST_ADDRESS;
90
top_level_config.requested_options
91
[top_level_config.requested_option_count++] = DHO_TIME_OFFSET;
92
top_level_config.requested_options
93
[top_level_config.requested_option_count++] = DHO_CLASSLESS_ROUTES;
94
top_level_config.requested_options
95
[top_level_config.requested_option_count++] = DHO_ROUTERS;
96
top_level_config.requested_options
97
[top_level_config.requested_option_count++] = DHO_DOMAIN_NAME;
98
top_level_config.requested_options
99
[top_level_config.requested_option_count++] =
100
DHO_DOMAIN_NAME_SERVERS;
101
top_level_config.requested_options
102
[top_level_config.requested_option_count++] = DHO_HOST_NAME;
103
top_level_config.requested_options
104
[top_level_config.requested_option_count++] = DHO_DOMAIN_SEARCH;
105
top_level_config.requested_options
106
[top_level_config.requested_option_count++] = DHO_INTERFACE_MTU;
107
108
if ((cfile = fopen(path_dhclient_conf, "r")) != NULL) {
109
do {
110
token = peek_token(&val, cfile);
111
if (token == EOF)
112
break;
113
parse_client_statement(cfile, NULL, &top_level_config);
114
} while (1);
115
token = next_token(&val, cfile); /* Clear the peek buffer */
116
fclose(cfile);
117
}
118
119
/*
120
* Set up state and config structures for clients that don't
121
* have per-interface configuration declarations.
122
*/
123
config = NULL;
124
if (!ifi->client) {
125
ifi->client = malloc(sizeof(struct client_state));
126
if (!ifi->client)
127
error("no memory for client state.");
128
memset(ifi->client, 0, sizeof(*(ifi->client)));
129
}
130
if (!ifi->client->config) {
131
if (!config) {
132
config = malloc(sizeof(struct client_config));
133
if (!config)
134
error("no memory for client config.");
135
memcpy(config, &top_level_config,
136
sizeof(top_level_config));
137
}
138
ifi->client->config = config;
139
}
140
141
return (!warnings_occurred);
142
}
143
144
/*
145
* lease-file :== client-lease-statements EOF
146
* client-lease-statements :== <nil>
147
* | client-lease-statements LEASE client-lease-statement
148
*/
149
void
150
read_client_leases(void)
151
{
152
FILE *cfile;
153
char *val;
154
int token;
155
156
new_parse(path_dhclient_db);
157
158
/* Open the lease file. If we can't open it, just return -
159
we can safely trust the server to remember our state. */
160
if ((cfile = fopen(path_dhclient_db, "r")) == NULL)
161
return;
162
do {
163
token = next_token(&val, cfile);
164
if (token == EOF)
165
break;
166
if (token != LEASE) {
167
warning("Corrupt lease file - possible data loss!");
168
skip_to_semi(cfile);
169
break;
170
} else
171
parse_client_lease_statement(cfile, 0);
172
173
} while (1);
174
fclose(cfile);
175
}
176
177
/*
178
* client-declaration :==
179
* SEND option-decl |
180
* DEFAULT option-decl |
181
* SUPERSEDE option-decl |
182
* PREPEND option-decl |
183
* APPEND option-decl |
184
* hardware-declaration |
185
* REQUEST option-list |
186
* REQUIRE option-list |
187
* IGNORE option-list |
188
* TIMEOUT number |
189
* RETRY number |
190
* REBOOT number |
191
* SELECT_TIMEOUT number |
192
* SCRIPT string |
193
* interface-declaration |
194
* LEASE client-lease-statement |
195
* ALIAS client-lease-statement
196
*/
197
void
198
parse_client_statement(FILE *cfile, struct interface_info *ip,
199
struct client_config *config)
200
{
201
char *val;
202
struct option *option;
203
time_t tmp;
204
205
switch (next_token(&val, cfile)) {
206
case SEND:
207
parse_option_decl(cfile, &config->send_options[0]);
208
return;
209
case DEFAULT:
210
option = parse_option_decl(cfile, &config->defaults[0]);
211
if (option)
212
config->default_actions[option->code] = ACTION_DEFAULT;
213
return;
214
case SUPERSEDE:
215
option = parse_option_decl(cfile, &config->defaults[0]);
216
if (option)
217
config->default_actions[option->code] =
218
ACTION_SUPERSEDE;
219
return;
220
case APPEND:
221
option = parse_option_decl(cfile, &config->defaults[0]);
222
if (option)
223
config->default_actions[option->code] = ACTION_APPEND;
224
return;
225
case PREPEND:
226
option = parse_option_decl(cfile, &config->defaults[0]);
227
if (option)
228
config->default_actions[option->code] = ACTION_PREPEND;
229
return;
230
case MEDIA:
231
parse_string_list(cfile, &config->media, 1);
232
return;
233
case HARDWARE:
234
if (ip)
235
parse_hardware_param(cfile, &ip->hw_address);
236
else {
237
parse_warn("hardware address parameter %s",
238
"not allowed here.");
239
skip_to_semi(cfile);
240
}
241
return;
242
case REQUEST:
243
config->requested_option_count =
244
parse_option_list(cfile, config->requested_options);
245
return;
246
case REQUIRE:
247
memset(config->required_options, 0,
248
sizeof(config->required_options));
249
parse_option_list(cfile, config->required_options);
250
return;
251
case IGNORE:
252
parse_option_list(cfile, config->ignored_options);
253
return;
254
case TIMEOUT:
255
parse_lease_time(cfile, &config->timeout);
256
return;
257
case RETRY:
258
parse_lease_time(cfile, &config->retry_interval);
259
return;
260
case SELECT_TIMEOUT:
261
parse_lease_time(cfile, &config->select_interval);
262
return;
263
case REBOOT:
264
parse_lease_time(cfile, &config->reboot_timeout);
265
return;
266
case VLAN_PCP:
267
parse_lease_time(cfile, &tmp);
268
config->vlan_pcp = (u_int)tmp;
269
return;
270
case BACKOFF_CUTOFF:
271
parse_lease_time(cfile, &config->backoff_cutoff);
272
return;
273
case INITIAL_INTERVAL:
274
parse_lease_time(cfile, &config->initial_interval);
275
return;
276
case SCRIPT:
277
config->script_name = parse_string(cfile);
278
return;
279
case INTERFACE:
280
if (ip)
281
parse_warn("nested interface declaration.");
282
parse_interface_declaration(cfile, config);
283
return;
284
case LEASE:
285
parse_client_lease_statement(cfile, 1);
286
return;
287
case ALIAS:
288
parse_client_lease_statement(cfile, 2);
289
return;
290
case REJECT:
291
parse_reject_statement(cfile, config);
292
return;
293
default:
294
break;
295
}
296
297
parse_warn("expecting a statement.");
298
skip_to_semi(cfile);
299
}
300
301
unsigned
302
parse_X(FILE *cfile, u_int8_t *buf, unsigned max)
303
{
304
int token;
305
char *val;
306
unsigned len;
307
308
token = peek_token(&val, cfile);
309
if (token == NUMBER_OR_NAME || token == NUMBER) {
310
len = 0;
311
do {
312
token = next_token(&val, cfile);
313
if (token != NUMBER && token != NUMBER_OR_NAME) {
314
parse_warn("expecting hexadecimal constant.");
315
skip_to_semi(cfile);
316
return (0);
317
}
318
convert_num(&buf[len], val, 16, 8);
319
if (len++ > max) {
320
parse_warn("hexadecimal constant too long.");
321
skip_to_semi(cfile);
322
return (0);
323
}
324
token = peek_token(&val, cfile);
325
if (token == COLON)
326
token = next_token(&val, cfile);
327
} while (token == COLON);
328
val = (char *)buf;
329
} else if (token == STRING) {
330
token = next_token(&val, cfile);
331
len = strlen(val);
332
if (len + 1 > max) {
333
parse_warn("string constant too long.");
334
skip_to_semi(cfile);
335
return (0);
336
}
337
memcpy(buf, val, len + 1);
338
} else {
339
parse_warn("expecting string or hexadecimal data");
340
skip_to_semi(cfile);
341
return (0);
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)
352
{
353
int ix, i;
354
int token;
355
char *val;
356
357
ix = 0;
358
do {
359
token = next_token(&val, cfile);
360
if (!is_identifier(token)) {
361
parse_warn("expected option name.");
362
skip_to_semi(cfile);
363
return (0);
364
}
365
for (i = 0; i < 256; i++)
366
if (!strcasecmp(dhcp_options[i].name, val))
367
break;
368
369
if (i == 256) {
370
parse_warn("%s: unexpected option name.", val);
371
skip_to_semi(cfile);
372
return (0);
373
}
374
list[ix++] = i;
375
if (ix == 256) {
376
parse_warn("%s: too many options.", val);
377
skip_to_semi(cfile);
378
return (0);
379
}
380
token = next_token(&val, cfile);
381
} while (token == COMMA);
382
if (token != SEMI) {
383
parse_warn("expecting semicolon.");
384
skip_to_semi(cfile);
385
return (0);
386
}
387
return (ix);
388
}
389
390
/*
391
* interface-declaration :==
392
* INTERFACE string LBRACE client-declarations RBRACE
393
*/
394
void
395
parse_interface_declaration(FILE *cfile, struct client_config *outer_config)
396
{
397
int token;
398
char *val;
399
struct interface_info *ip;
400
401
token = next_token(&val, cfile);
402
if (token != STRING) {
403
parse_warn("expecting interface name (in quotes).");
404
skip_to_semi(cfile);
405
return;
406
}
407
408
ip = interface_or_dummy(val);
409
410
if (!ip->client)
411
make_client_state(ip);
412
413
if (!ip->client->config)
414
make_client_config(ip, outer_config);
415
416
token = next_token(&val, cfile);
417
if (token != LBRACE) {
418
parse_warn("expecting left brace.");
419
skip_to_semi(cfile);
420
return;
421
}
422
423
do {
424
token = peek_token(&val, cfile);
425
if (token == EOF) {
426
parse_warn("unterminated interface declaration.");
427
return;
428
}
429
if (token == RBRACE)
430
break;
431
parse_client_statement(cfile, ip, ip->client->config);
432
} while (1);
433
token = next_token(&val, cfile);
434
}
435
436
struct interface_info *
437
interface_or_dummy(char *name)
438
{
439
struct interface_info *ip;
440
441
/* Find the interface (if any) that matches the name. */
442
if (!strcmp(ifi->name, name))
443
return (ifi);
444
445
/* If it's not a real interface, see if it's on the dummy list. */
446
for (ip = dummy_interfaces; ip; ip = ip->next)
447
if (!strcmp(ip->name, name))
448
return (ip);
449
450
/*
451
* If we didn't find an interface, make a dummy interface as a
452
* placeholder.
453
*/
454
ip = malloc(sizeof(*ip));
455
if (!ip)
456
error("Insufficient memory to record interface %s", name);
457
memset(ip, 0, sizeof(*ip));
458
strlcpy(ip->name, name, IFNAMSIZ);
459
ip->next = dummy_interfaces;
460
dummy_interfaces = ip;
461
return (ip);
462
}
463
464
void
465
make_client_state(struct interface_info *ip)
466
{
467
ip->client = malloc(sizeof(*(ip->client)));
468
if (!ip->client)
469
error("no memory for state on %s", ip->name);
470
memset(ip->client, 0, sizeof(*(ip->client)));
471
}
472
473
void
474
make_client_config(struct interface_info *ip, struct client_config *config)
475
{
476
ip->client->config = malloc(sizeof(struct client_config));
477
if (!ip->client->config)
478
error("no memory for config for %s", ip->name);
479
memset(ip->client->config, 0, sizeof(*(ip->client->config)));
480
memcpy(ip->client->config, config, sizeof(*config));
481
}
482
483
/*
484
* client-lease-statement :==
485
* RBRACE client-lease-declarations LBRACE
486
*
487
* client-lease-declarations :==
488
* <nil> |
489
* client-lease-declaration |
490
* client-lease-declarations client-lease-declaration
491
*/
492
void
493
parse_client_lease_statement(FILE *cfile, int is_static)
494
{
495
struct client_lease *lease, *lp, *pl;
496
struct interface_info *ip;
497
int token;
498
char *val;
499
500
token = next_token(&val, cfile);
501
if (token != LBRACE) {
502
parse_warn("expecting left brace.");
503
skip_to_semi(cfile);
504
return;
505
}
506
507
lease = malloc(sizeof(struct client_lease));
508
if (!lease)
509
error("no memory for lease.");
510
memset(lease, 0, sizeof(*lease));
511
lease->is_static = is_static;
512
513
ip = NULL;
514
515
do {
516
token = peek_token(&val, cfile);
517
if (token == EOF) {
518
parse_warn("unterminated lease declaration.");
519
free_client_lease(lease);
520
return;
521
}
522
if (token == RBRACE)
523
break;
524
parse_client_lease_declaration(cfile, lease, &ip);
525
} while (1);
526
token = next_token(&val, cfile);
527
528
/* If the lease declaration didn't include an interface
529
* declaration that we recognized, it's of no use to us.
530
*/
531
if (!ip) {
532
free_client_lease(lease);
533
return;
534
}
535
536
/* Make sure there's a client state structure... */
537
if (!ip->client)
538
make_client_state(ip);
539
540
/* If this is an alias lease, it doesn't need to be sorted in. */
541
if (is_static == 2) {
542
ip->client->alias = lease;
543
return;
544
}
545
546
/*
547
* The new lease may supersede a lease that's not the active
548
* lease but is still on the lease list, so scan the lease list
549
* looking for a lease with the same address, and if we find it,
550
* toss it.
551
*/
552
pl = NULL;
553
for (lp = ip->client->leases; lp; lp = lp->next) {
554
if (lp->address.len == lease->address.len &&
555
!memcmp(lp->address.iabuf, lease->address.iabuf,
556
lease->address.len)) {
557
if (pl)
558
pl->next = lp->next;
559
else
560
ip->client->leases = lp->next;
561
free_client_lease(lp);
562
break;
563
}
564
}
565
566
/*
567
* If this is a preloaded lease, just put it on the list of
568
* recorded leases - don't make it the active lease.
569
*/
570
if (is_static) {
571
lease->next = ip->client->leases;
572
ip->client->leases = lease;
573
return;
574
}
575
576
/*
577
* The last lease in the lease file on a particular interface is
578
* the active lease for that interface. Of course, we don't
579
* know what the last lease in the file is until we've parsed
580
* the whole file, so at this point, we assume that the lease we
581
* just parsed is the active lease for its interface. If
582
* there's already an active lease for the interface, and this
583
* lease is for the same ip address, then we just toss the old
584
* active lease and replace it with this one. If this lease is
585
* for a different address, then if the old active lease has
586
* expired, we dump it; if not, we put it on the list of leases
587
* for this interface which are still valid but no longer
588
* active.
589
*/
590
if (ip->client->active) {
591
if (ip->client->active->expiry < cur_time)
592
free_client_lease(ip->client->active);
593
else if (ip->client->active->address.len ==
594
lease->address.len &&
595
!memcmp(ip->client->active->address.iabuf,
596
lease->address.iabuf, lease->address.len))
597
free_client_lease(ip->client->active);
598
else {
599
ip->client->active->next = ip->client->leases;
600
ip->client->leases = ip->client->active;
601
}
602
}
603
ip->client->active = lease;
604
605
/* Phew. */
606
}
607
608
/*
609
* client-lease-declaration :==
610
* BOOTP |
611
* INTERFACE string |
612
* FIXED_ADDR ip_address |
613
* FILENAME string |
614
* SERVER_NAME string |
615
* OPTION option-decl |
616
* RENEW time-decl |
617
* REBIND time-decl |
618
* EXPIRE time-decl
619
*/
620
void
621
parse_client_lease_declaration(FILE *cfile, struct client_lease *lease,
622
struct interface_info **ipp)
623
{
624
int token;
625
char *val;
626
struct interface_info *ip;
627
628
switch (next_token(&val, cfile)) {
629
case BOOTP:
630
lease->is_bootp = 1;
631
break;
632
case INTERFACE:
633
token = next_token(&val, cfile);
634
if (token != STRING) {
635
parse_warn("expecting interface name (in quotes).");
636
skip_to_semi(cfile);
637
return;
638
}
639
ip = interface_or_dummy(val);
640
*ipp = ip;
641
break;
642
case FIXED_ADDR:
643
if (!parse_ip_addr(cfile, &lease->address))
644
return;
645
break;
646
case MEDIUM:
647
parse_string_list(cfile, &lease->medium, 0);
648
return;
649
case FILENAME:
650
lease->filename = parse_string(cfile);
651
return;
652
case NEXT_SERVER:
653
if (!parse_ip_addr(cfile, &lease->nextserver))
654
return;
655
break;
656
case SERVER_NAME:
657
lease->server_name = parse_string(cfile);
658
return;
659
case RENEW:
660
lease->renewal = parse_date(cfile);
661
return;
662
case REBIND:
663
lease->rebind = parse_date(cfile);
664
return;
665
case EXPIRE:
666
lease->expiry = parse_date(cfile);
667
return;
668
case OPTION:
669
parse_option_decl(cfile, lease->options);
670
return;
671
default:
672
parse_warn("expecting lease declaration.");
673
skip_to_semi(cfile);
674
return;
675
}
676
token = next_token(&val, cfile);
677
if (token != SEMI) {
678
parse_warn("expecting semicolon.");
679
skip_to_semi(cfile);
680
}
681
}
682
683
struct option *
684
parse_option_decl(FILE *cfile, struct option_data *options)
685
{
686
char *val;
687
int token;
688
u_int8_t buf[4];
689
u_int8_t hunkbuf[1024];
690
unsigned hunkix = 0;
691
char *vendor;
692
const char *fmt;
693
struct universe *universe;
694
struct option *option;
695
struct iaddr ip_addr;
696
u_int8_t *dp;
697
unsigned len;
698
int nul_term = 0;
699
700
token = next_token(&val, cfile);
701
if (!is_identifier(token)) {
702
parse_warn("expecting identifier after option keyword.");
703
if (token != SEMI)
704
skip_to_semi(cfile);
705
return (NULL);
706
}
707
if ((vendor = strdup(val)) == NULL)
708
error("no memory for vendor information.");
709
710
token = peek_token(&val, cfile);
711
if (token == DOT) {
712
/* Go ahead and take the DOT token... */
713
token = next_token(&val, cfile);
714
715
/* The next token should be an identifier... */
716
token = next_token(&val, cfile);
717
if (!is_identifier(token)) {
718
parse_warn("expecting identifier after '.'");
719
if (token != SEMI)
720
skip_to_semi(cfile);
721
free(vendor);
722
return (NULL);
723
}
724
725
/* Look up the option name hash table for the specified
726
vendor. */
727
universe = ((struct universe *)hash_lookup(&universe_hash,
728
(unsigned char *)vendor, 0));
729
/* If it's not there, we can't parse the rest of the
730
declaration. */
731
if (!universe) {
732
parse_warn("no vendor named %s.", vendor);
733
skip_to_semi(cfile);
734
free(vendor);
735
return (NULL);
736
}
737
} else {
738
/* Use the default hash table, which contains all the
739
standard dhcp option names. */
740
val = vendor;
741
universe = &dhcp_universe;
742
}
743
744
/* Look up the actual option info... */
745
option = (struct option *)hash_lookup(universe->hash,
746
(unsigned char *)val, 0);
747
748
/* If we didn't get an option structure, it's an undefined option. */
749
if (!option) {
750
if (val == vendor)
751
parse_warn("no option named %s", val);
752
else
753
parse_warn("no option named %s for vendor %s",
754
val, vendor);
755
skip_to_semi(cfile);
756
free(vendor);
757
return (NULL);
758
}
759
760
/* Free the initial identifier token. */
761
free(vendor);
762
763
/* Parse the option data... */
764
do {
765
for (fmt = option->format; *fmt; fmt++) {
766
if (*fmt == 'A')
767
break;
768
switch (*fmt) {
769
case 'X':
770
len = parse_X(cfile, &hunkbuf[hunkix],
771
sizeof(hunkbuf) - hunkix);
772
hunkix += len;
773
break;
774
case 't': /* Text string... */
775
token = next_token(&val, cfile);
776
if (token != STRING) {
777
parse_warn("expecting string.");
778
skip_to_semi(cfile);
779
return (NULL);
780
}
781
len = strlen(val);
782
if (hunkix + len + 1 > sizeof(hunkbuf)) {
783
parse_warn("option data buffer %s",
784
"overflow");
785
skip_to_semi(cfile);
786
return (NULL);
787
}
788
memcpy(&hunkbuf[hunkix], val, len + 1);
789
nul_term = 1;
790
hunkix += len;
791
break;
792
case 'I': /* IP address. */
793
if (!parse_ip_addr(cfile, &ip_addr))
794
return (NULL);
795
len = ip_addr.len;
796
dp = ip_addr.iabuf;
797
alloc:
798
if (hunkix + len > sizeof(hunkbuf)) {
799
parse_warn("option data buffer "
800
"overflow");
801
skip_to_semi(cfile);
802
return (NULL);
803
}
804
memcpy(&hunkbuf[hunkix], dp, len);
805
hunkix += len;
806
break;
807
case 'L': /* Unsigned 32-bit integer... */
808
case 'l': /* Signed 32-bit integer... */
809
token = next_token(&val, cfile);
810
if (token != NUMBER) {
811
need_number:
812
parse_warn("expecting number.");
813
if (token != SEMI)
814
skip_to_semi(cfile);
815
return (NULL);
816
}
817
convert_num(buf, val, 0, 32);
818
len = 4;
819
dp = buf;
820
goto alloc;
821
case 's': /* Signed 16-bit integer. */
822
case 'S': /* Unsigned 16-bit integer. */
823
token = next_token(&val, cfile);
824
if (token != NUMBER)
825
goto need_number;
826
convert_num(buf, val, 0, 16);
827
len = 2;
828
dp = buf;
829
goto alloc;
830
case 'b': /* Signed 8-bit integer. */
831
case 'B': /* Unsigned 8-bit integer. */
832
token = next_token(&val, cfile);
833
if (token != NUMBER)
834
goto need_number;
835
convert_num(buf, val, 0, 8);
836
len = 1;
837
dp = buf;
838
goto alloc;
839
case 'f': /* Boolean flag. */
840
token = next_token(&val, cfile);
841
if (!is_identifier(token)) {
842
parse_warn("expecting identifier.");
843
bad_flag:
844
if (token != SEMI)
845
skip_to_semi(cfile);
846
return (NULL);
847
}
848
if (!strcasecmp(val, "true") ||
849
!strcasecmp(val, "on"))
850
buf[0] = 1;
851
else if (!strcasecmp(val, "false") ||
852
!strcasecmp(val, "off"))
853
buf[0] = 0;
854
else {
855
parse_warn("expecting boolean.");
856
goto bad_flag;
857
}
858
len = 1;
859
dp = buf;
860
goto alloc;
861
default:
862
warning("Bad format %c in parse_option_param.",
863
*fmt);
864
skip_to_semi(cfile);
865
return (NULL);
866
}
867
}
868
token = next_token(&val, cfile);
869
} while (*fmt == 'A' && token == COMMA);
870
871
if (token != SEMI) {
872
parse_warn("semicolon expected.");
873
skip_to_semi(cfile);
874
return (NULL);
875
}
876
877
options[option->code].data = malloc(hunkix + nul_term);
878
if (!options[option->code].data)
879
error("out of memory allocating option data.");
880
memcpy(options[option->code].data, hunkbuf, hunkix + nul_term);
881
options[option->code].len = hunkix;
882
return (option);
883
}
884
885
void
886
parse_string_list(FILE *cfile, struct string_list **lp, int multiple)
887
{
888
int token;
889
char *val;
890
size_t valsize;
891
struct string_list *cur, *tmp;
892
893
/* Find the last medium in the media list. */
894
if (*lp)
895
for (cur = *lp; cur->next; cur = cur->next)
896
; /* nothing */
897
else
898
cur = NULL;
899
900
do {
901
token = next_token(&val, cfile);
902
if (token != STRING) {
903
parse_warn("Expecting media options.");
904
skip_to_semi(cfile);
905
return;
906
}
907
908
valsize = strlen(val) + 1;
909
tmp = new_string_list(valsize);
910
if (tmp == NULL)
911
error("no memory for string list entry.");
912
memcpy(tmp->string, val, valsize);
913
tmp->next = NULL;
914
915
/* Store this medium at the end of the media list. */
916
if (cur)
917
cur->next = tmp;
918
else
919
*lp = tmp;
920
cur = tmp;
921
922
token = next_token(&val, cfile);
923
} while (multiple && token == COMMA);
924
925
if (token != SEMI) {
926
parse_warn("expecting semicolon.");
927
skip_to_semi(cfile);
928
}
929
}
930
931
void
932
parse_reject_statement(FILE *cfile, struct client_config *config)
933
{
934
int token;
935
char *val;
936
struct iaddr addr;
937
struct iaddrlist *list;
938
939
do {
940
if (!parse_ip_addr(cfile, &addr)) {
941
parse_warn("expecting IP address.");
942
skip_to_semi(cfile);
943
return;
944
}
945
946
list = malloc(sizeof(struct iaddrlist));
947
if (!list)
948
error("no memory for reject list!");
949
950
list->addr = addr;
951
list->next = config->reject_list;
952
config->reject_list = list;
953
954
token = next_token(&val, cfile);
955
} while (token == COMMA);
956
957
if (token != SEMI) {
958
parse_warn("expecting semicolon.");
959
skip_to_semi(cfile);
960
}
961
}
962
963