HAMMER - Implement volume-list command
[dragonfly.git] / sbin / ifconfig / 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: head/sbin/ifconfig/regdomain.c 200587 2009-12-15 20:44:12Z gavin $";
27 #endif /* not lint */
28
29 #include <sys/types.h>
30 #include <sys/errno.h>
31 #include <sys/mman.h>
32 #include <sys/sbuf.h>
33 #include <sys/stat.h>
34
35 #include <stdio.h>
36 #include <string.h>
37 #include <ctype.h>
38 #include <fcntl.h>
39 #include <err.h>
40 #include <unistd.h>
41
42 #include <bsdxml.h>
43
44 #include "regdomain.h"
45
46 #include <netproto/802_11/_ieee80211.h>
47
48 #define MAXLEVEL        20
49
50 struct mystate {
51         XML_Parser              parser;
52         struct regdata          *rdp;
53         struct regdomain        *rd;            /* current domain */
54         struct netband          *netband;       /* current netband */
55         struct freqband         *freqband;      /* current freqband */
56         struct country          *country;       /* current country */
57         netband_head            *curband;       /* current netband list */
58         int                     level;
59         struct sbuf             *sbuf[MAXLEVEL];
60         int                     nident;
61 };
62
63 struct ident {
64         const void *id;
65         void *p;
66         enum { DOMAIN, COUNTRY, FREQBAND } type;
67 };
68
69 static void
70 start_element(void *data, const char *name, const char **attr)
71 {
72 #define iseq(a,b)       (strcasecmp(a,b) == 0)
73         struct mystate *mt;
74         const void *id, *ref, *mode;
75         int i;
76
77         mt = data;
78         if (++mt->level == MAXLEVEL) {
79                 /* XXX force parser to abort */
80                 return;
81         }
82         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
171 static int
172 decode_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
226 static void
227 end_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         }
365 done:
366         sbuf_delete(mt->sbuf[mt->level]);
367         mt->sbuf[mt->level--] = NULL;
368 #undef iseq
369 }
370
371 static void
372 char_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
389 static void *
390 findid(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  */
403 int
404 lib80211_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
531 static void
532 cleanup_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  */
547 void
548 lib80211_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
584 struct regdata *
585 lib80211_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
634 void
635 lib80211_free_regdata(struct regdata *rdp)
636 {
637         lib80211_regdomain_cleanup(rdp);
638         free(rdp);
639 }
640
641 /*
642  * Lookup a regdomain by SKU.
643  */
644 const struct regdomain *
645 lib80211_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  */
659 const struct regdomain *
660 lib80211_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  */
674 const struct country *
675 lib80211_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  */
689 const struct country *
690 lib80211_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 }