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