1127be1a7SAdrian Chadd /*- 2127be1a7SAdrian Chadd * Copyright (c) 2008 Sam Leffler, Errno Consulting 3127be1a7SAdrian Chadd * All rights reserved. 4127be1a7SAdrian Chadd * 5127be1a7SAdrian Chadd * Redistribution and use in source and binary forms, with or without 6127be1a7SAdrian Chadd * modification, are permitted provided that the following conditions 7127be1a7SAdrian Chadd * are met: 8127be1a7SAdrian Chadd * 1. Redistributions of source code must retain the above copyright 9127be1a7SAdrian Chadd * notice, this list of conditions and the following disclaimer. 10127be1a7SAdrian Chadd * 2. Redistributions in binary form must reproduce the above copyright 11127be1a7SAdrian Chadd * notice, this list of conditions and the following disclaimer in the 12127be1a7SAdrian Chadd * documentation and/or other materials provided with the distribution. 13127be1a7SAdrian Chadd * 14127be1a7SAdrian Chadd * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15127be1a7SAdrian Chadd * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16127be1a7SAdrian Chadd * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17127be1a7SAdrian Chadd * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18127be1a7SAdrian Chadd * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19127be1a7SAdrian Chadd * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20127be1a7SAdrian Chadd * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21127be1a7SAdrian Chadd * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22127be1a7SAdrian Chadd * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23127be1a7SAdrian Chadd * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24127be1a7SAdrian Chadd */ 25127be1a7SAdrian Chadd #ifndef lint 26127be1a7SAdrian Chadd static const char rcsid[] = "$FreeBSD$"; 27127be1a7SAdrian Chadd #endif /* not lint */ 28127be1a7SAdrian Chadd 29127be1a7SAdrian Chadd #include <sys/types.h> 30127be1a7SAdrian Chadd #include <sys/errno.h> 31127be1a7SAdrian Chadd #include <sys/param.h> 32127be1a7SAdrian Chadd #include <sys/mman.h> 33127be1a7SAdrian Chadd #include <sys/sbuf.h> 34127be1a7SAdrian Chadd #include <sys/stat.h> 35127be1a7SAdrian Chadd 36127be1a7SAdrian Chadd #include <stdio.h> 37127be1a7SAdrian Chadd #include <string.h> 38127be1a7SAdrian Chadd #include <ctype.h> 39127be1a7SAdrian Chadd #include <fcntl.h> 40127be1a7SAdrian Chadd #include <err.h> 41127be1a7SAdrian Chadd #include <unistd.h> 42127be1a7SAdrian Chadd 43127be1a7SAdrian Chadd #include <bsdxml.h> 44127be1a7SAdrian Chadd 45127be1a7SAdrian Chadd #include "lib80211_regdomain.h" 46127be1a7SAdrian Chadd 47127be1a7SAdrian Chadd #include <net80211/_ieee80211.h> 48127be1a7SAdrian Chadd 49127be1a7SAdrian Chadd #define MAXLEVEL 20 50127be1a7SAdrian Chadd 51127be1a7SAdrian Chadd struct mystate { 52127be1a7SAdrian Chadd XML_Parser parser; 53127be1a7SAdrian Chadd struct regdata *rdp; 54127be1a7SAdrian Chadd struct regdomain *rd; /* current domain */ 55127be1a7SAdrian Chadd struct netband *netband; /* current netband */ 56127be1a7SAdrian Chadd struct freqband *freqband; /* current freqband */ 57127be1a7SAdrian Chadd struct country *country; /* current country */ 58127be1a7SAdrian Chadd netband_head *curband; /* current netband list */ 59127be1a7SAdrian Chadd int level; 60127be1a7SAdrian Chadd struct sbuf *sbuf[MAXLEVEL]; 61127be1a7SAdrian Chadd int nident; 62127be1a7SAdrian Chadd }; 63127be1a7SAdrian Chadd 64127be1a7SAdrian Chadd struct ident { 65127be1a7SAdrian Chadd const void *id; 66127be1a7SAdrian Chadd void *p; 67127be1a7SAdrian Chadd enum { DOMAIN, COUNTRY, FREQBAND } type; 68127be1a7SAdrian Chadd }; 69127be1a7SAdrian Chadd 70127be1a7SAdrian Chadd static void 71127be1a7SAdrian Chadd start_element(void *data, const char *name, const char **attr) 72127be1a7SAdrian Chadd { 73127be1a7SAdrian Chadd #define iseq(a,b) (strcasecmp(a,b) == 0) 74127be1a7SAdrian Chadd struct mystate *mt; 75127be1a7SAdrian Chadd const void *id, *ref, *mode; 76127be1a7SAdrian Chadd int i; 77127be1a7SAdrian Chadd 78127be1a7SAdrian Chadd mt = data; 79127be1a7SAdrian Chadd if (++mt->level == MAXLEVEL) { 80127be1a7SAdrian Chadd /* XXX force parser to abort */ 81127be1a7SAdrian Chadd return; 82127be1a7SAdrian Chadd } 83127be1a7SAdrian Chadd mt->sbuf[mt->level] = sbuf_new_auto(); 84127be1a7SAdrian Chadd id = ref = mode = NULL; 85127be1a7SAdrian Chadd for (i = 0; attr[i] != NULL; i += 2) { 86127be1a7SAdrian Chadd if (iseq(attr[i], "id")) { 87127be1a7SAdrian Chadd id = attr[i+1]; 88127be1a7SAdrian Chadd } else if (iseq(attr[i], "ref")) { 89127be1a7SAdrian Chadd ref = attr[i+1]; 90127be1a7SAdrian Chadd } else if (iseq(attr[i], "mode")) { 91127be1a7SAdrian Chadd mode = attr[i+1]; 92127be1a7SAdrian Chadd } else 93127be1a7SAdrian Chadd printf("%*.*s[%s = %s]\n", mt->level + 1, 94127be1a7SAdrian Chadd mt->level + 1, "", attr[i], attr[i+1]); 95127be1a7SAdrian Chadd } 96127be1a7SAdrian Chadd if (iseq(name, "rd") && mt->rd == NULL) { 97127be1a7SAdrian Chadd if (mt->country == NULL) { 98127be1a7SAdrian Chadd mt->rd = calloc(1, sizeof(struct regdomain)); 99127be1a7SAdrian Chadd mt->rd->name = strdup(id); 100127be1a7SAdrian Chadd mt->nident++; 101127be1a7SAdrian Chadd LIST_INSERT_HEAD(&mt->rdp->domains, mt->rd, next); 102127be1a7SAdrian Chadd } else 103127be1a7SAdrian Chadd mt->country->rd = (void *)strdup(ref); 104127be1a7SAdrian Chadd return; 105127be1a7SAdrian Chadd } 106127be1a7SAdrian Chadd if (iseq(name, "defcc") && mt->rd != NULL) { 107127be1a7SAdrian Chadd mt->rd->cc = (void *)strdup(ref); 108127be1a7SAdrian Chadd return; 109127be1a7SAdrian Chadd } 110127be1a7SAdrian Chadd if (iseq(name, "netband") && mt->curband == NULL && mt->rd != NULL) { 111127be1a7SAdrian Chadd if (mode == NULL) { 112127be1a7SAdrian Chadd warnx("no mode for netband at line %ld", 113127be1a7SAdrian Chadd XML_GetCurrentLineNumber(mt->parser)); 114127be1a7SAdrian Chadd return; 115127be1a7SAdrian Chadd } 116127be1a7SAdrian Chadd if (iseq(mode, "11b")) 117127be1a7SAdrian Chadd mt->curband = &mt->rd->bands_11b; 118127be1a7SAdrian Chadd else if (iseq(mode, "11g")) 119127be1a7SAdrian Chadd mt->curband = &mt->rd->bands_11g; 120127be1a7SAdrian Chadd else if (iseq(mode, "11a")) 121127be1a7SAdrian Chadd mt->curband = &mt->rd->bands_11a; 122127be1a7SAdrian Chadd else if (iseq(mode, "11ng")) 123127be1a7SAdrian Chadd mt->curband = &mt->rd->bands_11ng; 124127be1a7SAdrian Chadd else if (iseq(mode, "11na")) 125127be1a7SAdrian Chadd mt->curband = &mt->rd->bands_11na; 126*36ea5759SAdrian Chadd else if (iseq(mode, "11ac")) 127*36ea5759SAdrian Chadd mt->curband = &mt->rd->bands_11ac; 128*36ea5759SAdrian Chadd else if (iseq(mode, "11acg")) 129*36ea5759SAdrian Chadd mt->curband = &mt->rd->bands_11acg; 130127be1a7SAdrian Chadd else 131127be1a7SAdrian Chadd warnx("unknown mode \"%s\" at line %ld", 132127be1a7SAdrian Chadd __DECONST(char *, mode), 133127be1a7SAdrian Chadd XML_GetCurrentLineNumber(mt->parser)); 134127be1a7SAdrian Chadd return; 135127be1a7SAdrian Chadd } 136127be1a7SAdrian Chadd if (iseq(name, "band") && mt->netband == NULL) { 137127be1a7SAdrian Chadd if (mt->curband == NULL) { 138127be1a7SAdrian Chadd warnx("band without enclosing netband at line %ld", 139127be1a7SAdrian Chadd XML_GetCurrentLineNumber(mt->parser)); 140127be1a7SAdrian Chadd return; 141127be1a7SAdrian Chadd } 142127be1a7SAdrian Chadd mt->netband = calloc(1, sizeof(struct netband)); 143127be1a7SAdrian Chadd LIST_INSERT_HEAD(mt->curband, mt->netband, next); 144127be1a7SAdrian Chadd return; 145127be1a7SAdrian Chadd } 146127be1a7SAdrian Chadd if (iseq(name, "freqband") && mt->freqband == NULL && mt->netband != NULL) { 147127be1a7SAdrian Chadd /* XXX handle inlines and merge into table? */ 148127be1a7SAdrian Chadd if (mt->netband->band != NULL) { 149127be1a7SAdrian Chadd warnx("duplicate freqband at line %ld ignored", 150127be1a7SAdrian Chadd XML_GetCurrentLineNumber(mt->parser)); 151127be1a7SAdrian Chadd /* XXX complain */ 152127be1a7SAdrian Chadd } else 153127be1a7SAdrian Chadd mt->netband->band = (void *)strdup(ref); 154127be1a7SAdrian Chadd return; 155127be1a7SAdrian Chadd } 156127be1a7SAdrian Chadd 157127be1a7SAdrian Chadd if (iseq(name, "country") && mt->country == NULL) { 158127be1a7SAdrian Chadd mt->country = calloc(1, sizeof(struct country)); 159127be1a7SAdrian Chadd mt->country->isoname = strdup(id); 160127be1a7SAdrian Chadd mt->country->code = NO_COUNTRY; 161127be1a7SAdrian Chadd mt->nident++; 162127be1a7SAdrian Chadd LIST_INSERT_HEAD(&mt->rdp->countries, mt->country, next); 163127be1a7SAdrian Chadd return; 164127be1a7SAdrian Chadd } 165127be1a7SAdrian Chadd 166127be1a7SAdrian Chadd if (iseq(name, "freqband") && mt->freqband == NULL) { 167127be1a7SAdrian Chadd mt->freqband = calloc(1, sizeof(struct freqband)); 168127be1a7SAdrian Chadd mt->freqband->id = strdup(id); 169127be1a7SAdrian Chadd mt->nident++; 170127be1a7SAdrian Chadd LIST_INSERT_HEAD(&mt->rdp->freqbands, mt->freqband, next); 171127be1a7SAdrian Chadd return; 172127be1a7SAdrian Chadd } 173127be1a7SAdrian Chadd #undef iseq 174127be1a7SAdrian Chadd } 175127be1a7SAdrian Chadd 176127be1a7SAdrian Chadd static int 177127be1a7SAdrian Chadd decode_flag(struct mystate *mt, const char *p, int len) 178127be1a7SAdrian Chadd { 179127be1a7SAdrian Chadd #define iseq(a,b) (strcasecmp(a,b) == 0) 180127be1a7SAdrian Chadd static const struct { 181127be1a7SAdrian Chadd const char *name; 182127be1a7SAdrian Chadd int len; 183127be1a7SAdrian Chadd uint32_t value; 184127be1a7SAdrian Chadd } flags[] = { 185127be1a7SAdrian Chadd #define FLAG(x) { #x, sizeof(#x)-1, x } 186127be1a7SAdrian Chadd FLAG(IEEE80211_CHAN_A), 187127be1a7SAdrian Chadd FLAG(IEEE80211_CHAN_B), 188127be1a7SAdrian Chadd FLAG(IEEE80211_CHAN_G), 189127be1a7SAdrian Chadd FLAG(IEEE80211_CHAN_HT20), 190127be1a7SAdrian Chadd FLAG(IEEE80211_CHAN_HT40), 191*36ea5759SAdrian Chadd FLAG(IEEE80211_CHAN_VHT20), 192*36ea5759SAdrian Chadd FLAG(IEEE80211_CHAN_VHT40), 193*36ea5759SAdrian Chadd FLAG(IEEE80211_CHAN_VHT80), 194*36ea5759SAdrian Chadd /* 195*36ea5759SAdrian Chadd * XXX VHT80_80? This likely should be done by 196*36ea5759SAdrian Chadd * 80MHz chan logic in net80211 / ifconfig. 197*36ea5759SAdrian Chadd */ 198*36ea5759SAdrian Chadd FLAG(IEEE80211_CHAN_VHT160), 199127be1a7SAdrian Chadd FLAG(IEEE80211_CHAN_ST), 200127be1a7SAdrian Chadd FLAG(IEEE80211_CHAN_TURBO), 201127be1a7SAdrian Chadd FLAG(IEEE80211_CHAN_PASSIVE), 202127be1a7SAdrian Chadd FLAG(IEEE80211_CHAN_DFS), 203127be1a7SAdrian Chadd FLAG(IEEE80211_CHAN_CCK), 204127be1a7SAdrian Chadd FLAG(IEEE80211_CHAN_OFDM), 205127be1a7SAdrian Chadd FLAG(IEEE80211_CHAN_2GHZ), 206127be1a7SAdrian Chadd FLAG(IEEE80211_CHAN_5GHZ), 207127be1a7SAdrian Chadd FLAG(IEEE80211_CHAN_DYN), 208127be1a7SAdrian Chadd FLAG(IEEE80211_CHAN_GFSK), 209127be1a7SAdrian Chadd FLAG(IEEE80211_CHAN_GSM), 210127be1a7SAdrian Chadd FLAG(IEEE80211_CHAN_STURBO), 211127be1a7SAdrian Chadd FLAG(IEEE80211_CHAN_HALF), 212127be1a7SAdrian Chadd FLAG(IEEE80211_CHAN_QUARTER), 213127be1a7SAdrian Chadd FLAG(IEEE80211_CHAN_HT40U), 214127be1a7SAdrian Chadd FLAG(IEEE80211_CHAN_HT40D), 215127be1a7SAdrian Chadd FLAG(IEEE80211_CHAN_4MSXMIT), 216127be1a7SAdrian Chadd FLAG(IEEE80211_CHAN_NOADHOC), 217127be1a7SAdrian Chadd FLAG(IEEE80211_CHAN_NOHOSTAP), 218127be1a7SAdrian Chadd FLAG(IEEE80211_CHAN_11D), 219127be1a7SAdrian Chadd FLAG(IEEE80211_CHAN_FHSS), 220127be1a7SAdrian Chadd FLAG(IEEE80211_CHAN_PUREG), 221127be1a7SAdrian Chadd FLAG(IEEE80211_CHAN_108A), 222127be1a7SAdrian Chadd FLAG(IEEE80211_CHAN_108G), 223127be1a7SAdrian Chadd #undef FLAG 224127be1a7SAdrian Chadd { "ECM", 3, REQ_ECM }, 225127be1a7SAdrian Chadd { "INDOOR", 6, REQ_INDOOR }, 226127be1a7SAdrian Chadd { "OUTDOOR", 7, REQ_OUTDOOR }, 227127be1a7SAdrian Chadd }; 228127be1a7SAdrian Chadd unsigned int i; 229127be1a7SAdrian Chadd 230127be1a7SAdrian Chadd for (i = 0; i < nitems(flags); i++) 231127be1a7SAdrian Chadd if (len == flags[i].len && iseq(p, flags[i].name)) 232127be1a7SAdrian Chadd return flags[i].value; 233127be1a7SAdrian Chadd warnx("unknown flag \"%.*s\" at line %ld ignored", 234127be1a7SAdrian Chadd len, p, XML_GetCurrentLineNumber(mt->parser)); 235127be1a7SAdrian Chadd return 0; 236127be1a7SAdrian Chadd #undef iseq 237127be1a7SAdrian Chadd } 238127be1a7SAdrian Chadd 239127be1a7SAdrian Chadd static void 240127be1a7SAdrian Chadd end_element(void *data, const char *name) 241127be1a7SAdrian Chadd { 242127be1a7SAdrian Chadd #define iseq(a,b) (strcasecmp(a,b) == 0) 243127be1a7SAdrian Chadd struct mystate *mt; 244127be1a7SAdrian Chadd int len; 245127be1a7SAdrian Chadd char *p; 246127be1a7SAdrian Chadd 247127be1a7SAdrian Chadd mt = data; 248127be1a7SAdrian Chadd sbuf_finish(mt->sbuf[mt->level]); 249127be1a7SAdrian Chadd p = sbuf_data(mt->sbuf[mt->level]); 250127be1a7SAdrian Chadd len = sbuf_len(mt->sbuf[mt->level]); 251127be1a7SAdrian Chadd 252127be1a7SAdrian Chadd /* <freqband>...</freqband> */ 253127be1a7SAdrian Chadd if (iseq(name, "freqstart") && mt->freqband != NULL) { 254127be1a7SAdrian Chadd mt->freqband->freqStart = strtoul(p, NULL, 0); 255127be1a7SAdrian Chadd goto done; 256127be1a7SAdrian Chadd } 257127be1a7SAdrian Chadd if (iseq(name, "freqend") && mt->freqband != NULL) { 258127be1a7SAdrian Chadd mt->freqband->freqEnd = strtoul(p, NULL, 0); 259127be1a7SAdrian Chadd goto done; 260127be1a7SAdrian Chadd } 261127be1a7SAdrian Chadd if (iseq(name, "chanwidth") && mt->freqband != NULL) { 262127be1a7SAdrian Chadd mt->freqband->chanWidth = strtoul(p, NULL, 0); 263127be1a7SAdrian Chadd goto done; 264127be1a7SAdrian Chadd } 265127be1a7SAdrian Chadd if (iseq(name, "chansep") && mt->freqband != NULL) { 266127be1a7SAdrian Chadd mt->freqband->chanSep = strtoul(p, NULL, 0); 267127be1a7SAdrian Chadd goto done; 268127be1a7SAdrian Chadd } 269127be1a7SAdrian Chadd if (iseq(name, "flags")) { 270127be1a7SAdrian Chadd if (mt->freqband != NULL) 271127be1a7SAdrian Chadd mt->freqband->flags |= decode_flag(mt, p, len); 272127be1a7SAdrian Chadd else if (mt->netband != NULL) 273127be1a7SAdrian Chadd mt->netband->flags |= decode_flag(mt, p, len); 274127be1a7SAdrian Chadd else { 275127be1a7SAdrian Chadd warnx("flags without freqband or netband at line %ld ignored", 276127be1a7SAdrian Chadd XML_GetCurrentLineNumber(mt->parser)); 277127be1a7SAdrian Chadd } 278127be1a7SAdrian Chadd goto done; 279127be1a7SAdrian Chadd } 280127be1a7SAdrian Chadd 281127be1a7SAdrian Chadd /* <rd> ... </rd> */ 282127be1a7SAdrian Chadd if (iseq(name, "name") && mt->rd != NULL) { 283127be1a7SAdrian Chadd mt->rd->name = strdup(p); 284127be1a7SAdrian Chadd goto done; 285127be1a7SAdrian Chadd } 286127be1a7SAdrian Chadd if (iseq(name, "sku") && mt->rd != NULL) { 287127be1a7SAdrian Chadd mt->rd->sku = strtoul(p, NULL, 0); 288127be1a7SAdrian Chadd goto done; 289127be1a7SAdrian Chadd } 290127be1a7SAdrian Chadd if (iseq(name, "netband") && mt->rd != NULL) { 291127be1a7SAdrian Chadd mt->curband = NULL; 292127be1a7SAdrian Chadd goto done; 293127be1a7SAdrian Chadd } 294127be1a7SAdrian Chadd 295127be1a7SAdrian Chadd /* <band> ... </band> */ 296127be1a7SAdrian Chadd if (iseq(name, "freqband") && mt->netband != NULL) { 297127be1a7SAdrian Chadd /* XXX handle inline freqbands */ 298127be1a7SAdrian Chadd goto done; 299127be1a7SAdrian Chadd } 300127be1a7SAdrian Chadd if (iseq(name, "maxpower") && mt->netband != NULL) { 301127be1a7SAdrian Chadd mt->netband->maxPower = strtoul(p, NULL, 0); 302127be1a7SAdrian Chadd goto done; 303127be1a7SAdrian Chadd } 304127be1a7SAdrian Chadd if (iseq(name, "maxpowerdfs") && mt->netband != NULL) { 305127be1a7SAdrian Chadd mt->netband->maxPowerDFS = strtoul(p, NULL, 0); 306127be1a7SAdrian Chadd goto done; 307127be1a7SAdrian Chadd } 308127be1a7SAdrian Chadd if (iseq(name, "maxantgain") && mt->netband != NULL) { 309127be1a7SAdrian Chadd mt->netband->maxAntGain = strtoul(p, NULL, 0); 310127be1a7SAdrian Chadd goto done; 311127be1a7SAdrian Chadd } 312127be1a7SAdrian Chadd 313127be1a7SAdrian Chadd /* <country>...</country> */ 314127be1a7SAdrian Chadd if (iseq(name, "isocc") && mt->country != NULL) { 315127be1a7SAdrian Chadd mt->country->code = strtoul(p, NULL, 0); 316127be1a7SAdrian Chadd goto done; 317127be1a7SAdrian Chadd } 318127be1a7SAdrian Chadd if (iseq(name, "name") && mt->country != NULL) { 319127be1a7SAdrian Chadd mt->country->name = strdup(p); 320127be1a7SAdrian Chadd goto done; 321127be1a7SAdrian Chadd } 322127be1a7SAdrian Chadd 323127be1a7SAdrian Chadd if (len != 0) { 324127be1a7SAdrian Chadd warnx("unexpected XML token \"%s\" data \"%s\" at line %ld", 325127be1a7SAdrian Chadd name, p, XML_GetCurrentLineNumber(mt->parser)); 326127be1a7SAdrian Chadd /* XXX goto done? */ 327127be1a7SAdrian Chadd } 328127be1a7SAdrian Chadd /* </freqband> */ 329127be1a7SAdrian Chadd if (iseq(name, "freqband") && mt->freqband != NULL) { 330127be1a7SAdrian Chadd /* XXX must have start/end frequencies */ 331127be1a7SAdrian Chadd /* XXX must have channel width/sep */ 332127be1a7SAdrian Chadd mt->freqband = NULL; 333127be1a7SAdrian Chadd goto done; 334127be1a7SAdrian Chadd } 335127be1a7SAdrian Chadd /* </rd> */ 336127be1a7SAdrian Chadd if (iseq(name, "rd") && mt->rd != NULL) { 337127be1a7SAdrian Chadd mt->rd = NULL; 338127be1a7SAdrian Chadd goto done; 339127be1a7SAdrian Chadd } 340127be1a7SAdrian Chadd /* </band> */ 341127be1a7SAdrian Chadd if (iseq(name, "band") && mt->netband != NULL) { 342127be1a7SAdrian Chadd if (mt->netband->band == NULL) { 343127be1a7SAdrian Chadd warnx("no freqbands for band at line %ld", 344127be1a7SAdrian Chadd XML_GetCurrentLineNumber(mt->parser)); 345127be1a7SAdrian Chadd } 346127be1a7SAdrian Chadd if (mt->netband->maxPower == 0) { 347127be1a7SAdrian Chadd warnx("no maxpower for band at line %ld", 348127be1a7SAdrian Chadd XML_GetCurrentLineNumber(mt->parser)); 349127be1a7SAdrian Chadd } 350127be1a7SAdrian Chadd /* default max power w/ DFS to max power */ 351127be1a7SAdrian Chadd if (mt->netband->maxPowerDFS == 0) 352127be1a7SAdrian Chadd mt->netband->maxPowerDFS = mt->netband->maxPower; 353127be1a7SAdrian Chadd mt->netband = NULL; 354127be1a7SAdrian Chadd goto done; 355127be1a7SAdrian Chadd } 356127be1a7SAdrian Chadd /* </netband> */ 357127be1a7SAdrian Chadd if (iseq(name, "netband") && mt->netband != NULL) { 358127be1a7SAdrian Chadd mt->curband = NULL; 359127be1a7SAdrian Chadd goto done; 360127be1a7SAdrian Chadd } 361127be1a7SAdrian Chadd /* </country> */ 362127be1a7SAdrian Chadd if (iseq(name, "country") && mt->country != NULL) { 363127be1a7SAdrian Chadd /* XXX NO_COUNTRY should be in the net80211 country enum */ 364127be1a7SAdrian Chadd if ((int) mt->country->code == NO_COUNTRY) { 365127be1a7SAdrian Chadd warnx("no ISO cc for country at line %ld", 366127be1a7SAdrian Chadd XML_GetCurrentLineNumber(mt->parser)); 367127be1a7SAdrian Chadd } 368127be1a7SAdrian Chadd if (mt->country->name == NULL) { 369127be1a7SAdrian Chadd warnx("no name for country at line %ld", 370127be1a7SAdrian Chadd XML_GetCurrentLineNumber(mt->parser)); 371127be1a7SAdrian Chadd } 372127be1a7SAdrian Chadd if (mt->country->rd == NULL) { 373127be1a7SAdrian Chadd warnx("no regdomain reference for country at line %ld", 374127be1a7SAdrian Chadd XML_GetCurrentLineNumber(mt->parser)); 375127be1a7SAdrian Chadd } 376127be1a7SAdrian Chadd mt->country = NULL; 377127be1a7SAdrian Chadd goto done; 378127be1a7SAdrian Chadd } 379127be1a7SAdrian Chadd done: 380127be1a7SAdrian Chadd sbuf_delete(mt->sbuf[mt->level]); 381127be1a7SAdrian Chadd mt->sbuf[mt->level--] = NULL; 382127be1a7SAdrian Chadd #undef iseq 383127be1a7SAdrian Chadd } 384127be1a7SAdrian Chadd 385127be1a7SAdrian Chadd static void 386127be1a7SAdrian Chadd char_data(void *data, const XML_Char *s, int len) 387127be1a7SAdrian Chadd { 388127be1a7SAdrian Chadd struct mystate *mt; 389127be1a7SAdrian Chadd const char *b, *e; 390127be1a7SAdrian Chadd 391127be1a7SAdrian Chadd mt = data; 392127be1a7SAdrian Chadd 393127be1a7SAdrian Chadd b = s; 394127be1a7SAdrian Chadd e = s + len-1; 395127be1a7SAdrian Chadd for (; isspace(*b) && b < e; b++) 396127be1a7SAdrian Chadd ; 397127be1a7SAdrian Chadd for (; isspace(*e) && e > b; e++) 398127be1a7SAdrian Chadd ; 399127be1a7SAdrian Chadd if (e != b || (*b != '\0' && !isspace(*b))) 400127be1a7SAdrian Chadd sbuf_bcat(mt->sbuf[mt->level], b, e-b+1); 401127be1a7SAdrian Chadd } 402127be1a7SAdrian Chadd 403127be1a7SAdrian Chadd static void * 404127be1a7SAdrian Chadd findid(struct regdata *rdp, const void *id, int type) 405127be1a7SAdrian Chadd { 406127be1a7SAdrian Chadd struct ident *ip; 407127be1a7SAdrian Chadd 408127be1a7SAdrian Chadd for (ip = rdp->ident; ip->id != NULL; ip++) 409127be1a7SAdrian Chadd if ((int) ip->type == type && strcasecmp(ip->id, id) == 0) 410127be1a7SAdrian Chadd return ip->p; 411127be1a7SAdrian Chadd return NULL; 412127be1a7SAdrian Chadd } 413127be1a7SAdrian Chadd 414127be1a7SAdrian Chadd /* 415127be1a7SAdrian Chadd * Parse an regdomain XML configuration and build the internal representation. 416127be1a7SAdrian Chadd */ 417127be1a7SAdrian Chadd int 418127be1a7SAdrian Chadd lib80211_regdomain_readconfig(struct regdata *rdp, const void *p, size_t len) 419127be1a7SAdrian Chadd { 420127be1a7SAdrian Chadd struct mystate *mt; 421127be1a7SAdrian Chadd struct regdomain *dp; 422127be1a7SAdrian Chadd struct country *cp; 423127be1a7SAdrian Chadd struct freqband *fp; 424127be1a7SAdrian Chadd struct netband *nb; 425127be1a7SAdrian Chadd const void *id; 426127be1a7SAdrian Chadd int i, errors; 427127be1a7SAdrian Chadd 428127be1a7SAdrian Chadd memset(rdp, 0, sizeof(struct regdata)); 429127be1a7SAdrian Chadd mt = calloc(1, sizeof(struct mystate)); 430127be1a7SAdrian Chadd if (mt == NULL) 431127be1a7SAdrian Chadd return ENOMEM; 432127be1a7SAdrian Chadd /* parse the XML input */ 433127be1a7SAdrian Chadd mt->rdp = rdp; 434127be1a7SAdrian Chadd mt->parser = XML_ParserCreate(NULL); 435127be1a7SAdrian Chadd XML_SetUserData(mt->parser, mt); 436127be1a7SAdrian Chadd XML_SetElementHandler(mt->parser, start_element, end_element); 437127be1a7SAdrian Chadd XML_SetCharacterDataHandler(mt->parser, char_data); 438127be1a7SAdrian Chadd if (XML_Parse(mt->parser, p, len, 1) != XML_STATUS_OK) { 439127be1a7SAdrian Chadd warnx("%s: %s at line %ld", __func__, 440127be1a7SAdrian Chadd XML_ErrorString(XML_GetErrorCode(mt->parser)), 441127be1a7SAdrian Chadd XML_GetCurrentLineNumber(mt->parser)); 442127be1a7SAdrian Chadd return -1; 443127be1a7SAdrian Chadd } 444127be1a7SAdrian Chadd XML_ParserFree(mt->parser); 445127be1a7SAdrian Chadd 446127be1a7SAdrian Chadd /* setup the identifer table */ 447127be1a7SAdrian Chadd rdp->ident = calloc(sizeof(struct ident), mt->nident + 1); 448127be1a7SAdrian Chadd if (rdp->ident == NULL) 449127be1a7SAdrian Chadd return ENOMEM; 450127be1a7SAdrian Chadd free(mt); 451127be1a7SAdrian Chadd 452127be1a7SAdrian Chadd errors = 0; 453127be1a7SAdrian Chadd i = 0; 454127be1a7SAdrian Chadd LIST_FOREACH(dp, &rdp->domains, next) { 455127be1a7SAdrian Chadd rdp->ident[i].id = dp->name; 456127be1a7SAdrian Chadd rdp->ident[i].p = dp; 457127be1a7SAdrian Chadd rdp->ident[i].type = DOMAIN; 458127be1a7SAdrian Chadd i++; 459127be1a7SAdrian Chadd } 460127be1a7SAdrian Chadd LIST_FOREACH(fp, &rdp->freqbands, next) { 461127be1a7SAdrian Chadd rdp->ident[i].id = fp->id; 462127be1a7SAdrian Chadd rdp->ident[i].p = fp; 463127be1a7SAdrian Chadd rdp->ident[i].type = FREQBAND; 464127be1a7SAdrian Chadd i++; 465127be1a7SAdrian Chadd } 466127be1a7SAdrian Chadd LIST_FOREACH(cp, &rdp->countries, next) { 467127be1a7SAdrian Chadd rdp->ident[i].id = cp->isoname; 468127be1a7SAdrian Chadd rdp->ident[i].p = cp; 469127be1a7SAdrian Chadd rdp->ident[i].type = COUNTRY; 470127be1a7SAdrian Chadd i++; 471127be1a7SAdrian Chadd } 472127be1a7SAdrian Chadd 473127be1a7SAdrian Chadd /* patch references */ 474127be1a7SAdrian Chadd LIST_FOREACH(dp, &rdp->domains, next) { 475127be1a7SAdrian Chadd if (dp->cc != NULL) { 476127be1a7SAdrian Chadd id = dp->cc; 477127be1a7SAdrian Chadd dp->cc = findid(rdp, id, COUNTRY); 478127be1a7SAdrian Chadd if (dp->cc == NULL) { 479127be1a7SAdrian Chadd warnx("undefined country \"%s\"", 480127be1a7SAdrian Chadd __DECONST(char *, id)); 481127be1a7SAdrian Chadd errors++; 482127be1a7SAdrian Chadd } 483127be1a7SAdrian Chadd free(__DECONST(char *, id)); 484127be1a7SAdrian Chadd } 485127be1a7SAdrian Chadd LIST_FOREACH(nb, &dp->bands_11b, next) { 486127be1a7SAdrian Chadd id = findid(rdp, nb->band, FREQBAND); 487127be1a7SAdrian Chadd if (id == NULL) { 488127be1a7SAdrian Chadd warnx("undefined 11b band \"%s\"", 489127be1a7SAdrian Chadd __DECONST(char *, nb->band)); 490127be1a7SAdrian Chadd errors++; 491127be1a7SAdrian Chadd } 492127be1a7SAdrian Chadd nb->band = id; 493127be1a7SAdrian Chadd } 494127be1a7SAdrian Chadd LIST_FOREACH(nb, &dp->bands_11g, next) { 495127be1a7SAdrian Chadd id = findid(rdp, nb->band, FREQBAND); 496127be1a7SAdrian Chadd if (id == NULL) { 497127be1a7SAdrian Chadd warnx("undefined 11g band \"%s\"", 498127be1a7SAdrian Chadd __DECONST(char *, nb->band)); 499127be1a7SAdrian Chadd errors++; 500127be1a7SAdrian Chadd } 501127be1a7SAdrian Chadd nb->band = id; 502127be1a7SAdrian Chadd } 503127be1a7SAdrian Chadd LIST_FOREACH(nb, &dp->bands_11a, next) { 504127be1a7SAdrian Chadd id = findid(rdp, nb->band, FREQBAND); 505127be1a7SAdrian Chadd if (id == NULL) { 506127be1a7SAdrian Chadd warnx("undefined 11a band \"%s\"", 507127be1a7SAdrian Chadd __DECONST(char *, nb->band)); 508127be1a7SAdrian Chadd errors++; 509127be1a7SAdrian Chadd } 510127be1a7SAdrian Chadd nb->band = id; 511127be1a7SAdrian Chadd } 512127be1a7SAdrian Chadd LIST_FOREACH(nb, &dp->bands_11ng, next) { 513127be1a7SAdrian Chadd id = findid(rdp, nb->band, FREQBAND); 514127be1a7SAdrian Chadd if (id == NULL) { 515127be1a7SAdrian Chadd warnx("undefined 11ng band \"%s\"", 516127be1a7SAdrian Chadd __DECONST(char *, nb->band)); 517127be1a7SAdrian Chadd errors++; 518127be1a7SAdrian Chadd } 519127be1a7SAdrian Chadd nb->band = id; 520127be1a7SAdrian Chadd } 521127be1a7SAdrian Chadd LIST_FOREACH(nb, &dp->bands_11na, next) { 522127be1a7SAdrian Chadd id = findid(rdp, nb->band, FREQBAND); 523127be1a7SAdrian Chadd if (id == NULL) { 524127be1a7SAdrian Chadd warnx("undefined 11na band \"%s\"", 525127be1a7SAdrian Chadd __DECONST(char *, nb->band)); 526127be1a7SAdrian Chadd errors++; 527127be1a7SAdrian Chadd } 528127be1a7SAdrian Chadd nb->band = id; 529127be1a7SAdrian Chadd } 530*36ea5759SAdrian Chadd LIST_FOREACH(nb, &dp->bands_11ac, next) { 531*36ea5759SAdrian Chadd id = findid(rdp, nb->band, FREQBAND); 532*36ea5759SAdrian Chadd if (id == NULL) { 533*36ea5759SAdrian Chadd warnx("undefined 11ac band \"%s\"", 534*36ea5759SAdrian Chadd __DECONST(char *, nb->band)); 535*36ea5759SAdrian Chadd errors++; 536*36ea5759SAdrian Chadd } 537*36ea5759SAdrian Chadd nb->band = id; 538*36ea5759SAdrian Chadd } 539*36ea5759SAdrian Chadd LIST_FOREACH(nb, &dp->bands_11acg, next) { 540*36ea5759SAdrian Chadd id = findid(rdp, nb->band, FREQBAND); 541*36ea5759SAdrian Chadd if (id == NULL) { 542*36ea5759SAdrian Chadd warnx("undefined 11acg band \"%s\"", 543*36ea5759SAdrian Chadd __DECONST(char *, nb->band)); 544*36ea5759SAdrian Chadd errors++; 545*36ea5759SAdrian Chadd } 546*36ea5759SAdrian Chadd nb->band = id; 547*36ea5759SAdrian Chadd } 548127be1a7SAdrian Chadd } 549127be1a7SAdrian Chadd LIST_FOREACH(cp, &rdp->countries, next) { 550127be1a7SAdrian Chadd id = cp->rd; 551127be1a7SAdrian Chadd cp->rd = findid(rdp, id, DOMAIN); 552127be1a7SAdrian Chadd if (cp->rd == NULL) { 553127be1a7SAdrian Chadd warnx("undefined country \"%s\"", 554127be1a7SAdrian Chadd __DECONST(char *, id)); 555127be1a7SAdrian Chadd errors++; 556127be1a7SAdrian Chadd } 557127be1a7SAdrian Chadd free(__DECONST(char *, id)); 558127be1a7SAdrian Chadd } 559127be1a7SAdrian Chadd 560127be1a7SAdrian Chadd return errors ? EINVAL : 0; 561127be1a7SAdrian Chadd } 562127be1a7SAdrian Chadd 563127be1a7SAdrian Chadd static void 564127be1a7SAdrian Chadd cleanup_bands(netband_head *head) 565127be1a7SAdrian Chadd { 566127be1a7SAdrian Chadd struct netband *nb; 567127be1a7SAdrian Chadd 568127be1a7SAdrian Chadd for (;;) { 569127be1a7SAdrian Chadd nb = LIST_FIRST(head); 570127be1a7SAdrian Chadd if (nb == NULL) 571127be1a7SAdrian Chadd break; 572127be1a7SAdrian Chadd free(nb); 573127be1a7SAdrian Chadd } 574127be1a7SAdrian Chadd } 575127be1a7SAdrian Chadd 576127be1a7SAdrian Chadd /* 577127be1a7SAdrian Chadd * Cleanup state/resources for a previously parsed regdomain database. 578127be1a7SAdrian Chadd */ 579127be1a7SAdrian Chadd void 580127be1a7SAdrian Chadd lib80211_regdomain_cleanup(struct regdata *rdp) 581127be1a7SAdrian Chadd { 582127be1a7SAdrian Chadd 583127be1a7SAdrian Chadd free(rdp->ident); 584127be1a7SAdrian Chadd rdp->ident = NULL; 585127be1a7SAdrian Chadd for (;;) { 586127be1a7SAdrian Chadd struct regdomain *dp = LIST_FIRST(&rdp->domains); 587127be1a7SAdrian Chadd if (dp == NULL) 588127be1a7SAdrian Chadd break; 589127be1a7SAdrian Chadd LIST_REMOVE(dp, next); 590127be1a7SAdrian Chadd cleanup_bands(&dp->bands_11b); 591127be1a7SAdrian Chadd cleanup_bands(&dp->bands_11g); 592127be1a7SAdrian Chadd cleanup_bands(&dp->bands_11a); 593127be1a7SAdrian Chadd cleanup_bands(&dp->bands_11ng); 594127be1a7SAdrian Chadd cleanup_bands(&dp->bands_11na); 595*36ea5759SAdrian Chadd cleanup_bands(&dp->bands_11ac); 596*36ea5759SAdrian Chadd cleanup_bands(&dp->bands_11acg); 597127be1a7SAdrian Chadd if (dp->name != NULL) 598127be1a7SAdrian Chadd free(__DECONST(char *, dp->name)); 599127be1a7SAdrian Chadd } 600127be1a7SAdrian Chadd for (;;) { 601127be1a7SAdrian Chadd struct country *cp = LIST_FIRST(&rdp->countries); 602127be1a7SAdrian Chadd if (cp == NULL) 603127be1a7SAdrian Chadd break; 604127be1a7SAdrian Chadd LIST_REMOVE(cp, next); 605127be1a7SAdrian Chadd if (cp->name != NULL) 606127be1a7SAdrian Chadd free(__DECONST(char *, cp->name)); 607127be1a7SAdrian Chadd free(cp); 608127be1a7SAdrian Chadd } 609127be1a7SAdrian Chadd for (;;) { 610127be1a7SAdrian Chadd struct freqband *fp = LIST_FIRST(&rdp->freqbands); 611127be1a7SAdrian Chadd if (fp == NULL) 612127be1a7SAdrian Chadd break; 613127be1a7SAdrian Chadd LIST_REMOVE(fp, next); 614127be1a7SAdrian Chadd free(fp); 615127be1a7SAdrian Chadd } 616127be1a7SAdrian Chadd } 617127be1a7SAdrian Chadd 618127be1a7SAdrian Chadd struct regdata * 619127be1a7SAdrian Chadd lib80211_alloc_regdata(void) 620127be1a7SAdrian Chadd { 621127be1a7SAdrian Chadd struct regdata *rdp; 622127be1a7SAdrian Chadd struct stat sb; 623127be1a7SAdrian Chadd void *xml; 624127be1a7SAdrian Chadd int fd; 625127be1a7SAdrian Chadd 626127be1a7SAdrian Chadd rdp = calloc(1, sizeof(struct regdata)); 627127be1a7SAdrian Chadd 628127be1a7SAdrian Chadd fd = open(_PATH_REGDOMAIN, O_RDONLY); 629127be1a7SAdrian Chadd if (fd < 0) { 630127be1a7SAdrian Chadd #ifdef DEBUG 631127be1a7SAdrian Chadd warn("%s: open(%s)", __func__, _PATH_REGDOMAIN); 632127be1a7SAdrian Chadd #endif 633127be1a7SAdrian Chadd free(rdp); 634127be1a7SAdrian Chadd return NULL; 635127be1a7SAdrian Chadd } 636127be1a7SAdrian Chadd if (fstat(fd, &sb) < 0) { 637127be1a7SAdrian Chadd #ifdef DEBUG 638127be1a7SAdrian Chadd warn("%s: fstat(%s)", __func__, _PATH_REGDOMAIN); 639127be1a7SAdrian Chadd #endif 640127be1a7SAdrian Chadd close(fd); 641127be1a7SAdrian Chadd free(rdp); 642127be1a7SAdrian Chadd return NULL; 643127be1a7SAdrian Chadd } 644127be1a7SAdrian Chadd xml = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0); 645127be1a7SAdrian Chadd if (xml == MAP_FAILED) { 646127be1a7SAdrian Chadd #ifdef DEBUG 647127be1a7SAdrian Chadd warn("%s: mmap", __func__); 648127be1a7SAdrian Chadd #endif 649127be1a7SAdrian Chadd close(fd); 650127be1a7SAdrian Chadd free(rdp); 651127be1a7SAdrian Chadd return NULL; 652127be1a7SAdrian Chadd } 653127be1a7SAdrian Chadd if (lib80211_regdomain_readconfig(rdp, xml, sb.st_size) != 0) { 654127be1a7SAdrian Chadd #ifdef DEBUG 655127be1a7SAdrian Chadd warn("%s: error reading regulatory database", __func__); 656127be1a7SAdrian Chadd #endif 657127be1a7SAdrian Chadd munmap(xml, sb.st_size); 658127be1a7SAdrian Chadd close(fd); 659127be1a7SAdrian Chadd free(rdp); 660127be1a7SAdrian Chadd return NULL; 661127be1a7SAdrian Chadd } 662127be1a7SAdrian Chadd munmap(xml, sb.st_size); 663127be1a7SAdrian Chadd close(fd); 664127be1a7SAdrian Chadd 665127be1a7SAdrian Chadd return rdp; 666127be1a7SAdrian Chadd } 667127be1a7SAdrian Chadd 668127be1a7SAdrian Chadd void 669127be1a7SAdrian Chadd lib80211_free_regdata(struct regdata *rdp) 670127be1a7SAdrian Chadd { 671127be1a7SAdrian Chadd lib80211_regdomain_cleanup(rdp); 672127be1a7SAdrian Chadd free(rdp); 673127be1a7SAdrian Chadd } 674127be1a7SAdrian Chadd 675127be1a7SAdrian Chadd /* 676127be1a7SAdrian Chadd * Lookup a regdomain by SKU. 677127be1a7SAdrian Chadd */ 678127be1a7SAdrian Chadd const struct regdomain * 679127be1a7SAdrian Chadd lib80211_regdomain_findbysku(const struct regdata *rdp, enum RegdomainCode sku) 680127be1a7SAdrian Chadd { 681127be1a7SAdrian Chadd const struct regdomain *dp; 682127be1a7SAdrian Chadd 683127be1a7SAdrian Chadd LIST_FOREACH(dp, &rdp->domains, next) { 684127be1a7SAdrian Chadd if (dp->sku == sku) 685127be1a7SAdrian Chadd return dp; 686127be1a7SAdrian Chadd } 687127be1a7SAdrian Chadd return NULL; 688127be1a7SAdrian Chadd } 689127be1a7SAdrian Chadd 690127be1a7SAdrian Chadd /* 691127be1a7SAdrian Chadd * Lookup a regdomain by name. 692127be1a7SAdrian Chadd */ 693127be1a7SAdrian Chadd const struct regdomain * 694127be1a7SAdrian Chadd lib80211_regdomain_findbyname(const struct regdata *rdp, const char *name) 695127be1a7SAdrian Chadd { 696127be1a7SAdrian Chadd const struct regdomain *dp; 697127be1a7SAdrian Chadd 698127be1a7SAdrian Chadd LIST_FOREACH(dp, &rdp->domains, next) { 699127be1a7SAdrian Chadd if (strcasecmp(dp->name, name) == 0) 700127be1a7SAdrian Chadd return dp; 701127be1a7SAdrian Chadd } 702127be1a7SAdrian Chadd return NULL; 703127be1a7SAdrian Chadd } 704127be1a7SAdrian Chadd 705127be1a7SAdrian Chadd /* 706127be1a7SAdrian Chadd * Lookup a country by ISO country code. 707127be1a7SAdrian Chadd */ 708127be1a7SAdrian Chadd const struct country * 709127be1a7SAdrian Chadd lib80211_country_findbycc(const struct regdata *rdp, enum ISOCountryCode cc) 710127be1a7SAdrian Chadd { 711127be1a7SAdrian Chadd const struct country *cp; 712127be1a7SAdrian Chadd 713127be1a7SAdrian Chadd LIST_FOREACH(cp, &rdp->countries, next) { 714127be1a7SAdrian Chadd if (cp->code == cc) 715127be1a7SAdrian Chadd return cp; 716127be1a7SAdrian Chadd } 717127be1a7SAdrian Chadd return NULL; 718127be1a7SAdrian Chadd } 719127be1a7SAdrian Chadd 720127be1a7SAdrian Chadd /* 721127be1a7SAdrian Chadd * Lookup a country by ISO/long name. 722127be1a7SAdrian Chadd */ 723127be1a7SAdrian Chadd const struct country * 724127be1a7SAdrian Chadd lib80211_country_findbyname(const struct regdata *rdp, const char *name) 725127be1a7SAdrian Chadd { 726127be1a7SAdrian Chadd const struct country *cp; 727127be1a7SAdrian Chadd int len; 728127be1a7SAdrian Chadd 729127be1a7SAdrian Chadd len = strlen(name); 730127be1a7SAdrian Chadd LIST_FOREACH(cp, &rdp->countries, next) { 731127be1a7SAdrian Chadd if (strcasecmp(cp->isoname, name) == 0) 732127be1a7SAdrian Chadd return cp; 733127be1a7SAdrian Chadd } 734127be1a7SAdrian Chadd LIST_FOREACH(cp, &rdp->countries, next) { 735127be1a7SAdrian Chadd if (strncasecmp(cp->name, name, len) == 0) 736127be1a7SAdrian Chadd return cp; 737127be1a7SAdrian Chadd } 738127be1a7SAdrian Chadd return NULL; 739127be1a7SAdrian Chadd } 740