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