1 /*- 2 * Copyright (c) 2008 Sam Leffler, Errno Consulting 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 #ifndef lint 26 static const char rcsid[] = "$FreeBSD$"; 27 #endif /* not lint */ 28 29 #include <sys/types.h> 30 #include <sys/errno.h> 31 #include <sys/param.h> 32 #include <sys/mman.h> 33 #include <sys/sbuf.h> 34 #include <sys/stat.h> 35 36 #include <stdio.h> 37 #include <string.h> 38 #include <ctype.h> 39 #include <fcntl.h> 40 #include <err.h> 41 #include <unistd.h> 42 43 #include <bsdxml.h> 44 45 #include "lib80211_regdomain.h" 46 47 #include <net80211/_ieee80211.h> 48 49 #define MAXLEVEL 20 50 51 struct mystate { 52 XML_Parser parser; 53 struct regdata *rdp; 54 struct regdomain *rd; /* current domain */ 55 struct netband *netband; /* current netband */ 56 struct freqband *freqband; /* current freqband */ 57 struct country *country; /* current country */ 58 netband_head *curband; /* current netband list */ 59 int level; 60 struct sbuf *sbuf[MAXLEVEL]; 61 int nident; 62 }; 63 64 struct ident { 65 const void *id; 66 void *p; 67 enum { DOMAIN, COUNTRY, FREQBAND } type; 68 }; 69 70 static void 71 start_element(void *data, const char *name, const char **attr) 72 { 73 #define iseq(a,b) (strcasecmp(a,b) == 0) 74 struct mystate *mt; 75 const void *id, *ref, *mode; 76 int i; 77 78 mt = data; 79 if (++mt->level == MAXLEVEL) { 80 /* XXX force parser to abort */ 81 return; 82 } 83 mt->sbuf[mt->level] = sbuf_new_auto(); 84 id = ref = mode = NULL; 85 for (i = 0; attr[i] != NULL; i += 2) { 86 if (iseq(attr[i], "id")) { 87 id = attr[i+1]; 88 } else if (iseq(attr[i], "ref")) { 89 ref = attr[i+1]; 90 } else if (iseq(attr[i], "mode")) { 91 mode = attr[i+1]; 92 } else 93 printf("%*.*s[%s = %s]\n", mt->level + 1, 94 mt->level + 1, "", attr[i], attr[i+1]); 95 } 96 if (iseq(name, "rd") && mt->rd == NULL) { 97 if (mt->country == NULL) { 98 mt->rd = calloc(1, sizeof(struct regdomain)); 99 mt->rd->name = strdup(id); 100 mt->nident++; 101 LIST_INSERT_HEAD(&mt->rdp->domains, mt->rd, next); 102 } else 103 mt->country->rd = (void *)strdup(ref); 104 return; 105 } 106 if (iseq(name, "defcc") && mt->rd != NULL) { 107 mt->rd->cc = (void *)strdup(ref); 108 return; 109 } 110 if (iseq(name, "netband") && mt->curband == NULL && mt->rd != NULL) { 111 if (mode == NULL) { 112 warnx("no mode for netband at line %ld", 113 XML_GetCurrentLineNumber(mt->parser)); 114 return; 115 } 116 if (iseq(mode, "11b")) 117 mt->curband = &mt->rd->bands_11b; 118 else if (iseq(mode, "11g")) 119 mt->curband = &mt->rd->bands_11g; 120 else if (iseq(mode, "11a")) 121 mt->curband = &mt->rd->bands_11a; 122 else if (iseq(mode, "11ng")) 123 mt->curband = &mt->rd->bands_11ng; 124 else if (iseq(mode, "11na")) 125 mt->curband = &mt->rd->bands_11na; 126 else if (iseq(mode, "11ac")) 127 mt->curband = &mt->rd->bands_11ac; 128 else if (iseq(mode, "11acg")) 129 mt->curband = &mt->rd->bands_11acg; 130 else 131 warnx("unknown mode \"%s\" at line %ld", 132 __DECONST(char *, mode), 133 XML_GetCurrentLineNumber(mt->parser)); 134 return; 135 } 136 if (iseq(name, "band") && mt->netband == NULL) { 137 if (mt->curband == NULL) { 138 warnx("band without enclosing netband at line %ld", 139 XML_GetCurrentLineNumber(mt->parser)); 140 return; 141 } 142 mt->netband = calloc(1, sizeof(struct netband)); 143 LIST_INSERT_HEAD(mt->curband, mt->netband, next); 144 return; 145 } 146 if (iseq(name, "freqband") && mt->freqband == NULL && mt->netband != NULL) { 147 /* XXX handle inlines and merge into table? */ 148 if (mt->netband->band != NULL) { 149 warnx("duplicate freqband at line %ld ignored", 150 XML_GetCurrentLineNumber(mt->parser)); 151 /* XXX complain */ 152 } else 153 mt->netband->band = (void *)strdup(ref); 154 return; 155 } 156 157 if (iseq(name, "country") && mt->country == NULL) { 158 mt->country = calloc(1, sizeof(struct country)); 159 mt->country->isoname = strdup(id); 160 mt->country->code = NO_COUNTRY; 161 mt->nident++; 162 LIST_INSERT_HEAD(&mt->rdp->countries, mt->country, next); 163 return; 164 } 165 166 if (iseq(name, "freqband") && mt->freqband == NULL) { 167 mt->freqband = calloc(1, sizeof(struct freqband)); 168 mt->freqband->id = strdup(id); 169 mt->nident++; 170 LIST_INSERT_HEAD(&mt->rdp->freqbands, mt->freqband, next); 171 return; 172 } 173 #undef iseq 174 } 175 176 static int 177 decode_flag(struct mystate *mt, const char *p, int len) 178 { 179 #define iseq(a,b) (strcasecmp(a,b) == 0) 180 static const struct { 181 const char *name; 182 int len; 183 uint32_t value; 184 } flags[] = { 185 #define FLAG(x) { #x, sizeof(#x)-1, x } 186 FLAG(IEEE80211_CHAN_A), 187 FLAG(IEEE80211_CHAN_B), 188 FLAG(IEEE80211_CHAN_G), 189 FLAG(IEEE80211_CHAN_HT20), 190 FLAG(IEEE80211_CHAN_HT40), 191 FLAG(IEEE80211_CHAN_VHT20), 192 FLAG(IEEE80211_CHAN_VHT40), 193 FLAG(IEEE80211_CHAN_VHT80), 194 FLAG(IEEE80211_CHAN_VHT160), 195 /* 196 * XXX VHT80P80? This likely should be done by 197 * 80MHz chan logic in net80211 / ifconfig. 198 */ 199 FLAG(IEEE80211_CHAN_ST), 200 FLAG(IEEE80211_CHAN_TURBO), 201 FLAG(IEEE80211_CHAN_PASSIVE), 202 FLAG(IEEE80211_CHAN_DFS), 203 FLAG(IEEE80211_CHAN_CCK), 204 FLAG(IEEE80211_CHAN_OFDM), 205 FLAG(IEEE80211_CHAN_2GHZ), 206 FLAG(IEEE80211_CHAN_5GHZ), 207 FLAG(IEEE80211_CHAN_DYN), 208 FLAG(IEEE80211_CHAN_GFSK), 209 FLAG(IEEE80211_CHAN_GSM), 210 FLAG(IEEE80211_CHAN_STURBO), 211 FLAG(IEEE80211_CHAN_HALF), 212 FLAG(IEEE80211_CHAN_QUARTER), 213 FLAG(IEEE80211_CHAN_HT40U), 214 FLAG(IEEE80211_CHAN_HT40D), 215 FLAG(IEEE80211_CHAN_4MSXMIT), 216 FLAG(IEEE80211_CHAN_NOADHOC), 217 FLAG(IEEE80211_CHAN_NOHOSTAP), 218 FLAG(IEEE80211_CHAN_11D), 219 FLAG(IEEE80211_CHAN_FHSS), 220 FLAG(IEEE80211_CHAN_PUREG), 221 FLAG(IEEE80211_CHAN_108A), 222 FLAG(IEEE80211_CHAN_108G), 223 #undef FLAG 224 { "ECM", 3, REQ_ECM }, 225 { "INDOOR", 6, REQ_INDOOR }, 226 { "OUTDOOR", 7, REQ_OUTDOOR }, 227 }; 228 unsigned int i; 229 230 for (i = 0; i < nitems(flags); i++) 231 if (len == flags[i].len && iseq(p, flags[i].name)) 232 return flags[i].value; 233 warnx("unknown flag \"%.*s\" at line %ld ignored", 234 len, p, XML_GetCurrentLineNumber(mt->parser)); 235 return 0; 236 #undef iseq 237 } 238 239 static void 240 end_element(void *data, const char *name) 241 { 242 #define iseq(a,b) (strcasecmp(a,b) == 0) 243 struct mystate *mt; 244 int len; 245 char *p; 246 247 mt = data; 248 sbuf_finish(mt->sbuf[mt->level]); 249 p = sbuf_data(mt->sbuf[mt->level]); 250 len = sbuf_len(mt->sbuf[mt->level]); 251 252 /* <freqband>...</freqband> */ 253 if (iseq(name, "freqstart") && mt->freqband != NULL) { 254 mt->freqband->freqStart = strtoul(p, NULL, 0); 255 goto done; 256 } 257 if (iseq(name, "freqend") && mt->freqband != NULL) { 258 mt->freqband->freqEnd = strtoul(p, NULL, 0); 259 goto done; 260 } 261 if (iseq(name, "chanwidth") && mt->freqband != NULL) { 262 mt->freqband->chanWidth = strtoul(p, NULL, 0); 263 goto done; 264 } 265 if (iseq(name, "chansep") && mt->freqband != NULL) { 266 mt->freqband->chanSep = strtoul(p, NULL, 0); 267 goto done; 268 } 269 if (iseq(name, "flags")) { 270 if (mt->freqband != NULL) 271 mt->freqband->flags |= decode_flag(mt, p, len); 272 else if (mt->netband != NULL) 273 mt->netband->flags |= decode_flag(mt, p, len); 274 else { 275 warnx("flags without freqband or netband at line %ld ignored", 276 XML_GetCurrentLineNumber(mt->parser)); 277 } 278 goto done; 279 } 280 281 /* <rd> ... </rd> */ 282 if (iseq(name, "name") && mt->rd != NULL) { 283 mt->rd->name = strdup(p); 284 goto done; 285 } 286 if (iseq(name, "sku") && mt->rd != NULL) { 287 mt->rd->sku = strtoul(p, NULL, 0); 288 goto done; 289 } 290 if (iseq(name, "netband") && mt->rd != NULL) { 291 mt->curband = NULL; 292 goto done; 293 } 294 295 /* <band> ... </band> */ 296 if (iseq(name, "freqband") && mt->netband != NULL) { 297 /* XXX handle inline freqbands */ 298 goto done; 299 } 300 if (iseq(name, "maxpower") && mt->netband != NULL) { 301 mt->netband->maxPower = strtoul(p, NULL, 0); 302 goto done; 303 } 304 if (iseq(name, "maxpowerdfs") && mt->netband != NULL) { 305 mt->netband->maxPowerDFS = strtoul(p, NULL, 0); 306 goto done; 307 } 308 if (iseq(name, "maxantgain") && mt->netband != NULL) { 309 mt->netband->maxAntGain = strtoul(p, NULL, 0); 310 goto done; 311 } 312 313 /* <country>...</country> */ 314 if (iseq(name, "isocc") && mt->country != NULL) { 315 mt->country->code = strtoul(p, NULL, 0); 316 goto done; 317 } 318 if (iseq(name, "name") && mt->country != NULL) { 319 mt->country->name = strdup(p); 320 goto done; 321 } 322 323 if (len != 0) { 324 warnx("unexpected XML token \"%s\" data \"%s\" at line %ld", 325 name, p, XML_GetCurrentLineNumber(mt->parser)); 326 /* XXX goto done? */ 327 } 328 /* </freqband> */ 329 if (iseq(name, "freqband") && mt->freqband != NULL) { 330 /* XXX must have start/end frequencies */ 331 /* XXX must have channel width/sep */ 332 mt->freqband = NULL; 333 goto done; 334 } 335 /* </rd> */ 336 if (iseq(name, "rd") && mt->rd != NULL) { 337 mt->rd = NULL; 338 goto done; 339 } 340 /* </band> */ 341 if (iseq(name, "band") && mt->netband != NULL) { 342 if (mt->netband->band == NULL) { 343 warnx("no freqbands for band at line %ld", 344 XML_GetCurrentLineNumber(mt->parser)); 345 } 346 if (mt->netband->maxPower == 0) { 347 warnx("no maxpower for band at line %ld", 348 XML_GetCurrentLineNumber(mt->parser)); 349 } 350 /* default max power w/ DFS to max power */ 351 if (mt->netband->maxPowerDFS == 0) 352 mt->netband->maxPowerDFS = mt->netband->maxPower; 353 mt->netband = NULL; 354 goto done; 355 } 356 /* </netband> */ 357 if (iseq(name, "netband") && mt->netband != NULL) { 358 mt->curband = NULL; 359 goto done; 360 } 361 /* </country> */ 362 if (iseq(name, "country") && mt->country != NULL) { 363 /* XXX NO_COUNTRY should be in the net80211 country enum */ 364 if ((int) mt->country->code == NO_COUNTRY) { 365 warnx("no ISO cc for country at line %ld", 366 XML_GetCurrentLineNumber(mt->parser)); 367 } 368 if (mt->country->name == NULL) { 369 warnx("no name for country at line %ld", 370 XML_GetCurrentLineNumber(mt->parser)); 371 } 372 if (mt->country->rd == NULL) { 373 warnx("no regdomain reference for country at line %ld", 374 XML_GetCurrentLineNumber(mt->parser)); 375 } 376 mt->country = NULL; 377 goto done; 378 } 379 done: 380 sbuf_delete(mt->sbuf[mt->level]); 381 mt->sbuf[mt->level--] = NULL; 382 #undef iseq 383 } 384 385 static void 386 char_data(void *data, const XML_Char *s, int len) 387 { 388 struct mystate *mt; 389 const char *b, *e; 390 391 mt = data; 392 393 b = s; 394 e = s + len-1; 395 for (; isspace(*b) && b < e; b++) 396 ; 397 for (; isspace(*e) && e > b; e++) 398 ; 399 if (e != b || (*b != '\0' && !isspace(*b))) 400 sbuf_bcat(mt->sbuf[mt->level], b, e-b+1); 401 } 402 403 static void * 404 findid(struct regdata *rdp, const void *id, int type) 405 { 406 struct ident *ip; 407 408 for (ip = rdp->ident; ip->id != NULL; ip++) 409 if ((int) ip->type == type && strcasecmp(ip->id, id) == 0) 410 return ip->p; 411 return NULL; 412 } 413 414 /* 415 * Parse an regdomain XML configuration and build the internal representation. 416 */ 417 int 418 lib80211_regdomain_readconfig(struct regdata *rdp, const void *p, size_t len) 419 { 420 struct mystate *mt; 421 struct regdomain *dp; 422 struct country *cp; 423 struct freqband *fp; 424 struct netband *nb; 425 const void *id; 426 int i, errors; 427 428 memset(rdp, 0, sizeof(struct regdata)); 429 mt = calloc(1, sizeof(struct mystate)); 430 if (mt == NULL) 431 return ENOMEM; 432 /* parse the XML input */ 433 mt->rdp = rdp; 434 mt->parser = XML_ParserCreate(NULL); 435 XML_SetUserData(mt->parser, mt); 436 XML_SetElementHandler(mt->parser, start_element, end_element); 437 XML_SetCharacterDataHandler(mt->parser, char_data); 438 if (XML_Parse(mt->parser, p, len, 1) != XML_STATUS_OK) { 439 warnx("%s: %s at line %ld", __func__, 440 XML_ErrorString(XML_GetErrorCode(mt->parser)), 441 XML_GetCurrentLineNumber(mt->parser)); 442 return -1; 443 } 444 XML_ParserFree(mt->parser); 445 446 /* setup the identifer table */ 447 rdp->ident = calloc(sizeof(struct ident), mt->nident + 1); 448 if (rdp->ident == NULL) 449 return ENOMEM; 450 free(mt); 451 452 errors = 0; 453 i = 0; 454 LIST_FOREACH(dp, &rdp->domains, next) { 455 rdp->ident[i].id = dp->name; 456 rdp->ident[i].p = dp; 457 rdp->ident[i].type = DOMAIN; 458 i++; 459 } 460 LIST_FOREACH(fp, &rdp->freqbands, next) { 461 rdp->ident[i].id = fp->id; 462 rdp->ident[i].p = fp; 463 rdp->ident[i].type = FREQBAND; 464 i++; 465 } 466 LIST_FOREACH(cp, &rdp->countries, next) { 467 rdp->ident[i].id = cp->isoname; 468 rdp->ident[i].p = cp; 469 rdp->ident[i].type = COUNTRY; 470 i++; 471 } 472 473 /* patch references */ 474 LIST_FOREACH(dp, &rdp->domains, next) { 475 if (dp->cc != NULL) { 476 id = dp->cc; 477 dp->cc = findid(rdp, id, COUNTRY); 478 if (dp->cc == NULL) { 479 warnx("undefined country \"%s\"", 480 __DECONST(char *, id)); 481 errors++; 482 } 483 free(__DECONST(char *, id)); 484 } 485 LIST_FOREACH(nb, &dp->bands_11b, next) { 486 id = findid(rdp, nb->band, FREQBAND); 487 if (id == NULL) { 488 warnx("undefined 11b band \"%s\"", 489 __DECONST(char *, nb->band)); 490 errors++; 491 } 492 nb->band = id; 493 } 494 LIST_FOREACH(nb, &dp->bands_11g, next) { 495 id = findid(rdp, nb->band, FREQBAND); 496 if (id == NULL) { 497 warnx("undefined 11g band \"%s\"", 498 __DECONST(char *, nb->band)); 499 errors++; 500 } 501 nb->band = id; 502 } 503 LIST_FOREACH(nb, &dp->bands_11a, next) { 504 id = findid(rdp, nb->band, FREQBAND); 505 if (id == NULL) { 506 warnx("undefined 11a band \"%s\"", 507 __DECONST(char *, nb->band)); 508 errors++; 509 } 510 nb->band = id; 511 } 512 LIST_FOREACH(nb, &dp->bands_11ng, next) { 513 id = findid(rdp, nb->band, FREQBAND); 514 if (id == NULL) { 515 warnx("undefined 11ng band \"%s\"", 516 __DECONST(char *, nb->band)); 517 errors++; 518 } 519 nb->band = id; 520 } 521 LIST_FOREACH(nb, &dp->bands_11na, next) { 522 id = findid(rdp, nb->band, FREQBAND); 523 if (id == NULL) { 524 warnx("undefined 11na band \"%s\"", 525 __DECONST(char *, nb->band)); 526 errors++; 527 } 528 nb->band = id; 529 } 530 LIST_FOREACH(nb, &dp->bands_11ac, next) { 531 id = findid(rdp, nb->band, FREQBAND); 532 if (id == NULL) { 533 warnx("undefined 11ac band \"%s\"", 534 __DECONST(char *, nb->band)); 535 errors++; 536 } 537 nb->band = id; 538 } 539 LIST_FOREACH(nb, &dp->bands_11acg, next) { 540 id = findid(rdp, nb->band, FREQBAND); 541 if (id == NULL) { 542 warnx("undefined 11acg band \"%s\"", 543 __DECONST(char *, nb->band)); 544 errors++; 545 } 546 nb->band = id; 547 } 548 } 549 LIST_FOREACH(cp, &rdp->countries, next) { 550 id = cp->rd; 551 cp->rd = findid(rdp, id, DOMAIN); 552 if (cp->rd == NULL) { 553 warnx("undefined country \"%s\"", 554 __DECONST(char *, id)); 555 errors++; 556 } 557 free(__DECONST(char *, id)); 558 } 559 560 return errors ? EINVAL : 0; 561 } 562 563 static void 564 cleanup_bands(netband_head *head) 565 { 566 struct netband *nb; 567 568 for (;;) { 569 nb = LIST_FIRST(head); 570 if (nb == NULL) 571 break; 572 LIST_REMOVE(nb, next); 573 free(nb); 574 } 575 } 576 577 /* 578 * Cleanup state/resources for a previously parsed regdomain database. 579 */ 580 void 581 lib80211_regdomain_cleanup(struct regdata *rdp) 582 { 583 584 free(rdp->ident); 585 rdp->ident = NULL; 586 for (;;) { 587 struct regdomain *dp = LIST_FIRST(&rdp->domains); 588 if (dp == NULL) 589 break; 590 LIST_REMOVE(dp, next); 591 cleanup_bands(&dp->bands_11b); 592 cleanup_bands(&dp->bands_11g); 593 cleanup_bands(&dp->bands_11a); 594 cleanup_bands(&dp->bands_11ng); 595 cleanup_bands(&dp->bands_11na); 596 cleanup_bands(&dp->bands_11ac); 597 cleanup_bands(&dp->bands_11acg); 598 if (dp->name != NULL) 599 free(__DECONST(char *, dp->name)); 600 } 601 for (;;) { 602 struct country *cp = LIST_FIRST(&rdp->countries); 603 if (cp == NULL) 604 break; 605 LIST_REMOVE(cp, next); 606 if (cp->name != NULL) 607 free(__DECONST(char *, cp->name)); 608 free(cp); 609 } 610 for (;;) { 611 struct freqband *fp = LIST_FIRST(&rdp->freqbands); 612 if (fp == NULL) 613 break; 614 LIST_REMOVE(fp, next); 615 free(fp); 616 } 617 } 618 619 struct regdata * 620 lib80211_alloc_regdata(void) 621 { 622 struct regdata *rdp; 623 struct stat sb; 624 void *xml; 625 int fd; 626 627 rdp = calloc(1, sizeof(struct regdata)); 628 629 fd = open(_PATH_REGDOMAIN, O_RDONLY); 630 if (fd < 0) { 631 #ifdef DEBUG 632 warn("%s: open(%s)", __func__, _PATH_REGDOMAIN); 633 #endif 634 free(rdp); 635 return NULL; 636 } 637 if (fstat(fd, &sb) < 0) { 638 #ifdef DEBUG 639 warn("%s: fstat(%s)", __func__, _PATH_REGDOMAIN); 640 #endif 641 close(fd); 642 free(rdp); 643 return NULL; 644 } 645 xml = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0); 646 if (xml == MAP_FAILED) { 647 #ifdef DEBUG 648 warn("%s: mmap", __func__); 649 #endif 650 close(fd); 651 free(rdp); 652 return NULL; 653 } 654 if (lib80211_regdomain_readconfig(rdp, xml, sb.st_size) != 0) { 655 #ifdef DEBUG 656 warn("%s: error reading regulatory database", __func__); 657 #endif 658 munmap(xml, sb.st_size); 659 close(fd); 660 free(rdp); 661 return NULL; 662 } 663 munmap(xml, sb.st_size); 664 close(fd); 665 666 return rdp; 667 } 668 669 void 670 lib80211_free_regdata(struct regdata *rdp) 671 { 672 lib80211_regdomain_cleanup(rdp); 673 free(rdp); 674 } 675 676 /* 677 * Lookup a regdomain by SKU. 678 */ 679 const struct regdomain * 680 lib80211_regdomain_findbysku(const struct regdata *rdp, enum RegdomainCode sku) 681 { 682 const struct regdomain *dp; 683 684 LIST_FOREACH(dp, &rdp->domains, next) { 685 if (dp->sku == sku) 686 return dp; 687 } 688 return NULL; 689 } 690 691 /* 692 * Lookup a regdomain by name. 693 */ 694 const struct regdomain * 695 lib80211_regdomain_findbyname(const struct regdata *rdp, const char *name) 696 { 697 const struct regdomain *dp; 698 699 LIST_FOREACH(dp, &rdp->domains, next) { 700 if (strcasecmp(dp->name, name) == 0) 701 return dp; 702 } 703 return NULL; 704 } 705 706 /* 707 * Lookup a country by ISO country code. 708 */ 709 const struct country * 710 lib80211_country_findbycc(const struct regdata *rdp, enum ISOCountryCode cc) 711 { 712 const struct country *cp; 713 714 LIST_FOREACH(cp, &rdp->countries, next) { 715 if (cp->code == cc) 716 return cp; 717 } 718 return NULL; 719 } 720 721 /* 722 * Lookup a country by ISO/long name. 723 */ 724 const struct country * 725 lib80211_country_findbyname(const struct regdata *rdp, const char *name) 726 { 727 const struct country *cp; 728 int len; 729 730 len = strlen(name); 731 LIST_FOREACH(cp, &rdp->countries, next) { 732 if (strcasecmp(cp->isoname, name) == 0) 733 return cp; 734 } 735 LIST_FOREACH(cp, &rdp->countries, next) { 736 if (strncasecmp(cp->name, name, len) == 0) 737 return cp; 738 } 739 return NULL; 740 } 741