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