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