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
start_element(void * data,const char * name,const char ** attr)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
decode_flag(struct mystate * mt,const char * p,int len)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
end_element(void * data,const char * name)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
char_data(void * data,const XML_Char * s,int len)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 *
findid(struct regdata * rdp,const void * id,int type)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
lib80211_regdomain_readconfig(struct regdata * rdp,const void * p,size_t len)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
cleanup_bands(netband_head * head)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
lib80211_regdomain_cleanup(struct regdata * rdp)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 *
lib80211_alloc_regdata(void)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
lib80211_free_regdata(struct regdata * rdp)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 *
lib80211_regdomain_findbysku(const struct regdata * rdp,enum RegdomainCode sku)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 *
lib80211_regdomain_findbyname(const struct regdata * rdp,const char * name)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 *
lib80211_country_findbycc(const struct regdata * rdp,enum ISOCountryCode cc)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 *
lib80211_country_findbyname(const struct regdata * rdp,const char * name)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