1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
|
/* Copyright © 2012 Brandon L Black <blblack@gmail.com>
*
* This file is part of gdnsd.
*
* gdnsd-plugin-geoip is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* gdnsd-plugin-geoip is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with gdnsd. If not, see <http://www.gnu.org/licenses/>.
*
*/
// gdmaps = GeoIP -> Datacenter Mapping library code
#include <config.h>
#include "dcmap.h"
#include "dclists.h"
#include <gdnsd/alloc.h>
#include <gdnsd/log.h>
#include <stdbool.h>
#include <strings.h>
/***************************************
* dcmap_t and related methods
**************************************/
// Exactly one of the following must be true:
// dclist is non-zero, indicating a direct dclist
// dcmap is non-null, indicating another level of depth
typedef struct {
char* name;
dcmap_t* dcmap;
uint32_t dclist;
} dcmap_child_t;
struct dcmap {
// All 3 below are allocated to num_children entries.
dcmap_child_t* children;
unsigned def_dclist; // copied from parent if not specced in cfg, required at root
unsigned num_children;
};
typedef struct {
dcmap_t* dcmap;
dclists_t* dclists;
const char* map_name;
unsigned child_num;
unsigned true_depth;
bool allow_auto;
} dcmap_iter_data;
F_NONNULL
static bool dcmap_new_iter(const char* key, unsigned klen V_UNUSED, vscf_data_t* val, void* data)
{
dcmap_iter_data* did = data;
dcmap_child_t* child = &did->dcmap->children[did->child_num];
child->name = xstrdup(key);
if (vscf_is_hash(val))
child->dcmap = dcmap_new(val, did->dclists, did->dcmap->def_dclist, did->true_depth + 1, did->map_name, did->allow_auto);
else
child->dclist = dclists_find_or_add_vscf(did->dclists, val, did->map_name, did->allow_auto);
did->child_num++;
return true;
}
dcmap_t* dcmap_new(const vscf_data_t* map_cfg, dclists_t* dclists, const unsigned parent_def, const unsigned true_depth, const char* map_name, const bool allow_auto)
{
gdnsd_assert(vscf_is_hash(map_cfg));
dcmap_t* dcmap = xcalloc(sizeof(*dcmap));
unsigned nchild = vscf_hash_get_len(map_cfg);
vscf_data_t* def_cfg = vscf_hash_get_data_byconstkey(map_cfg, "default", true);
if (def_cfg) {
if (!true_depth) {
uint8_t newlist[256];
bool is_auto = dclists_xlate_vscf(dclists, def_cfg, map_name, newlist, allow_auto);
if (is_auto) {
gdnsd_assert(allow_auto);
dcmap->def_dclist = DCLIST_AUTO;
} else {
dcmap->def_dclist = 0;
dclists_replace_list0(dclists, (uint8_t*)xstrdup((char*)newlist));
}
} else {
dcmap->def_dclist = dclists_find_or_add_vscf(dclists, def_cfg, map_name, allow_auto);
}
nchild--; // don't iterate "default" later
} else {
if (!true_depth) {
dcmap->def_dclist = allow_auto ? DCLIST_AUTO : 0;
} else {
dcmap->def_dclist = parent_def;
}
}
if (nchild) {
dcmap->num_children = nchild;
dcmap->children = xcalloc_n(nchild, sizeof(*dcmap->children));
dcmap_iter_data did = {
.child_num = 0,
.dcmap = dcmap,
.dclists = dclists,
.map_name = map_name,
.true_depth = true_depth,
.allow_auto = allow_auto
};
vscf_hash_iterate(map_cfg, true, dcmap_new_iter, &did);
}
return dcmap;
}
// as above, but supports abitrary levels of nesting in the map without regard
// to any named hierarchy, and without prefetching levels from the lookup source
// unless the map actually wants to see them.
static uint32_t dcmap_llc_(const dcmap_t* dcmap, dcmap_lookup_cb_t cb, void* data, unsigned level)
{
// map empty within this level, e.g. "US => {}" or "US => { default => [...] }"
if (!dcmap->num_children)
return dcmap->def_dclist;
// This will potentially execute multiple callbacks to search several
// levels deep in the network record for a match, but only once we've
// explicitly passed the Country level (so search only happens for
// subdivisions and cities).
char lookup[DCMAP_LOOKUP_MAXLEN];
do {
lookup[0] = '\0';
cb(data, &lookup[0], level++);
if (!lookup[0])
break;
for (unsigned i = 0; i < dcmap->num_children; i++) {
const dcmap_child_t* child = &dcmap->children[i];
if (!strcasecmp(lookup, child->name)) {
if (child->dcmap)
return dcmap_llc_(child->dcmap, cb, data, level);
return child->dclist;
}
}
} while (level > 2); // >1 => post-continent, >2 => post-country
return dcmap->def_dclist;
}
uint32_t dcmap_lookup_loc_callback(const dcmap_t* dcmap, dcmap_lookup_cb_t cb, void* data)
{
return dcmap_llc_(dcmap, cb, data, 0);
}
void dcmap_destroy(dcmap_t* dcmap)
{
for (unsigned i = 0; i < dcmap->num_children; i++) {
const dcmap_child_t* child = &dcmap->children[i];
if (child->name)
free(child->name);
if (child->dcmap)
dcmap_destroy(child->dcmap);
}
free(dcmap->children);
free(dcmap);
}
|