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