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