GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/hostapd/parse.y Lines: 0 280 0.0 %
Date: 2017-11-13 Branches: 0 211 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: parse.y,v 1.53 2017/01/05 13:53:09 krw Exp $	*/
2
3
/*
4
 * Copyright (c) 2004, 2005, 2006 Reyk Floeter <reyk@openbsd.org>
5
 * Copyright (c) 2002 - 2005 Henning Brauer <henning@openbsd.org>
6
 * Copyright (c) 2001 Markus Friedl.  All rights reserved.
7
 * Copyright (c) 2001 Daniel Hartmeier.  All rights reserved.
8
 * Copyright (c) 2001 Theo de Raadt.  All rights reserved.
9
 *
10
 * Permission to use, copy, modify, and distribute this software for any
11
 * purpose with or without fee is hereby granted, provided that the above
12
 * copyright notice and this permission notice appear in all copies.
13
 *
14
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
15
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
16
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
17
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
18
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
19
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
20
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21
 */
22
23
%{
24
#include <sys/ioctl.h>
25
#include <sys/types.h>
26
#include <sys/socket.h>
27
#include <sys/time.h>
28
#include <sys/queue.h>
29
#include <sys/stat.h>
30
31
#include <net/if.h>
32
#include <net/if_dl.h>
33
#include <net/if_media.h>
34
#include <net/if_arp.h>
35
#include <net/if_llc.h>
36
#include <net/bpf.h>
37
38
#include <netinet/in.h>
39
#include <netinet/if_ether.h>
40
#include <arpa/inet.h>
41
42
#include <net80211/ieee80211.h>
43
#include <net80211/ieee80211_radiotap.h>
44
45
#include <ctype.h>
46
#include <errno.h>
47
#include <event.h>
48
#include <fcntl.h>
49
#include <stdio.h>
50
#include <stdlib.h>
51
#include <stdarg.h>
52
#include <string.h>
53
#include <unistd.h>
54
#include <limits.h>
55
#include <stdint.h>
56
#include <err.h>
57
58
#include "hostapd.h"
59
60
TAILQ_HEAD(files, file)		 files = TAILQ_HEAD_INITIALIZER(files);
61
static struct file {
62
	TAILQ_ENTRY(file)	 entry;
63
	FILE			*stream;
64
	char			*name;
65
	int			 lineno;
66
	int			 errors;
67
} *file, *topfile;
68
struct file	*pushfile(const char *, int);
69
int		 popfile(void);
70
int		 check_file_secrecy(int, const char *);
71
int		 yyparse(void);
72
int		 yylex(void);
73
int		 yyerror(const char *, ...)
74
    __attribute__((__format__ (printf, 1, 2)))
75
    __attribute__((__nonnull__ (1)));
76
int		 kw_cmp(const void *, const void *);
77
int		 lookup(char *);
78
int		 lgetc(int);
79
int		 lungetc(int);
80
int		 findeol(void);
81
82
TAILQ_HEAD(symhead, sym)	 symhead = TAILQ_HEAD_INITIALIZER(symhead);
83
struct sym {
84
	TAILQ_ENTRY(sym)	 entry;
85
	int			 used;
86
	int			 persist;
87
	char			*nam;
88
	char			*val;
89
};
90
int		 symset(const char *, const char *, int);
91
char		*symget(const char *);
92
93
extern struct hostapd_config hostapd_cfg;
94
95
typedef struct {
96
	union {
97
		struct {
98
			u_int8_t		lladdr[IEEE80211_ADDR_LEN];
99
			struct hostapd_table	*table;
100
			u_int32_t		flags;
101
		} reflladdr;
102
		struct {
103
			u_int16_t		alg;
104
			u_int16_t		transaction;
105
		} authalg;
106
		struct in_addr		in;
107
		char			*string;
108
		int64_t			number;
109
		u_int16_t		reason;
110
		enum hostapd_op		op;
111
		struct timeval		timeout;
112
	} v;
113
	int lineno;
114
} YYSTYPE;
115
116
struct hostapd_apme *apme;
117
struct hostapd_table *table;
118
struct hostapd_entry *entry;
119
struct hostapd_frame frame, *frame_ptr;
120
struct hostapd_ieee80211_frame *frame_ieee80211;
121
122
#define HOSTAPD_MATCH(_m, _not)	{					\
123
	frame.f_flags |= (_not) ?					\
124
	    HOSTAPD_FRAME_F_##_m##_N : HOSTAPD_FRAME_F_##_m;		\
125
}
126
#define HOSTAPD_MATCH_TABLE(_m, _not)	{				\
127
	frame.f_flags |= HOSTAPD_FRAME_F_##_m##_TABLE | ((_not) ?	\
128
	    HOSTAPD_FRAME_F_##_m##_N : HOSTAPD_FRAME_F_##_m);		\
129
}
130
#define HOSTAPD_MATCH_RADIOTAP(_x) {					\
131
	if (hostapd_cfg.c_apme_dlt == DLT_IEEE802_11 ||			\
132
	    (hostapd_cfg.c_apme_dlt == 0 &&				\
133
	    HOSTAPD_DLT == DLT_IEEE802_11)) {				\
134
		yyerror("option %s requires radiotap headers", #_x);	\
135
		YYERROR;						\
136
	}								\
137
	frame.f_radiotap |= HOSTAPD_RADIOTAP_F(RSSI);			\
138
	frame.f_flags |= HOSTAPD_FRAME_F_##_x;				\
139
}
140
#define HOSTAPD_IAPP_FLAG(_f, _not) {					\
141
	if (_not)							\
142
		hostapd_cfg.c_iapp.i_flags &= ~(HOSTAPD_IAPP_F_##_f);	\
143
	else								\
144
		hostapd_cfg.c_iapp.i_flags |= (HOSTAPD_IAPP_F_##_f);	\
145
}
146
147
%}
148
149
%token	MODE INTERFACE IAPP HOSTAP MULTICAST BROADCAST SET SEC USEC
150
%token	HANDLE TYPE SUBTYPE FROM TO BSSID WITH FRAME RADIOTAP NWID PASSIVE
151
%token	MANAGEMENT DATA PROBE BEACON ATIM ANY DS NO DIR RESEND RANDOM
152
%token	AUTH DEAUTH ASSOC DISASSOC REASSOC REQUEST RESPONSE PCAP RATE
153
%token	ERROR CONST TABLE NODE DELETE ADD LOG VERBOSE LIMIT QUICK SKIP
154
%token	REASON UNSPECIFIED EXPIRE LEAVE ASSOC TOOMANY NOT AUTHED ASSOCED
155
%token	RESERVED RSN REQUIRED INCONSISTENT IE INVALID MIC FAILURE OPEN
156
%token	ADDRESS PORT ON NOTIFY TTL INCLUDE ROUTE ROAMING RSSI TXRATE FREQ
157
%token	HOPPER DELAY NE LE GE ARROW
158
%token	<v.string>	STRING
159
%token	<v.number>	NUMBER
160
%type	<v.in>		ipv4addr
161
%type	<v.reflladdr>	refaddr, lladdr, randaddr, frmactionaddr, frmmatchaddr
162
%type	<v.reason>	frmreason_l
163
%type	<v.string>	table
164
%type	<v.string>	string
165
%type	<v.authalg>	authalg
166
%type	<v.op>		unaryop
167
%type	<v.number>	percent
168
%type	<v.number>	txrate
169
%type	<v.number>	freq
170
%type	<v.number>	not
171
%type	<v.timeout>	timeout
172
173
%%
174
175
/*
176
 * Configuration grammar
177
 */
178
179
grammar		: /* empty */
180
		| grammar '\n'
181
		| grammar include '\n'
182
		| grammar tabledef '\n'
183
		| grammar option '\n'
184
		| grammar event '\n'
185
		| grammar varset '\n'
186
		| grammar error '\n'		{ file->errors++; }
187
		;
188
189
include		: INCLUDE STRING
190
		{
191
			struct file *nfile;
192
193
			if ((nfile =
194
			    pushfile($2, 1)) == NULL) {
195
				yyerror("failed to include file %s", $2);
196
				free($2);
197
				YYERROR;
198
			}
199
			free($2);
200
201
			file = nfile;
202
			lungetc('\n');
203
		}
204
205
option		: SET HOSTAP INTERFACE hostapifaces
206
		{
207
			if (!TAILQ_EMPTY(&hostapd_cfg.c_apmes))
208
				hostapd_cfg.c_flags |= HOSTAPD_CFG_F_APME;
209
		}
210
		| SET HOSTAP HOPPER INTERFACE hopperifaces
211
		| SET HOSTAP HOPPER DELAY timeout
212
		{
213
			bcopy(&$5, &hostapd_cfg.c_apme_hopdelay,
214
			    sizeof(struct timeval));
215
		}
216
		| SET HOSTAP MODE hostapmode
217
		| SET IAPP INTERFACE STRING passive
218
		{
219
			if (strlcpy(hostapd_cfg.c_iapp.i_iface, $4,
220
			    sizeof(hostapd_cfg.c_iapp.i_iface)) >=
221
			    sizeof(hostapd_cfg.c_iapp.i_iface)) {
222
				yyerror("invalid interface %s", $4);
223
				free($4);
224
				YYERROR;
225
			}
226
227
			hostapd_cfg.c_flags |= HOSTAPD_CFG_F_IAPP;
228
229
			hostapd_log(HOSTAPD_LOG_DEBUG,
230
			    "%s: IAPP interface added", $4);
231
232
			free($4);
233
		}
234
		| SET IAPP MODE iappmode
235
		| SET IAPP ADDRESS ROAMING TABLE table
236
		{
237
			if ((hostapd_cfg.c_iapp.i_addr_tbl =
238
			    hostapd_table_lookup(&hostapd_cfg, $6)) == NULL) {
239
				yyerror("undefined table <%s>", $6);
240
				free($6);
241
				YYERROR;
242
			}
243
			free($6);
244
		}
245
		| SET IAPP ROUTE ROAMING TABLE table
246
		{
247
			if ((hostapd_cfg.c_iapp.i_route_tbl =
248
			    hostapd_table_lookup(&hostapd_cfg, $6)) == NULL) {
249
				yyerror("undefined table <%s>", $6);
250
				free($6);
251
				YYERROR;
252
			}
253
			free($6);
254
		}
255
		| SET IAPP HANDLE SUBTYPE iappsubtypes
256
		;
257
258
iappmode	: MULTICAST iappmodeaddr iappmodeport iappmodettl
259
		{
260
			hostapd_cfg.c_flags &= ~HOSTAPD_CFG_F_BRDCAST;
261
		}
262
		| BROADCAST iappmodeport
263
		{
264
			hostapd_cfg.c_flags |= HOSTAPD_CFG_F_BRDCAST;
265
		}
266
		;
267
268
iappmodeaddr	: /* empty */
269
		| ADDRESS ipv4addr
270
		{
271
			bcopy(&$2, &hostapd_cfg.c_iapp.i_multicast.sin_addr,
272
			    sizeof(struct in_addr));
273
		}
274
		;
275
276
iappmodeport	: /* empty */
277
		| PORT NUMBER
278
		{
279
			if ($2 < 0 || $2 > UINT16_MAX) {
280
				yyerror("port out of range: %lld", $2);
281
				YYERROR;
282
			}
283
			hostapd_cfg.c_iapp.i_addr.sin_port = htons($2);
284
		}
285
		;
286
287
iappmodettl	: /* empty */
288
		| TTL NUMBER
289
		{
290
			if ($2 < 1 || $2 > UINT8_MAX) {
291
				yyerror("ttl out of range: %lld", $2);
292
				YYERROR;
293
			}
294
			hostapd_cfg.c_iapp.i_ttl = $2;
295
		}
296
		;
297
298
hostapmode	: RADIOTAP
299
		{
300
			hostapd_cfg.c_apme_dlt = DLT_IEEE802_11_RADIO;
301
		}
302
		| PCAP
303
		{
304
			hostapd_cfg.c_apme_dlt = DLT_IEEE802_11;
305
		}
306
		;
307
308
hostapifaces	: '{' optnl hostapifacelist optnl '}'
309
		| hostapiface
310
		;
311
312
hostapifacelist	: hostapiface
313
		| hostapifacelist comma hostapiface
314
		;
315
316
hostapiface	: STRING
317
		{
318
			if (hostapd_apme_add(&hostapd_cfg, $1) != 0) {
319
				yyerror("failed to add hostap interface");
320
				YYERROR;
321
			}
322
			free($1);
323
		}
324
		;
325
326
hopperifaces	: '{' optnl hopperifacelist optnl '}'
327
		| hopperiface
328
		;
329
330
hopperifacelist	: hopperiface
331
		| hopperifacelist comma hopperiface
332
		;
333
334
hopperiface	: STRING
335
		{
336
			if ((apme = hostapd_apme_addhopper(&hostapd_cfg,
337
			    $1)) == NULL) {
338
				yyerror("failed to add hopper %s", $1);
339
				free($1);
340
				YYERROR;
341
			}
342
			free($1);
343
		}
344
		;
345
346
hostapmatch	: /* empty */
347
		| ON not STRING
348
		{
349
			if ((frame.f_apme =
350
			    hostapd_apme_lookup(&hostapd_cfg, $3)) == NULL) {
351
				yyerror("undefined hostap interface");
352
				free($3);
353
				YYERROR;
354
			}
355
			free($3);
356
357
			HOSTAPD_MATCH(APME, $2);
358
		}
359
		;
360
361
event		: HOSTAP HANDLE
362
		{
363
			bzero(&frame, sizeof(struct hostapd_frame));
364
			/* IEEE 802.11 frame to match */
365
			frame_ieee80211 = &frame.f_frame;
366
		} eventopt hostapmatch frmmatch {
367
			/* IEEE 802.11 raw frame to send as an action */
368
			frame_ieee80211 = &frame.f_action_data.a_frame;
369
		} action limit rate {
370
			if ((frame_ptr = calloc(1, sizeof(struct hostapd_frame)))
371
			    == NULL) {
372
				yyerror("calloc");
373
				YYERROR;
374
			}
375
376
			if (gettimeofday(&frame.f_last, NULL) == -1)
377
				hostapd_fatal("gettimeofday");
378
			timeradd(&frame.f_last, &frame.f_limit, &frame.f_then);
379
380
			bcopy(&frame, frame_ptr, sizeof(struct hostapd_frame));
381
			TAILQ_INSERT_TAIL(&hostapd_cfg.c_frames,
382
			    frame_ptr, f_entries);
383
		}
384
		;
385
386
iappsubtypes	: '{' optnl iappsubtypelist optnl '}'
387
		| iappsubtype
388
		;
389
390
iappsubtypelist	: iappsubtype
391
		| iappsubtypelist comma iappsubtype
392
		;
393
394
iappsubtype	: not ADD NOTIFY
395
		{
396
			HOSTAPD_IAPP_FLAG(ADD_NOTIFY, $1);
397
		}
398
		| not RADIOTAP
399
		{
400
			HOSTAPD_IAPP_FLAG(RADIOTAP, $1);
401
		}
402
		| not ROUTE ROAMING
403
		{
404
			HOSTAPD_IAPP_FLAG(ROAMING_ROUTE, $1);
405
		}
406
		| not ADDRESS ROAMING
407
		{
408
			HOSTAPD_IAPP_FLAG(ROAMING_ADDRESS, $1);
409
		}
410
		;
411
412
eventopt	: /* empty */
413
		{
414
			frame.f_flags |= HOSTAPD_FRAME_F_RET_OK;
415
		}
416
		| QUICK
417
		{
418
			frame.f_flags |= HOSTAPD_FRAME_F_RET_QUICK;
419
		}
420
		| SKIP
421
		{
422
			frame.f_flags |= HOSTAPD_FRAME_F_RET_SKIP;
423
		}
424
		;
425
426
action		: /* empty */
427
		{
428
			frame.f_action = HOSTAPD_ACTION_NONE;
429
		}
430
		| WITH LOG verbose
431
		{
432
			frame.f_action = HOSTAPD_ACTION_LOG;
433
		}
434
		| WITH FRAME frmaction
435
		{
436
			frame.f_action = HOSTAPD_ACTION_FRAME;
437
		}
438
		| WITH IAPP iapp
439
		| WITH NODE nodeopt frmactionaddr
440
		{
441
			if (($4.flags & HOSTAPD_ACTION_F_REF_M) == 0) {
442
				bcopy($4.lladdr, frame.f_action_data.a_lladdr,
443
				    IEEE80211_ADDR_LEN);
444
			} else
445
				frame.f_action_data.a_flags |= $4.flags;
446
		}
447
		| WITH RESEND
448
		{
449
			frame.f_action = HOSTAPD_ACTION_RESEND;
450
		}
451
		;
452
453
verbose		: /* empty */
454
		| VERBOSE
455
		{
456
			frame.f_action_flags |= HOSTAPD_ACTION_VERBOSE;
457
		}
458
		;
459
460
iapp		: TYPE RADIOTAP verbose
461
		{
462
			frame.f_action = HOSTAPD_ACTION_RADIOTAP;
463
		}
464
		;
465
466
nodeopt		: DELETE
467
		{
468
			frame.f_action = HOSTAPD_ACTION_DELNODE;
469
		}
470
		| ADD
471
		{
472
			frame.f_action = HOSTAPD_ACTION_ADDNODE;
473
		}
474
		;
475
476
frmmatch	: ANY
477
		| frm frmmatchtype frmmatchdir frmmatchfrom frmmatchto
478
			frmmatchbssid frmmatchrtap
479
		;
480
481
frm		: /* empty */
482
		| FRAME
483
		;
484
485
frmaction	: frmactiontype frmactiondir frmactionfrom frmactionto frmactionbssid
486
		;
487
488
limit		: /* empty */
489
		| LIMIT NUMBER SEC
490
		{
491
			if ($2 < 0 || $2 > LONG_MAX) {
492
				yyerror("limit out of range: %lld sec", $2);
493
				YYERROR;
494
			}
495
			frame.f_limit.tv_sec = $2;
496
		}
497
		| LIMIT NUMBER USEC
498
		{
499
			if ($2 < 0 || $2 > LONG_MAX) {
500
				yyerror("limit out of range: %lld usec", $2);
501
				YYERROR;
502
			}
503
			frame.f_limit.tv_usec = $2;
504
		}
505
		;
506
507
rate		: /* empty */
508
		| RATE NUMBER '/' NUMBER SEC
509
		{
510
			if (($2 < 1 || $2 > LONG_MAX) ||
511
			    ($4 < 1 || $4 > LONG_MAX)) {
512
				yyerror("rate out of range: %lld/%lld sec",
513
				    $2, $4);
514
				YYERROR;
515
			}
516
517
			if (!($2 && $4)) {
518
				yyerror("invalid rate");
519
				YYERROR;
520
			}
521
522
			frame.f_rate = $2;
523
			frame.f_rate_intval = $4;
524
		}
525
		;
526
527
frmmatchtype	: /* any */
528
		| TYPE ANY
529
		| TYPE not DATA
530
		{
531
			frame_ieee80211->i_fc[0] |=
532
			    IEEE80211_FC0_TYPE_DATA;
533
			HOSTAPD_MATCH(TYPE, $2);
534
		}
535
		| TYPE not MANAGEMENT frmmatchmgmt
536
		{
537
			frame_ieee80211->i_fc[0] |=
538
			    IEEE80211_FC0_TYPE_MGT;
539
			HOSTAPD_MATCH(TYPE, $2);
540
		}
541
		;
542
543
frmmatchmgmt	: /* any */
544
		| SUBTYPE ANY
545
		| SUBTYPE not frmsubtype
546
		{
547
			HOSTAPD_MATCH(SUBTYPE, $2);
548
		}
549
		;
550
551
frmsubtype	: PROBE REQUEST frmelems
552
		{
553
			frame_ieee80211->i_fc[0] |=
554
			    IEEE80211_FC0_SUBTYPE_PROBE_REQ;
555
		}
556
		| PROBE RESPONSE frmelems
557
		{
558
			frame_ieee80211->i_fc[0] |=
559
			    IEEE80211_FC0_SUBTYPE_PROBE_RESP;
560
		}
561
		| BEACON frmelems
562
		{
563
			frame_ieee80211->i_fc[0] |=
564
			    IEEE80211_FC0_SUBTYPE_BEACON;
565
		}
566
		| ATIM
567
		{
568
			frame_ieee80211->i_fc[0] |=
569
			    IEEE80211_FC0_SUBTYPE_ATIM;
570
		}
571
		| AUTH frmauth
572
		{
573
			frame_ieee80211->i_fc[0] |=
574
			    IEEE80211_FC0_SUBTYPE_AUTH;
575
		}
576
		| DEAUTH frmreason
577
		{
578
			frame_ieee80211->i_fc[0] |=
579
			    IEEE80211_FC0_SUBTYPE_DEAUTH;
580
		}
581
		| ASSOC REQUEST
582
		{
583
			frame_ieee80211->i_fc[0] |=
584
			    IEEE80211_FC0_SUBTYPE_ASSOC_REQ;
585
		}
586
		| DISASSOC frmreason
587
		{
588
			frame_ieee80211->i_fc[0] |=
589
			    IEEE80211_FC0_SUBTYPE_DISASSOC;
590
		}
591
		| ASSOC RESPONSE
592
		{
593
			frame_ieee80211->i_fc[0] |=
594
			    IEEE80211_FC0_SUBTYPE_ASSOC_RESP;
595
		}
596
		| REASSOC REQUEST
597
		{
598
			frame_ieee80211->i_fc[0] |=
599
			    IEEE80211_FC0_SUBTYPE_REASSOC_REQ;
600
		}
601
		| REASSOC RESPONSE
602
		{
603
			frame_ieee80211->i_fc[0] |=
604
			    IEEE80211_FC0_SUBTYPE_REASSOC_RESP;
605
		}
606
		;
607
608
frmelems	: /* empty */
609
		| frmelems_l
610
		;
611
612
frmelems_l	: frmelems_l frmelem
613
		| frmelem
614
		;
615
616
frmelem		: NWID not STRING
617
		;
618
619
frmauth		: /* empty */
620
		| authalg
621
		{
622
			if ((frame_ieee80211->i_data = malloc(6)) == NULL) {
623
				yyerror("failed to allocate auth");
624
				YYERROR;
625
			}
626
			((u_int16_t *)frame_ieee80211->i_data)[0] =
627
				$1.alg;
628
			((u_int16_t *)frame_ieee80211->i_data)[1] =
629
				$1.transaction;
630
			((u_int16_t *)frame_ieee80211->i_data)[0] = 0;
631
			frame_ieee80211->i_data_len = 6;
632
		}
633
		;
634
635
authalg		: OPEN REQUEST
636
		{
637
			$$.alg = htole16(IEEE80211_AUTH_ALG_OPEN);
638
			$$.transaction = htole16(IEEE80211_AUTH_OPEN_REQUEST);
639
		}
640
		| OPEN RESPONSE
641
		{
642
			$$.alg = htole16(IEEE80211_AUTH_ALG_OPEN);
643
			$$.transaction = htole16(IEEE80211_AUTH_OPEN_RESPONSE);
644
		}
645
		;
646
647
frmreason	: frmreason_l
648
		{
649
			if ($1 != 0) {
650
				if ((frame_ieee80211->i_data =
651
				    malloc(sizeof(u_int16_t))) == NULL) {
652
					yyerror("failed to allocate "
653
					    "reason code %u", $1);
654
					YYERROR;
655
				}
656
				*(u_int16_t *)frame_ieee80211->i_data =
657
				    htole16($1);
658
				frame_ieee80211->i_data_len = sizeof(u_int16_t);
659
			}
660
		}
661
		;
662
663
frmreason_l	: /* empty */
664
		{
665
			$$ = 0;
666
		}
667
		| REASON UNSPECIFIED
668
		{
669
			$$ = IEEE80211_REASON_UNSPECIFIED;
670
		}
671
		| REASON AUTH EXPIRE
672
		{
673
			$$ = IEEE80211_REASON_AUTH_EXPIRE;
674
		}
675
		| REASON AUTH LEAVE
676
		{
677
			$$ = IEEE80211_REASON_AUTH_LEAVE;
678
		}
679
		| REASON ASSOC EXPIRE
680
		{
681
			$$ = IEEE80211_REASON_ASSOC_EXPIRE;
682
		}
683
		| REASON ASSOC TOOMANY
684
		{
685
			$$ = IEEE80211_REASON_ASSOC_TOOMANY;
686
		}
687
		| REASON NOT AUTHED
688
		{
689
			$$ = IEEE80211_REASON_NOT_AUTHED;
690
		}
691
		| REASON NOT ASSOCED
692
		{
693
			$$ = IEEE80211_REASON_NOT_ASSOCED;
694
		}
695
		| REASON ASSOC LEAVE
696
		{
697
			$$ = IEEE80211_REASON_ASSOC_LEAVE;
698
		}
699
		| REASON ASSOC NOT AUTHED
700
		{
701
			$$ = IEEE80211_REASON_NOT_AUTHED;
702
		}
703
		| REASON RESERVED
704
		{
705
			$$ = 10;	/* XXX unknown */
706
		}
707
		| REASON RSN REQUIRED
708
		{
709
			$$ = IEEE80211_REASON_RSN_REQUIRED;
710
		}
711
		| REASON RSN INCONSISTENT
712
		{
713
			$$ = IEEE80211_REASON_RSN_INCONSISTENT;
714
		}
715
		| REASON IE INVALID
716
		{
717
			$$ = IEEE80211_REASON_IE_INVALID;
718
		}
719
		| REASON MIC FAILURE
720
		{
721
			$$ = IEEE80211_REASON_MIC_FAILURE;
722
		}
723
		;
724
725
frmmatchdir	: /* any */
726
		| DIR ANY
727
		| DIR not frmdir
728
		{
729
			HOSTAPD_MATCH(DIR, $2);
730
		}
731
		;
732
733
frmdir		: NO DS
734
		{
735
			frame_ieee80211->i_fc[1] |= IEEE80211_FC1_DIR_NODS;
736
		}
737
		| TO DS
738
		{
739
			frame_ieee80211->i_fc[1] |= IEEE80211_FC1_DIR_TODS;
740
		}
741
		| FROM DS
742
		{
743
			frame_ieee80211->i_fc[1] |= IEEE80211_FC1_DIR_FROMDS;
744
		}
745
		| DS TO DS
746
		{
747
			frame_ieee80211->i_fc[1] |= IEEE80211_FC1_DIR_DSTODS;
748
		}
749
		;
750
751
frmmatchfrom	: /* any */
752
		| FROM ANY
753
		| FROM not frmmatchaddr
754
		{
755
			if (($3.flags & HOSTAPD_ACTION_F_OPT_TABLE) == 0) {
756
				bcopy($3.lladdr, &frame_ieee80211->i_from,
757
				    IEEE80211_ADDR_LEN);
758
				HOSTAPD_MATCH(FROM, $2);
759
			} else {
760
				frame.f_from = $3.table;
761
				HOSTAPD_MATCH_TABLE(FROM, $2);
762
			}
763
		}
764
		;
765
766
frmmatchto	: /* any */
767
		| TO ANY
768
		| TO not frmmatchaddr
769
		{
770
			if (($3.flags & HOSTAPD_ACTION_F_OPT_TABLE) == 0) {
771
				bcopy($3.lladdr, &frame_ieee80211->i_to,
772
				    IEEE80211_ADDR_LEN);
773
				HOSTAPD_MATCH(TO, $2);
774
			} else {
775
				frame.f_to = $3.table;
776
				HOSTAPD_MATCH_TABLE(TO, $2);
777
			}
778
		}
779
		;
780
781
frmmatchbssid	: /* any */
782
		| BSSID ANY
783
		| BSSID not frmmatchaddr
784
		{
785
			if (($3.flags & HOSTAPD_ACTION_F_OPT_TABLE) == 0) {
786
				bcopy($3.lladdr, &frame_ieee80211->i_bssid,
787
				    IEEE80211_ADDR_LEN);
788
				HOSTAPD_MATCH(BSSID, $2);
789
			} else {
790
				frame.f_bssid = $3.table;
791
				HOSTAPD_MATCH_TABLE(BSSID, $2);
792
			}
793
		}
794
		;
795
796
frmmatchrtap	: /* empty */
797
		| frmmatchrtap_l
798
		;
799
800
frmmatchrtap_l	: frmmatchrtap_l frmmatchrtapopt
801
		| frmmatchrtapopt
802
		;
803
804
frmmatchrtapopt	: RSSI unaryop percent
805
		{
806
			if (($2 == HOSTAPD_OP_GT && $3 == 100) ||
807
			    ($2 == HOSTAPD_OP_LE && $3 == 100) ||
808
			    ($2 == HOSTAPD_OP_LT && $3 == 0) ||
809
			    ($2 == HOSTAPD_OP_GE && $3 == 0)) {
810
				yyerror("absurd unary comparison");
811
				YYERROR;
812
			}
813
814
			frame.f_rssi_op = $2;
815
			frame.f_rssi = $3;
816
			HOSTAPD_MATCH_RADIOTAP(RSSI);
817
		}
818
		| TXRATE unaryop txrate
819
		{
820
			frame.f_txrate_op = $2;
821
			frame.f_txrate = $3;
822
			HOSTAPD_MATCH_RADIOTAP(RATE);
823
		}
824
		| FREQ unaryop freq
825
		{
826
			frame.f_chan_op = $2;
827
			frame.f_chan = $3;
828
			HOSTAPD_MATCH_RADIOTAP(CHANNEL);
829
		}
830
		;
831
832
frmmatchaddr	: table
833
		{
834
			if (($$.table =
835
			    hostapd_table_lookup(&hostapd_cfg, $1)) == NULL) {
836
				yyerror("undefined table <%s>", $1);
837
				free($1);
838
				YYERROR;
839
			}
840
			$$.flags = HOSTAPD_ACTION_F_OPT_TABLE;
841
			free($1);
842
		}
843
		| lladdr
844
		{
845
			bcopy($1.lladdr, $$.lladdr, IEEE80211_ADDR_LEN);
846
			$$.flags = HOSTAPD_ACTION_F_OPT_LLADDR;
847
		}
848
		;
849
850
frmactiontype	: TYPE DATA
851
		{
852
			frame_ieee80211->i_fc[0] |= IEEE80211_FC0_TYPE_DATA;
853
		}
854
		| TYPE MANAGEMENT frmactionmgmt
855
		{
856
			frame_ieee80211->i_fc[0] |= IEEE80211_FC0_TYPE_MGT;
857
		}
858
		;
859
860
frmactionmgmt	: SUBTYPE frmsubtype
861
		;
862
863
frmactiondir	: /* empty */
864
		{
865
			frame.f_action_data.a_flags |=
866
			    HOSTAPD_ACTION_F_OPT_DIR_AUTO;
867
		}
868
		| DIR frmdir
869
		;
870
871
frmactionfrom	: FROM frmactionaddr
872
		{
873
			if (($2.flags & HOSTAPD_ACTION_F_REF_M) == 0) {
874
				bcopy($2.lladdr, frame_ieee80211->i_from,
875
				    IEEE80211_ADDR_LEN);
876
			} else
877
				frame.f_action_data.a_flags |=
878
				    ($2.flags << HOSTAPD_ACTION_F_REF_FROM_S);
879
		}
880
		;
881
882
frmactionto	: TO frmactionaddr
883
		{
884
			if (($2.flags & HOSTAPD_ACTION_F_REF_M) == 0) {
885
				bcopy($2.lladdr, frame_ieee80211->i_to,
886
				    IEEE80211_ADDR_LEN);
887
			} else
888
				frame.f_action_data.a_flags |=
889
				    ($2.flags << HOSTAPD_ACTION_F_REF_TO_S);
890
		}
891
		;
892
893
frmactionbssid	: BSSID frmactionaddr
894
		{
895
			if (($2.flags & HOSTAPD_ACTION_F_REF_M) == 0) {
896
				bcopy($2.lladdr, frame_ieee80211->i_bssid,
897
				    IEEE80211_ADDR_LEN);
898
			} else
899
				frame.f_action_data.a_flags |=
900
				    ($2.flags << HOSTAPD_ACTION_F_REF_BSSID_S);
901
		}
902
		;
903
904
frmactionaddr	: lladdr
905
		{
906
			bcopy($1.lladdr, $$.lladdr, IEEE80211_ADDR_LEN);
907
			$$.flags = $1.flags;
908
		}
909
		| randaddr
910
		{
911
			$$.flags = $1.flags;
912
		}
913
		| refaddr
914
		{
915
			$$.flags = $1.flags;
916
		}
917
		;
918
919
table		: '<' STRING '>' {
920
			if (strlen($2) >= HOSTAPD_TABLE_NAMELEN) {
921
				yyerror("table name %s too long, max %u",
922
				    $2, HOSTAPD_TABLE_NAMELEN - 1);
923
				free($2);
924
				YYERROR;
925
			}
926
			$$ = $2;
927
		}
928
		;
929
930
tabledef	: TABLE table {
931
			if ((table =
932
			    hostapd_table_add(&hostapd_cfg, $2)) == NULL) {
933
				yyerror("failed to add table: %s", $2);
934
				free($2);
935
				YYERROR;
936
			}
937
			free($2);
938
		} tableopts {
939
			table = NULL;
940
		}
941
		;
942
943
tableopts	: /* empty */
944
		| tableopts_l
945
		;
946
947
tableopts_l	: tableopts_l tableopt
948
		| tableopt
949
		;
950
951
tableopt	: CONST	{
952
			if (table->t_flags & HOSTAPD_TABLE_F_CONST) {
953
				yyerror("option already specified");
954
				YYERROR;
955
			}
956
			table->t_flags |= HOSTAPD_TABLE_F_CONST;
957
		}
958
		| '{' optnl '}'
959
		| '{' optnl tableaddrlist optnl '}'
960
		;
961
962
string		: string STRING
963
		{
964
			if (asprintf(&$$, "%s %s", $1, $2) == -1)
965
				hostapd_fatal("string: asprintf");
966
			free($1);
967
			free($2);
968
		}
969
		| STRING
970
		;
971
972
varset		: STRING '=' string
973
		{
974
			char *s = $1;
975
			while (*s++) {
976
				if (isspace((unsigned char)*s)) {
977
					yyerror("macro name cannot contain "
978
					    "whitespace");
979
					YYERROR;
980
				}
981
			}
982
			if (symset($1, $3, 0) == -1)
983
				hostapd_fatal("cannot store variable");
984
			free($1);
985
			free($3);
986
		}
987
		;
988
989
refaddr		: '&' FROM
990
		{
991
			$$.flags |= HOSTAPD_ACTION_F_REF_FROM;
992
		}
993
		| '&' TO
994
		{
995
			$$.flags |= HOSTAPD_ACTION_F_REF_TO;
996
		}
997
		| '&' BSSID
998
		{
999
			$$.flags |= HOSTAPD_ACTION_F_REF_BSSID;
1000
		}
1001
		;
1002
1003
tableaddrlist	: tableaddrentry
1004
		| tableaddrlist comma tableaddrentry
1005
		;
1006
1007
tableaddrentry	: lladdr
1008
		{
1009
			if ((entry = hostapd_entry_add(table,
1010
			    $1.lladdr)) == NULL) {
1011
				yyerror("failed to add entry: %s",
1012
				    etheraddr_string($1.lladdr));
1013
				YYERROR;
1014
			}
1015
		} tableaddropt {
1016
			entry = NULL;
1017
		}
1018
		;
1019
1020
tableaddropt	: /* empty */
1021
		| assign ipv4addr ipv4netmask
1022
		{
1023
			entry->e_flags |= HOSTAPD_ENTRY_F_INADDR;
1024
			entry->e_inaddr.in_af = AF_INET;
1025
			bcopy(&$2, &entry->e_inaddr.in_v4,
1026
			    sizeof(struct in_addr));
1027
		}
1028
		| mask lladdr
1029
		{
1030
			entry->e_flags |= HOSTAPD_ENTRY_F_MASK;
1031
			bcopy($2.lladdr, entry->e_mask, IEEE80211_ADDR_LEN);
1032
1033
			/* Update entry position in the table */
1034
			hostapd_entry_update(table, entry);
1035
		}
1036
		;
1037
1038
ipv4addr	: STRING
1039
		{
1040
			if (inet_net_pton(AF_INET, $1, &$$, sizeof($$)) == -1) {
1041
				yyerror("invalid address: %s\n", $1);
1042
				free($1);
1043
				YYERROR;
1044
			}
1045
			free($1);
1046
		}
1047
		;
1048
1049
ipv4netmask	: /* empty */
1050
		{
1051
			entry->e_inaddr.in_netmask = -1;
1052
		}
1053
		| '/' NUMBER
1054
		{
1055
			if ($2 < 0 || $2 > 32) {
1056
				yyerror("netmask out of range: %lld", $2);
1057
				YYERROR;
1058
			}
1059
			entry->e_inaddr.in_netmask = $2;
1060
		}
1061
		;
1062
1063
lladdr		: STRING
1064
		{
1065
			struct ether_addr *ea;
1066
1067
			if ((ea = ether_aton($1)) == NULL) {
1068
				yyerror("invalid address: %s\n", $1);
1069
				free($1);
1070
				YYERROR;
1071
			}
1072
			free($1);
1073
1074
			bcopy(ea, $$.lladdr, IEEE80211_ADDR_LEN);
1075
			$$.flags = HOSTAPD_ACTION_F_OPT_LLADDR;
1076
		}
1077
		;
1078
1079
randaddr	: RANDOM
1080
		{
1081
			$$.flags |= HOSTAPD_ACTION_F_REF_RANDOM;
1082
		}
1083
		;
1084
1085
passive		: /* empty */
1086
		| PASSIVE
1087
		{
1088
			hostapd_cfg.c_flags |= HOSTAPD_CFG_F_IAPP_PASSIVE;
1089
		}
1090
		;
1091
1092
assign		: ARROW
1093
		;
1094
1095
mask		: '&'
1096
		;
1097
1098
comma		: /* emtpy */
1099
		| ',' optnl
1100
		;
1101
1102
optnl		: /* empty */
1103
		| '\n'
1104
		;
1105
1106
not		: /* empty */
1107
		{
1108
			$$ = 0;
1109
		}
1110
		| '!'
1111
		{
1112
			$$ = 1;
1113
		}
1114
		| NOT
1115
		{
1116
			$$ = 1;
1117
		}
1118
		;
1119
1120
unaryop		: /* any */
1121
		{
1122
			$$ = HOSTAPD_OP_EQ;
1123
		}
1124
		| '='
1125
		{
1126
			$$ = HOSTAPD_OP_EQ;
1127
		}
1128
		| '=='
1129
		{
1130
			$$ = HOSTAPD_OP_EQ;
1131
		}
1132
		| '!'
1133
		{
1134
			$$ = HOSTAPD_OP_NE;
1135
		}
1136
		| NE
1137
		{
1138
			$$ = HOSTAPD_OP_NE;
1139
		}
1140
		| LE
1141
		{
1142
			$$ = HOSTAPD_OP_LE;
1143
		}
1144
		| '<'
1145
		{
1146
			$$ = HOSTAPD_OP_LT;
1147
		}
1148
		| GE
1149
		{
1150
			$$ = HOSTAPD_OP_GE;
1151
		}
1152
		| '>'
1153
		{
1154
			$$ = HOSTAPD_OP_GT;
1155
		}
1156
		;
1157
1158
percent		: STRING
1159
		{
1160
			double val;
1161
			char *cp;
1162
1163
			val = strtod($1, &cp);
1164
			if (cp == NULL || strcmp(cp, "%") != 0 ||
1165
			    val < 0 || val > 100) {
1166
				yyerror("invalid percentage: %s", $1);
1167
				free($1);
1168
				YYERROR;
1169
			}
1170
			free($1);
1171
			$$ = val;
1172
		}
1173
		;
1174
1175
txrate		: STRING
1176
		{
1177
			double val;
1178
			char *cp;
1179
1180
			val = strtod($1, &cp) * 2;
1181
			if (cp == NULL || strcasecmp(cp, "mb") != 0 ||
1182
			    val != (int)val) {
1183
				yyerror("invalid rate: %s", $1);
1184
				free($1);
1185
				YYERROR;
1186
			}
1187
			free($1);
1188
			$$ = val;
1189
		}
1190
		;
1191
1192
freq		: STRING
1193
		{
1194
			double val;
1195
			char *cp;
1196
1197
			val = strtod($1, &cp);
1198
			if (cp != NULL) {
1199
				if (strcasecmp(cp, "ghz") == 0) {
1200
					$$ = val * 1000;
1201
				} else if (strcasecmp(cp, "mhz") == 0) {
1202
					$$ = val;
1203
				} else
1204
					cp = NULL;
1205
			}
1206
			if (cp == NULL) {
1207
				yyerror("invalid frequency: %s", $1);
1208
				free($1);
1209
				YYERROR;
1210
			}
1211
			free($1);
1212
		}
1213
		;
1214
1215
timeout		: NUMBER
1216
		{
1217
			if ($1 < 1 || $1 > LONG_MAX) {
1218
				yyerror("timeout out of range: %lld", $1);
1219
				YYERROR;
1220
			}
1221
			$$.tv_sec = $1 / 1000;
1222
			$$.tv_usec = ($1 % 1000) * 1000;
1223
		}
1224
		;
1225
%%
1226
1227
/*
1228
 * Parser and lexer
1229
 */
1230
1231
struct keywords {
1232
	char *k_name;
1233
	int k_val;
1234
};
1235
1236
int
1237
kw_cmp(const void *a, const void *b)
1238
{
1239
	return strcmp(a, ((const struct keywords *)b)->k_name);
1240
}
1241
1242
int
1243
lookup(char *token)
1244
{
1245
	/* Keep this list sorted */
1246
	static const struct keywords keywords[] = {
1247
		{ "add",		ADD },
1248
		{ "address",		ADDRESS },
1249
		{ "any",		ANY },
1250
		{ "assoc",		ASSOC },
1251
		{ "assoced",		ASSOCED },
1252
		{ "atim",		ATIM },
1253
		{ "auth",		AUTH },
1254
		{ "authed",		AUTHED },
1255
		{ "beacon",		BEACON },
1256
		{ "broadcast",		BROADCAST },
1257
		{ "bssid",		BSSID },
1258
		{ "const",		CONST },
1259
		{ "data",		DATA },
1260
		{ "deauth",		DEAUTH },
1261
		{ "delay",		DELAY },
1262
		{ "delete",		DELETE },
1263
		{ "dir",		DIR },
1264
		{ "disassoc",		DISASSOC },
1265
		{ "ds",			DS },
1266
		{ "expire",		EXPIRE },
1267
		{ "failure",		FAILURE },
1268
		{ "frame",		FRAME },
1269
		{ "freq",		FREQ },
1270
		{ "from",		FROM },
1271
		{ "handle",		HANDLE },
1272
		{ "hopper",		HOPPER },
1273
		{ "hostap",		HOSTAP },
1274
		{ "iapp",		IAPP },
1275
		{ "ie",			IE },
1276
		{ "include",		INCLUDE },
1277
		{ "inconsistent",	INCONSISTENT },
1278
		{ "interface",		INTERFACE },
1279
		{ "invalid",		INVALID },
1280
		{ "leave",		LEAVE },
1281
		{ "limit",		LIMIT },
1282
		{ "log",		LOG },
1283
		{ "management",		MANAGEMENT },
1284
		{ "mic",		MIC },
1285
		{ "mode",		MODE },
1286
		{ "multicast",		MULTICAST },
1287
		{ "no",			NO },
1288
		{ "node",		NODE },
1289
		{ "not",		NOT },
1290
		{ "notify",		NOTIFY },
1291
		{ "nwid",		NWID },
1292
		{ "on",			ON },
1293
		{ "open",		OPEN },
1294
		{ "passive",		PASSIVE },
1295
		{ "pcap",		PCAP },
1296
		{ "port",		PORT },
1297
		{ "probe",		PROBE },
1298
		{ "quick",		QUICK },
1299
		{ "radiotap",		RADIOTAP },
1300
		{ "random",		RANDOM },
1301
		{ "rate",		RATE },
1302
		{ "reason",		REASON },
1303
		{ "reassoc",		REASSOC },
1304
		{ "request",		REQUEST },
1305
		{ "required",		REQUIRED },
1306
		{ "resend",		RESEND },
1307
		{ "reserved",		RESERVED },
1308
		{ "response",		RESPONSE },
1309
		{ "roaming",		ROAMING },
1310
		{ "route",		ROUTE },
1311
		{ "rsn",		RSN },
1312
		{ "sec",		SEC },
1313
		{ "set",		SET },
1314
		{ "signal",		RSSI },
1315
		{ "skip",		SKIP },
1316
		{ "subtype",		SUBTYPE },
1317
		{ "table",		TABLE },
1318
		{ "to",			TO },
1319
		{ "toomany",		TOOMANY },
1320
		{ "ttl",		TTL },
1321
		{ "txrate",		TXRATE },
1322
		{ "type",		TYPE },
1323
		{ "unspecified",	UNSPECIFIED },
1324
		{ "usec",		USEC },
1325
		{ "verbose",		VERBOSE },
1326
		{ "with",		WITH }
1327
	};
1328
	const struct keywords *p;
1329
1330
	p = bsearch(token, keywords, sizeof(keywords) / sizeof(keywords[0]),
1331
	    sizeof(keywords[0]), kw_cmp);
1332
1333
	return (p == NULL ? STRING : p->k_val);
1334
}
1335
1336
#define MAXPUSHBACK	128
1337
1338
u_char	*parsebuf;
1339
int	 parseindex;
1340
u_char	 pushback_buffer[MAXPUSHBACK];
1341
int	 pushback_index = 0;
1342
1343
int
1344
lgetc(int quotec)
1345
{
1346
	int		c, next;
1347
1348
	if (parsebuf) {
1349
		/* Read character from the parsebuffer instead of input. */
1350
		if (parseindex >= 0) {
1351
			c = parsebuf[parseindex++];
1352
			if (c != '\0')
1353
				return (c);
1354
			parsebuf = NULL;
1355
		} else
1356
			parseindex++;
1357
	}
1358
1359
	if (pushback_index)
1360
		return (pushback_buffer[--pushback_index]);
1361
1362
	if (quotec) {
1363
		if ((c = getc(file->stream)) == EOF) {
1364
			yyerror("reached end of file while parsing "
1365
			    "quoted string");
1366
			if (file == topfile || popfile() == EOF)
1367
				return (EOF);
1368
			return (quotec);
1369
		}
1370
		return (c);
1371
	}
1372
1373
	while ((c = getc(file->stream)) == '\\') {
1374
		next = getc(file->stream);
1375
		if (next != '\n') {
1376
			c = next;
1377
			break;
1378
		}
1379
		yylval.lineno = file->lineno;
1380
		file->lineno++;
1381
	}
1382
1383
	while (c == EOF) {
1384
		if (file == topfile || popfile() == EOF)
1385
			return (EOF);
1386
		c = getc(file->stream);
1387
	}
1388
	return (c);
1389
}
1390
1391
int
1392
lungetc(int c)
1393
{
1394
	if (c == EOF)
1395
		return (EOF);
1396
	if (parsebuf) {
1397
		parseindex--;
1398
		if (parseindex >= 0)
1399
			return (c);
1400
	}
1401
	if (pushback_index < MAXPUSHBACK-1)
1402
		return (pushback_buffer[pushback_index++] = c);
1403
	else
1404
		return (EOF);
1405
}
1406
1407
int
1408
findeol(void)
1409
{
1410
	int	c;
1411
1412
	parsebuf = NULL;
1413
1414
	/* skip to either EOF or the first real EOL */
1415
	while (1) {
1416
		if (pushback_index)
1417
			c = pushback_buffer[--pushback_index];
1418
		else
1419
			c = lgetc(0);
1420
		if (c == '\n') {
1421
			file->lineno++;
1422
			break;
1423
		}
1424
		if (c == EOF)
1425
			break;
1426
	}
1427
	return (ERROR);
1428
}
1429
1430
int
1431
yylex(void)
1432
{
1433
	u_char	 buf[8096];
1434
	u_char	*p, *val;
1435
	int	 quotec, next, c;
1436
	int	 token;
1437
1438
top:
1439
	p = buf;
1440
	while ((c = lgetc(0)) == ' ' || c == '\t')
1441
		; /* nothing */
1442
1443
	yylval.lineno = file->lineno;
1444
	if (c == '#')
1445
		while ((c = lgetc(0)) != '\n' && c != EOF)
1446
			; /* nothing */
1447
	if (c == '$' && parsebuf == NULL) {
1448
		while (1) {
1449
			if ((c = lgetc(0)) == EOF)
1450
				return (0);
1451
1452
			if (p + 1 >= buf + sizeof(buf) - 1) {
1453
				yyerror("string too long");
1454
				return (findeol());
1455
			}
1456
			if (isalnum(c) || c == '_') {
1457
				*p++ = c;
1458
				continue;
1459
			}
1460
			*p = '\0';
1461
			lungetc(c);
1462
			break;
1463
		}
1464
		val = symget(buf);
1465
		if (val == NULL) {
1466
			yyerror("macro \"%s\" not defined", buf);
1467
			return (findeol());
1468
		}
1469
		parsebuf = val;
1470
		parseindex = 0;
1471
		goto top;
1472
	}
1473
1474
	switch (c) {
1475
	case '\'':
1476
	case '"':
1477
		quotec = c;
1478
		while (1) {
1479
			if ((c = lgetc(quotec)) == EOF)
1480
				return (0);
1481
			if (c == '\n') {
1482
				file->lineno++;
1483
				continue;
1484
			} else if (c == '\\') {
1485
				if ((next = lgetc(quotec)) == EOF)
1486
					return (0);
1487
				if (next == quotec || c == ' ' || c == '\t')
1488
					c = next;
1489
				else if (next == '\n') {
1490
					file->lineno++;
1491
					continue;
1492
				} else
1493
					lungetc(next);
1494
			} else if (c == quotec) {
1495
				*p = '\0';
1496
				break;
1497
			} else if (c == '\0') {
1498
				yyerror("syntax error");
1499
				return (findeol());
1500
			}
1501
			if (p + 1 >= buf + sizeof(buf) - 1) {
1502
				yyerror("string too long");
1503
				return (findeol());
1504
			}
1505
			*p++ = c;
1506
		}
1507
		yylval.v.string = strdup(buf);
1508
		if (yylval.v.string == NULL)
1509
			hostapd_fatal("yylex: strdup");
1510
		return (STRING);
1511
	case '-':
1512
		next = lgetc(0);
1513
		if (next == '>')
1514
			return (ARROW);
1515
		lungetc(next);
1516
		break;
1517
	case '!':
1518
		next = lgetc(0);
1519
		if (next == '=')
1520
			return (NE);
1521
		lungetc(next);
1522
		break;
1523
	case '<':
1524
		next = lgetc(0);
1525
		if (next == '=')
1526
			return (LE);
1527
		lungetc(next);
1528
		break;
1529
	case '>':
1530
		next = lgetc(0);
1531
		if (next == '=')
1532
			return (GE);
1533
		lungetc(next);
1534
		break;
1535
	}
1536
1537
#define allowed_to_end_number(x) \
1538
	(isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
1539
1540
	if (c == '-' || isdigit(c)) {
1541
		do {
1542
			*p++ = c;
1543
			if ((unsigned)(p-buf) >= sizeof(buf)) {
1544
				yyerror("string too long");
1545
				return (findeol());
1546
			}
1547
		} while ((c = lgetc(0)) != EOF && isdigit(c));
1548
		lungetc(c);
1549
		if (p == buf + 1 && buf[0] == '-')
1550
			goto nodigits;
1551
		if (c == EOF || allowed_to_end_number(c)) {
1552
			const char *errstr = NULL;
1553
1554
			*p = '\0';
1555
			yylval.v.number = strtonum(buf, LLONG_MIN,
1556
			    LLONG_MAX, &errstr);
1557
			if (errstr) {
1558
				yyerror("\"%s\" invalid number: %s",
1559
				    buf, errstr);
1560
				return (findeol());
1561
			}
1562
			return (NUMBER);
1563
		} else {
1564
nodigits:
1565
			while (p > buf + 1)
1566
				lungetc(*--p);
1567
			c = *--p;
1568
			if (c == '-')
1569
				return (c);
1570
		}
1571
	}
1572
1573
#define allowed_in_string(x) \
1574
	(isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
1575
	x != '{' && x != '}' && x != '<' && x != '>' && \
1576
	x != '!' && x != '=' && x != '/' && x != '#' && \
1577
	x != ','))
1578
1579
	if (isalnum(c) || c == ':' || c == '_' || c == '*') {
1580
		do {
1581
			*p++ = c;
1582
			if ((unsigned)(p-buf) >= sizeof(buf)) {
1583
				yyerror("string too long");
1584
				return (findeol());
1585
			}
1586
		} while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
1587
		lungetc(c);
1588
		*p = '\0';
1589
		if ((token = lookup(buf)) == STRING)
1590
			if ((yylval.v.string = strdup(buf)) == NULL)
1591
				hostapd_fatal("yylex: strdup");
1592
		return (token);
1593
	}
1594
	if (c == '\n') {
1595
		yylval.lineno = file->lineno;
1596
		file->lineno++;
1597
	}
1598
	if (c == EOF)
1599
		return (0);
1600
	return (c);
1601
}
1602
1603
int
1604
symset(const char *nam, const char *val, int persist)
1605
{
1606
	struct sym	*sym;
1607
1608
	TAILQ_FOREACH(sym, &symhead, entry) {
1609
		if (strcmp(nam, sym->nam) == 0)
1610
			break;
1611
	}
1612
1613
	if (sym != NULL) {
1614
		if (sym->persist == 1)
1615
			return (0);
1616
		else {
1617
			free(sym->nam);
1618
			free(sym->val);
1619
			TAILQ_REMOVE(&symhead, sym, entry);
1620
			free(sym);
1621
		}
1622
	}
1623
	if ((sym = calloc(1, sizeof(*sym))) == NULL)
1624
		return (-1);
1625
1626
	sym->nam = strdup(nam);
1627
	if (sym->nam == NULL) {
1628
		free(sym);
1629
		return (-1);
1630
	}
1631
	sym->val = strdup(val);
1632
	if (sym->val == NULL) {
1633
		free(sym->nam);
1634
		free(sym);
1635
		return (-1);
1636
	}
1637
	sym->used = 0;
1638
	sym->persist = persist;
1639
	TAILQ_INSERT_TAIL(&symhead, sym, entry);
1640
1641
	hostapd_log(HOSTAPD_LOG_DEBUG, "%s = \"%s\"", sym->nam, sym->val);
1642
1643
	return (0);
1644
}
1645
1646
int
1647
hostapd_parse_symset(char *s)
1648
{
1649
	char	*sym, *val;
1650
	int	ret;
1651
	size_t	len;
1652
1653
	if ((val = strrchr(s, '=')) == NULL)
1654
		return (-1);
1655
1656
	len = strlen(s) - strlen(val) + 1;
1657
	if ((sym = malloc(len)) == NULL)
1658
		hostapd_fatal("cmdline_symset: malloc");
1659
1660
	(void)strlcpy(sym, s, len);
1661
1662
	ret = symset(sym, val + 1, 1);
1663
1664
	free(sym);
1665
1666
	return (ret);
1667
}
1668
1669
char *
1670
symget(const char *nam)
1671
{
1672
	struct sym	*sym;
1673
1674
	TAILQ_FOREACH(sym, &symhead, entry) {
1675
		if (strcmp(nam, sym->nam) == 0) {
1676
			sym->used = 1;
1677
			return (sym->val);
1678
		}
1679
	}
1680
	return (NULL);
1681
}
1682
1683
int
1684
check_file_secrecy(int fd, const char *fname)
1685
{
1686
	struct stat	st;
1687
1688
	if (fstat(fd, &st)) {
1689
		warn("cannot stat %s", fname);
1690
		return (-1);
1691
	}
1692
	if (st.st_uid != 0 && st.st_uid != getuid()) {
1693
		warnx("%s: owner not root or current user", fname);
1694
		return (-1);
1695
	}
1696
	if (st.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)) {
1697
		warnx("%s: group writable or world read/writable", fname);
1698
		return (-1);
1699
	}
1700
	return (0);
1701
}
1702
1703
struct file *
1704
pushfile(const char *name, int secret)
1705
{
1706
	struct file	*nfile;
1707
1708
	if ((nfile = calloc(1, sizeof(struct file))) == NULL) {
1709
		warn("out of memory");
1710
		return (NULL);
1711
	}
1712
	if ((nfile->name = strdup(name)) == NULL) {
1713
		warn("out of memory");
1714
		free(nfile);
1715
		return (NULL);
1716
	}
1717
	if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
1718
		warn("%s", nfile->name);
1719
		free(nfile->name);
1720
		free(nfile);
1721
		return (NULL);
1722
	} else if (secret &&
1723
	    check_file_secrecy(fileno(nfile->stream), nfile->name)) {
1724
		fclose(nfile->stream);
1725
		free(nfile->name);
1726
		free(nfile);
1727
		return (NULL);
1728
	}
1729
	nfile->lineno = 1;
1730
	TAILQ_INSERT_TAIL(&files, nfile, entry);
1731
	return (nfile);
1732
}
1733
1734
int
1735
popfile(void)
1736
{
1737
	struct file	*prev;
1738
1739
	if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
1740
		prev->errors += file->errors;
1741
1742
	TAILQ_REMOVE(&files, file, entry);
1743
	fclose(file->stream);
1744
	free(file->name);
1745
	free(file);
1746
	file = prev;
1747
	return (file ? 0 : EOF);
1748
}
1749
1750
int
1751
hostapd_parse_file(struct hostapd_config *cfg)
1752
{
1753
	struct sym *sym, *next;
1754
	int errors = 0;
1755
	int ret;
1756
1757
	if ((file = pushfile(cfg->c_config, 1)) == NULL)
1758
		hostapd_fatal("failed to open the main config file: %s\n",
1759
		    cfg->c_config);
1760
	topfile = file;
1761
1762
	/* Init tables and data structures */
1763
	TAILQ_INIT(&cfg->c_apmes);
1764
	TAILQ_INIT(&cfg->c_tables);
1765
	TAILQ_INIT(&cfg->c_frames);
1766
	cfg->c_iapp.i_multicast.sin_addr.s_addr = INADDR_ANY;
1767
	cfg->c_iapp.i_flags = HOSTAPD_IAPP_F_DEFAULT;
1768
	cfg->c_iapp.i_ttl = IP_DEFAULT_MULTICAST_TTL;
1769
	cfg->c_apme_hopdelay.tv_sec = HOSTAPD_HOPPER_MDELAY / 1000;
1770
	cfg->c_apme_hopdelay.tv_usec = (HOSTAPD_HOPPER_MDELAY % 1000) * 1000;
1771
1772
	ret = yyparse();
1773
	errors = file->errors;
1774
	popfile();
1775
1776
	/* Free macros and check which have not been used. */
1777
	TAILQ_FOREACH_SAFE(sym, &symhead, entry, next) {
1778
		if (!sym->used)
1779
			hostapd_log(HOSTAPD_LOG_VERBOSE,
1780
			    "warning: macro '%s' not used", sym->nam);
1781
		if (!sym->persist) {
1782
			free(sym->nam);
1783
			free(sym->val);
1784
			TAILQ_REMOVE(&symhead, sym, entry);
1785
			free(sym);
1786
		}
1787
	}
1788
1789
	return (errors ? EINVAL : ret);
1790
}
1791
1792
int
1793
yyerror(const char *fmt, ...)
1794
{
1795
	va_list		 ap;
1796
	char		*msg;
1797
1798
	file->errors++;
1799
1800
	va_start(ap, fmt);
1801
	if (vasprintf(&msg, fmt, ap) == -1)
1802
		hostapd_fatal("yyerror vasprintf");
1803
	va_end(ap);
1804
	fprintf(stderr, "%s:%d: %s\n", file->name, yylval.lineno, msg);
1805
	fflush(stderr);
1806
	free(msg);
1807
1808
	return (0);
1809
}