Exemplo n.º 1
0
F_NONNULL
static bool config_addr_group_addr(const char* lb_name, const unsigned lb_name_len, const vscf_data_t* lb_data, void* iaga_asvoid) {
    dmn_assert(lb_name); dmn_assert(lb_name_len); dmn_assert(lb_data); dmn_assert(iaga_asvoid);

    iaga_t* iaga = (iaga_t*)iaga_asvoid;

    addrset_t* addrset = iaga->addrset;
    res_aitem_t* res_item = iaga->res_item;
    unsigned lb_idx = iaga->lb_idx++;
    const char* res_name = iaga->res_name;
    const char* stanza = iaga->stanza;
    const char* item_name = iaga->item_name;
    const bool ipv6 = iaga->ipv6;
    const unsigned res_name_len = strlen(res_name);
    const unsigned item_name_len = strlen(item_name);

    long lb_weight = 0;
    if(!vscf_is_array(lb_data)
            || (2 != vscf_array_get_len(lb_data))
            || !vscf_is_simple(vscf_array_get_data(lb_data, 0))
            || !vscf_is_simple(vscf_array_get_data(lb_data, 1))
            || !vscf_simple_get_as_long(vscf_array_get_data(lb_data, 1), &lb_weight)
            || lb_weight < 1 || lb_weight > MAX_WEIGHT )
        log_fatal("plugin_weighted: resource '%s', group '%s': values in address group mode must be arrays of [ IPADDR, WEIGHT ], where weight must be an integer in the range 1 - " MAX_WEIGHT_STR, res_name, item_name);

    res_item->as[lb_idx].states = calloc(addrset->num_svcs, sizeof(mon_state_t));
    res_item->as[lb_idx].weight = lb_weight;

    const char* addr_txt = vscf_simple_get_data(vscf_array_get_data(lb_data, 0));
    int addr_err = gdnsd_anysin_getaddrinfo(addr_txt, NULL, &res_item->as[lb_idx].addr);
    if(addr_err)
        log_fatal("plugin_weighted: resource '%s', group '%s', addr '%s': parsing '%s' as an IP address failed: %s", res_name, item_name, lb_name, addr_txt, gai_strerror(addr_err));
    if(ipv6 && res_item->as[lb_idx].addr.sa.sa_family != AF_INET6)
        log_fatal("plugin_weighted: resource '%s' (%s): item '%s': '%s' is IPv4, was expecting IPv6", res_name, stanza, item_name, addr_txt);
    else if(!ipv6 && res_item->as[lb_idx].addr.sa.sa_family != AF_INET)
        log_fatal("plugin_weighted: resource '%s' (%s): item '%s': '%s' is IPv6, was expecting IPv4", res_name, stanza, item_name, addr_txt);

    for(unsigned i = 0; i < addrset->num_svcs; i++) {
        const unsigned svc_name_len = strlen(addrset->svc_names[i]);
        char *complete_desc = malloc(res_name_len + 1 + 4 + 1 + item_name_len + 1 + lb_name_len + 1 + svc_name_len + 1);
        sprintf(complete_desc, "%s/%s/%s/%s/%s", res_name, ipv6 ? "ipv6" : "ipv4", item_name, lb_name, addrset->svc_names[i]);
        mon_list.info = realloc(mon_list.info, sizeof(mon_info_t) * (mon_list.count + 1));
        mon_info_t* m = &mon_list.info[mon_list.count++];
        m->svctype = addrset->svc_names[i];
        m->desc = complete_desc;
        m->addr = addr_txt;
        m->state_ptr = &res_item->as[lb_idx].states[i];
    }

    log_debug("plugin_weighted: resource '%s' (%s), item '%s', address %s added with weight %u", res_name, stanza, item_name, addr_txt, res_item->as[lb_idx].weight);

    return true;
}
Exemplo n.º 2
0
Arquivo: dcinfo.c Projeto: gdnsd/gdnsd
F_NONNULL
static unsigned dcinfo_init_auto(dcinfo_t* info, vscf_data_t* dc_auto_cfg, const char* map_name)
{
    if (!vscf_is_hash(dc_auto_cfg))
        log_fatal("plugin_geoip: map '%s': auto_dc_coords must be a key-value hash", map_name);
    const unsigned num_auto = vscf_hash_get_len(dc_auto_cfg);
    const unsigned num_dcs = info->num_dcs;

    for (unsigned i = 0; i < num_dcs; i++) {
        info->dcs[i].coords.lat = (double)NAN;
        info->dcs[i].coords.lon = (double)NAN;
        info->dcs[i].coords.cos_lat = (double)NAN;
    }

    for (unsigned i = 0; i < num_auto; i++) {
        const char* dcname = vscf_hash_get_key_byindex(dc_auto_cfg, i, NULL);
        unsigned dcidx;
        for (dcidx = 0; dcidx < num_dcs; dcidx++) {
            if (!strcmp(dcname, info->dcs[dcidx].name))
                break;
        }
        if (dcidx == num_dcs)
            log_fatal("plugin_geoip: map '%s': auto_dc_coords key '%s' not matched from 'datacenters' list", map_name, dcname);
        GDNSD_DIAG_PUSH_IGNORED("-Wdouble-promotion")
        if (!isnan(info->dcs[dcidx].coords.lat))
            log_fatal("plugin_geoip: map '%s': auto_dc_coords key '%s' defined twice", map_name, dcname);
        GDNSD_DIAG_POP
        vscf_data_t* coord_cfg = vscf_hash_get_data_byindex(dc_auto_cfg, i);
        if (!vscf_is_array(coord_cfg) || vscf_array_get_len(coord_cfg) != 2)
            log_fatal("plugin_geoip: map '%s': auto_dc_coords value for datacenter '%s' must be an array of two values", map_name, dcname);
        vscf_data_t* lat_cfg = vscf_array_get_data(coord_cfg, 0);
        vscf_data_t* lon_cfg = vscf_array_get_data(coord_cfg, 1);
        gdnsd_assert(lat_cfg);
        gdnsd_assert(lon_cfg);

        double lat;
        double lon;
        if (!vscf_is_simple(lat_cfg)
                || !vscf_is_simple(lon_cfg)
                || !vscf_simple_get_as_double(lat_cfg, &lat)
                || !vscf_simple_get_as_double(lon_cfg, &lon)
                || lat > 90.0 || lat < -90.0
                || lon > 180.0 || lon < -180.0
           )
            log_fatal("plugin_geoip: map '%s': auto_dc_coords value for datacenter '%s' must be a legal latitude and longitude in decimal degrees", map_name, dcname);
        info->dcs[dcidx].coords.lat = lat * DEG2RAD;
        info->dcs[dcidx].coords.lon = lon * DEG2RAD;
        info->dcs[dcidx].coords.cos_lat = cos(lat * DEG2RAD);
    }

    return num_auto;
}
Exemplo n.º 3
0
void plugin_extfile_add_svctype(const char* name, vscf_data_t* svc_cfg, const unsigned interval, const unsigned timeout) {
    dmn_assert(name); dmn_assert(svc_cfg);

    service_types = xrealloc(service_types, (num_svcs + 1) * sizeof(extf_svc_t));
    extf_svc_t* svc = &service_types[num_svcs++];

    svc->name = strdup(name);
    svc->timeout = timeout;
    svc->interval = interval;

    vscf_data_t* path_cfg = vscf_hash_get_data_byconstkey(svc_cfg, "file", true);
    if(!path_cfg || !vscf_is_simple(path_cfg))
        log_fatal("plugin_extfile: service_type '%s': the 'file' option is required and must be a string filename", name);
    svc->path = gdnsd_resolve_path_state(vscf_simple_get_data(path_cfg), "extfile");

    svc->direct = false;
    svc->def_sttl = GDNSD_STTL_TTL_MAX;
    SVC_OPT_BOOL(svc_cfg, name, direct, svc->direct);
    SVC_OPT_UINT(svc_cfg, name, def_ttl, svc->def_sttl, 1LU, (unsigned long)GDNSD_STTL_TTL_MAX);
    bool def_down = false;
    SVC_OPT_BOOL(svc_cfg, name, def_down, def_down);
    if(def_down)
        svc->def_sttl |= GDNSD_STTL_DOWN;

    svc->num_mons = 0;
    svc->mons = NULL;
}
Exemplo n.º 4
0
F_NONNULL
static unsigned res_get_mapnum(const vscf_data_t* res_cfg, const char* res_name) {
    dmn_assert(res_cfg); dmn_assert(res_name);

    // Get 'dclist' name, convert, store, return 0-based dclist index
    const vscf_data_t* dc_cfg = vscf_hash_get_data_byconstkey(res_cfg, "datacenters", true);
    if(!dc_cfg)
        log_fatal("plugin_metafo: resource '%s': required key 'datacenters' is missing", res_name);
    dclist_t* dcl = malloc(sizeof(dclist_t));
    if(vscf_is_hash(dc_cfg) || !(dcl->num_dcs = vscf_array_get_len(dc_cfg)))
        log_fatal("plugin_metafo: resource '%s': 'datacenters' must be an array of one or more datacenter name strings", res_name);

    uint8_t* dclptr = dcl->dc_list = malloc(dcl->num_dcs + 1);
    dcl->dc_names = malloc((dcl->num_dcs + 1) * sizeof(char*));
    dcl->dc_names[0] = NULL; // index zero is invalid
    for(unsigned i = 0; i < dcl->num_dcs; i++) {
        const vscf_data_t* dcname_cfg = vscf_array_get_data(dc_cfg, i);
        if(!dcname_cfg || !vscf_is_simple(dcname_cfg))
            log_fatal("plugin_metafo: resource '%s': 'datacenters' must be an array of one or more datacenter name strings", res_name);
        const unsigned dcidx = i + 1;
        *dclptr++ = dcidx;
        dcl->dc_names[dcidx] = strdup(vscf_simple_get_data(dcname_cfg));
    }
    *dclptr = 0;

    const unsigned rv_idx = num_dclists++;
    dclists = realloc(dclists, num_dclists * sizeof(dclist_t*));
    dclists[rv_idx] = dcl;
    return rv_idx;
}
Exemplo n.º 5
0
F_NONNULL
static dc_t* config_res_perdc(const unsigned mapnum, const vscf_data_t* cfg, const char* resname) {
    dmn_assert(cfg); dmn_assert(resname);
    dmn_assert(vscf_is_hash(cfg));

    const unsigned num_dcs = vscf_hash_get_len(cfg);
    dc_t* store = calloc((num_dcs + 1), sizeof(dc_t));
    for(unsigned i = 0; i < num_dcs; i++) {
        const char* dcname = vscf_hash_get_key_byindex(cfg, i, NULL);
        const unsigned dc_idx = map_get_dcidx(mapnum, dcname);
        if(!dc_idx)
            log_fatal("plugin_" PNSTR ": resource '%s': datacenter name '%s' is not valid", resname, dcname);
        dmn_assert(dc_idx <= num_dcs);
        dc_t* this_dc = &store[dc_idx];
        this_dc->dc_name = strdup(dcname);
        const vscf_data_t* plugdata = vscf_hash_get_data_byindex(cfg, i);
        if(vscf_is_simple(plugdata)) {
            const char* textdata = vscf_simple_get_data(plugdata);
            if(*textdata == '%') {
                char* child_plugname = strdup(textdata + 1);
                this_dc->plugin_name = child_plugname;
                char* child_resname = strchr(child_plugname, '!');
                if(child_resname) {
                    *child_resname++ = '\0';
                    this_dc->res_name = strdup(child_resname);
                }
                if(!strcmp(this_dc->plugin_name, PNSTR) && !strcmp(this_dc->res_name, resname))
                    log_fatal("plugin_" PNSTR ": resource '%s': not allowed to reference itself!", resname);
            }
            else if(*textdata == '!') {
                this_dc->res_name = strdup(textdata + 1);
                const vscf_data_t* res_cfg = vscf_get_parent(cfg);
                this_dc->plugin_name = get_defaulted_plugname(res_cfg, resname, dcname);
                if(!strcmp(this_dc->plugin_name, PNSTR) && !strcmp(this_dc->res_name, resname))
                    log_fatal("plugin_" PNSTR ": resource '%s': not allowed to reference itself!", resname);
            }
            else {
                anysin_t tempsin;
                if(gdnsd_anysin_getaddrinfo(textdata, NULL, &tempsin)) {
                    // failed to parse as address, so set up direct CNAME if possible
                    uint8_t* dname = malloc(256);
                    dname_status_t dnstat = vscf_simple_get_as_dname(plugdata, dname);
                    if(dnstat == DNAME_INVALID)
                        log_fatal("plugin_" PNSTR ": resource '%s': CNAME for datacenter '%s' is not a legal domainname", resname, dcname);
                    if(dnstat == DNAME_VALID)
                        dname = dname_trim(dname);
                    this_dc->dname = dname;
                }
                else {
                    inject_child_plugin_config(this_dc, resname, (vscf_data_t*)plugdata);
                }
            }
        }
        else {
            inject_child_plugin_config(this_dc, resname, (vscf_data_t*)plugdata);
        }
    }
    return store;
}
Exemplo n.º 6
0
F_NONNULL
static void config_item_addrs(res_aitem_t* res_item, const char* res_name, const char* stanza, const char* item_name, const bool ipv6, const vscf_data_t* cfg_data, addrset_t* addrset) {
    dmn_assert(res_name); dmn_assert(stanza); dmn_assert(item_name);
    dmn_assert(res_item); dmn_assert(cfg_data); dmn_assert(addrset);

    const unsigned res_name_len = strlen(res_name);
    const unsigned item_name_len = strlen(item_name);

    long wtemp = 0;
    if(!vscf_is_array(cfg_data)
            || (2 != vscf_array_get_len(cfg_data))
            || !vscf_is_simple(vscf_array_get_data(cfg_data, 0))
            || !vscf_is_simple(vscf_array_get_data(cfg_data, 1))
            || !vscf_simple_get_as_long(vscf_array_get_data(cfg_data, 1), &wtemp)
            || wtemp < 1 || wtemp > MAX_WEIGHT )
        log_fatal("plugin_weighted: resource '%s' (%s): item '%s': values in addrs mode must be arrays of [ IPADDR, WEIGHT ], where weight must be an integer in the range 1 - " MAX_WEIGHT_STR, res_name, stanza, item_name);

    res_item->count = 1;
    res_item->as = calloc(res_item->count, sizeof(addrstate_t));
    res_item->as[0].states = calloc(addrset->num_svcs, sizeof(mon_state_t));
    res_item->as[0].weight = wtemp;
    res_item->max_weight = wtemp;
    res_item->weight = wtemp;

    const char* addr_txt = vscf_simple_get_data(vscf_array_get_data(cfg_data, 0));
    int addr_err = gdnsd_anysin_getaddrinfo(addr_txt, NULL, &res_item->as[0].addr);
    if(addr_err)
        log_fatal("plugin_weighted: resource '%s' (%s): item '%s': parsing '%s' as an IP address failed: %s", res_name, stanza, item_name, addr_txt, gai_strerror(addr_err));
    if(ipv6 && res_item->as[0].addr.sa.sa_family != AF_INET6)
        log_fatal("plugin_weighted: resource '%s' (%s): item '%s': '%s' is IPv4, was expecting IPv6", res_name, stanza, item_name, addr_txt);
    else if(!ipv6 && res_item->as[0].addr.sa.sa_family != AF_INET)
        log_fatal("plugin_weighted: resource '%s' (%s): item '%s': '%s' is IPv6, was expecting IPv4", res_name, stanza, item_name, addr_txt);

    for(unsigned i = 0; i < addrset->num_svcs; i++) {
        const unsigned svc_name_len = strlen(addrset->svc_names[i]);
        char *complete_desc = malloc(res_name_len + 1 + 4 + 1 + item_name_len + 1 + svc_name_len + 1);
        sprintf(complete_desc, "%s/%s/%s/%s", res_name, ipv6 ? "ipv6" : "ipv4", item_name, addrset->svc_names[i]);
        mon_list.info = realloc(mon_list.info, sizeof(mon_info_t) * (mon_list.count + 1));
        mon_info_t* m = &mon_list.info[mon_list.count++];
        m->svctype = addrset->svc_names[i];
        m->desc = complete_desc;
        m->addr = addr_txt;
        m->state_ptr = &res_item->as[0].states[i];
    }
    log_debug("plugin_weighted: resource '%s' (%s), item '%s': A '%s' added w/ weight %u", res_name, stanza, item_name, addr_txt, res_item->weight);
}
Exemplo n.º 7
0
F_NONNULL
static bool config_addr_group_addr(const char* lb_name, const unsigned lb_name_len V_UNUSED, const vscf_data_t* lb_data, void* iaga_asvoid) {
    dmn_assert(lb_name); dmn_assert(lb_name_len); dmn_assert(lb_data); dmn_assert(iaga_asvoid);

    iaga_t* iaga = (iaga_t*)iaga_asvoid;

    addrset_t* addrset = iaga->addrset;
    res_aitem_t* res_item = iaga->res_item;
    unsigned lb_idx = iaga->lb_idx++;
    const char* res_name = iaga->res_name;
    const char* stanza = iaga->stanza;
    const char* item_name = iaga->item_name;
    const bool ipv6 = iaga->ipv6;

    long lb_weight = 0;
    if(!vscf_is_array(lb_data)
            || (2 != vscf_array_get_len(lb_data))
            || !vscf_is_simple(vscf_array_get_data(lb_data, 0))
            || !vscf_is_simple(vscf_array_get_data(lb_data, 1))
            || !vscf_simple_get_as_long(vscf_array_get_data(lb_data, 1), &lb_weight)
            || lb_weight < 1 || lb_weight > MAX_WEIGHT )
        log_fatal("plugin_weighted: resource '%s', group '%s': values in address group mode must be arrays of [ IPADDR, WEIGHT ], where weight must be an integer in the range 1 - " MAX_WEIGHT_STR, res_name, item_name);

    res_item->as[lb_idx].weight = lb_weight;

    const char* addr_txt = vscf_simple_get_data(vscf_array_get_data(lb_data, 0));
    int addr_err = gdnsd_anysin_getaddrinfo(addr_txt, NULL, &res_item->as[lb_idx].addr);
    if(addr_err)
        log_fatal("plugin_weighted: resource '%s', group '%s', addr '%s': parsing '%s' as an IP address failed: %s", res_name, item_name, lb_name, addr_txt, gai_strerror(addr_err));
    if(ipv6 && res_item->as[lb_idx].addr.sa.sa_family != AF_INET6)
        log_fatal("plugin_weighted: resource '%s' (%s): item '%s': '%s' is IPv4, was expecting IPv6", res_name, stanza, item_name, addr_txt);
    else if(!ipv6 && res_item->as[lb_idx].addr.sa.sa_family != AF_INET)
        log_fatal("plugin_weighted: resource '%s' (%s): item '%s': '%s' is IPv6, was expecting IPv4", res_name, stanza, item_name, addr_txt);

    if(addrset->num_svcs) {
        res_item->as[lb_idx].indices = malloc(addrset->num_svcs * sizeof(unsigned));
        for(unsigned i = 0; i < addrset->num_svcs; i++)
            res_item->as[lb_idx].indices[i] = gdnsd_mon_addr(addrset->svc_names[i], &res_item->as[lb_idx].addr);
    }

    log_debug("plugin_weighted: resource '%s' (%s), item '%s', address %s added with weight %u", res_name, stanza, item_name, addr_txt, res_item->as[lb_idx].weight);

    return true;
}
Exemplo n.º 8
0
Arquivo: dcinfo.c Projeto: gdnsd/gdnsd
// Technically we could/should check for duplicates here.  The plugin will
//  still fail later though: when a resource is defined, the datacenter
//  names go into a hash requiring uniqueness, and the count is required
//  to match (ditto for auto_dc_coords never succeeding with dupes in the
//  datacenters list).
void dcinfo_init(dcinfo_t* info, vscf_data_t* dc_cfg, vscf_data_t* dc_auto_cfg, vscf_data_t* dc_auto_limit_cfg, const char* map_name, monreg_func_t mrf)
{
    const unsigned num_dcs = vscf_array_get_len(dc_cfg);
    unsigned num_auto = num_dcs;
    if (!num_dcs)
        log_fatal("plugin_geoip: map '%s': 'datacenters' must be an array of one or more strings", map_name);
    if (num_dcs > MAX_NUM_DCS)
        log_fatal("plugin_geoip: map '%s': %u datacenters is too many, this code only supports up to %i", map_name, num_dcs, MAX_NUM_DCS);

    info->num_dcs = num_dcs;
    info->dcs = xmalloc_n(num_dcs, sizeof(*info->dcs));

    for (unsigned i = 0; i < num_dcs; i++) {
        vscf_data_t* dcname_cfg = vscf_array_get_data(dc_cfg, i);
        if (!dcname_cfg || !vscf_is_simple(dcname_cfg))
            log_fatal("plugin_geoip: map '%s': 'datacenters' must be an array of one or more strings", map_name);
        info->dcs[i].name = xstrdup(vscf_simple_get_data(dcname_cfg));
        if (!strcmp(info->dcs[i].name, "auto"))
            log_fatal("plugin_geoip: map '%s': datacenter name 'auto' is illegal", map_name);
        char* map_mon_desc = gdnsd_str_combine_n(4, "geoip/", map_name, "/", info->dcs[i].name);
        if (mrf)
            info->dcs[i].mon_index = mrf(map_mon_desc);
        free(map_mon_desc);
    }

    if (dc_auto_cfg)
        num_auto = dcinfo_init_auto(info, dc_auto_cfg, map_name);

    if (dc_auto_limit_cfg) {
        unsigned long auto_limit_ul;
        if (!vscf_is_simple(dc_auto_limit_cfg) || !vscf_simple_get_as_ulong(dc_auto_limit_cfg, &auto_limit_ul))
            log_fatal("plugin_geoip: map '%s': auto_dc_limit must be a single unsigned integer value", map_name);
        if (auto_limit_ul > num_auto || !auto_limit_ul)
            auto_limit_ul = num_auto;
        info->auto_limit = auto_limit_ul;
    } else {
        info->auto_limit = (num_auto > 3) ? 3 : num_auto;
    }
}
Exemplo n.º 9
0
F_NONNULL
static void config_item_addrs(res_aitem_t* res_item, const char* res_name, const char* stanza, const char* item_name, const bool ipv6, const vscf_data_t* cfg_data, addrset_t* addrset) {
    dmn_assert(res_name); dmn_assert(stanza); dmn_assert(item_name);
    dmn_assert(res_item); dmn_assert(cfg_data); dmn_assert(addrset);

    long wtemp = 0;
    if(!vscf_is_array(cfg_data)
            || (2 != vscf_array_get_len(cfg_data))
            || !vscf_is_simple(vscf_array_get_data(cfg_data, 0))
            || !vscf_is_simple(vscf_array_get_data(cfg_data, 1))
            || !vscf_simple_get_as_long(vscf_array_get_data(cfg_data, 1), &wtemp)
            || wtemp < 1 || wtemp > MAX_WEIGHT )
        log_fatal("plugin_weighted: resource '%s' (%s): item '%s': values in addrs mode must be arrays of [ IPADDR, WEIGHT ], where weight must be an integer in the range 1 - " MAX_WEIGHT_STR, res_name, stanza, item_name);

    res_item->count = 1;
    res_item->as = calloc(res_item->count, sizeof(addrstate_t));
    res_item->as[0].weight = wtemp;
    res_item->max_weight = wtemp;
    res_item->weight = wtemp;

    const char* addr_txt = vscf_simple_get_data(vscf_array_get_data(cfg_data, 0));
    int addr_err = gdnsd_anysin_getaddrinfo(addr_txt, NULL, &res_item->as[0].addr);
    if(addr_err)
        log_fatal("plugin_weighted: resource '%s' (%s): item '%s': parsing '%s' as an IP address failed: %s", res_name, stanza, item_name, addr_txt, gai_strerror(addr_err));
    if(ipv6 && res_item->as[0].addr.sa.sa_family != AF_INET6)
        log_fatal("plugin_weighted: resource '%s' (%s): item '%s': '%s' is IPv4, was expecting IPv6", res_name, stanza, item_name, addr_txt);
    else if(!ipv6 && res_item->as[0].addr.sa.sa_family != AF_INET)
        log_fatal("plugin_weighted: resource '%s' (%s): item '%s': '%s' is IPv6, was expecting IPv4", res_name, stanza, item_name, addr_txt);

    if(addrset->num_svcs) {
        res_item->as[0].indices = malloc(addrset->num_svcs * sizeof(unsigned));
        for(unsigned i = 0; i < addrset->num_svcs; i++)
            res_item->as[0].indices[i] = gdnsd_mon_addr(addrset->svc_names[i], &res_item->as[0].addr);
    }
    log_debug("plugin_weighted: resource '%s' (%s), item '%s': A '%s' added w/ weight %u", res_name, stanza, item_name, addr_txt, res_item->weight);
}
Exemplo n.º 10
0
// retval is new storage.
// "plugin", if existed in config, will be marked afterwards
F_NONNULL
static char* get_defaulted_plugname(const vscf_data_t* cfg, const char* resname, const char* dcname) {
    dmn_assert(cfg);

    char* rv;
    const vscf_data_t* plugname_data = vscf_hash_get_data_byconstkey(cfg, "plugin", true);
    if(plugname_data) {
        if(!vscf_is_simple(plugname_data))
            log_fatal("plugin_" PNSTR ": resource '%s': datacenter '%s': value of 'plugin' must be a string", resname, dcname);
        rv = strdup(vscf_simple_get_data(plugname_data));
    }
    else {
        rv = strdup("multifo");
    }

    return rv;
}
Exemplo n.º 11
0
F_NONNULL
static void conf_options(const vscf_data_t* cfg_root) {
    dmn_assert(cfg_root);

    // options stanza: set dmn_debug
    bool debug_tmp = false;
    const vscf_data_t* options = vscf_hash_get_data_byconstkey(cfg_root, "options", true);
    if(options) {
        if(!vscf_is_hash(options))
            log_fatal("Config stanza 'options' must be a hash");
        const vscf_data_t* debug_setting = vscf_hash_get_data_byconstkey(options, "debug", false);
        if(debug_setting
            && (!vscf_is_simple(debug_setting)
            || !vscf_simple_get_as_bool(debug_setting, &debug_tmp)))
                log_fatal("Config option 'debug': value must be 'true' or 'false'");
    }
    dmn_set_debug(debug_tmp);
}
Exemplo n.º 12
0
void gdnsd_plugins_set_search_path(vscf_data_t* psearch_array) {
    dmn_assert(!psearch); // only called once

    // Create a plugin search path array
    unsigned psearch_count = psearch_array
        ? vscf_array_get_len(psearch_array)
        : 0;

    psearch = xmalloc((psearch_count + 2) * sizeof(const char*));
    for(unsigned i = 0; i < psearch_count; i++) {
        vscf_data_t* psd = vscf_array_get_data(psearch_array, i);
        if(!vscf_is_simple(psd))
            log_fatal("Plugin search paths must be strings");
        psearch[i] = strdup(vscf_simple_get_data(psd));
    }

    psearch[psearch_count++] = GDNSD_DEFPATH_LIB;
    psearch[psearch_count] = NULL;
}
Exemplo n.º 13
0
// Technically we could/should check for duplicates here.  The plugin will
//  still fail later though: when a resource is defined, the datacenter
//  names go into a hash requiring uniqueness, and the count is required
//  to match (ditto for auto_dc_coords never succeeding with dupes in the
//  datacenters list).
dcinfo_t* dcinfo_new(const vscf_data_t* dc_cfg, const vscf_data_t* dc_auto_cfg, const vscf_data_t* dc_auto_limit_cfg, const char* map_name) {
    dmn_assert(dc_cfg); dmn_assert(map_name);

    dcinfo_t* info = malloc(sizeof(dcinfo_t));

    const unsigned num_dcs = vscf_array_get_len(dc_cfg);
    unsigned num_auto = num_dcs;
    if(!num_dcs)
        log_fatal("plugin_geoip: map '%s': 'datacenters' must be an array of one or more strings", map_name);
    if(num_dcs > 254)
        log_fatal("plugin_geoip: map '%s': %u datacenters is too many, this code only supports up to 254", map_name, num_dcs);

    info->names = malloc(sizeof(char*) * num_dcs);
    info->num_dcs = num_dcs;
    for(unsigned i = 0; i < num_dcs; i++) {
        const vscf_data_t* dcname_cfg = vscf_array_get_data(dc_cfg, i);
        if(!dcname_cfg || !vscf_is_simple(dcname_cfg))
            log_fatal("plugin_geoip: map '%s': 'datacenters' must be an array of one or more strings", map_name);
        info->names[i] = strdup(vscf_simple_get_data(dcname_cfg));
        if(!strcmp(info->names[i], "auto"))
            log_fatal("plugin_geoip: map '%s': datacenter name 'auto' is illegal", map_name);
    }

    if(dc_auto_cfg) {
        if(!vscf_is_hash(dc_auto_cfg))
            log_fatal("plugin_geoip: map '%s': auto_dc_coords must be a key-value hash", map_name);
        num_auto = vscf_hash_get_len(dc_auto_cfg);
        info->coords = malloc(num_dcs * 2 * sizeof(double));
        for(unsigned i = 0; i < 2*num_dcs; i++)
            info->coords[i] = NAN;
        for(unsigned i = 0; i < num_auto; i++) {
            const char* dcname = vscf_hash_get_key_byindex(dc_auto_cfg, i, NULL);
            unsigned dcidx;
            for(dcidx = 0; dcidx < num_dcs; dcidx++) {
                if(!strcmp(dcname, info->names[dcidx]))
                    break;
            }
            if(dcidx == num_dcs)
                log_fatal("plugin_geoip: map '%s': auto_dc_coords key '%s' not matched from 'datacenters' list", map_name, dcname);
            if(!isnan(info->coords[(dcidx*2)]))
                log_fatal("plugin_geoip: map '%s': auto_dc_coords key '%s' defined twice", map_name, dcname);
            const vscf_data_t* coord_cfg = vscf_hash_get_data_byindex(dc_auto_cfg, i);
            const vscf_data_t* lat_cfg;
            const vscf_data_t* lon_cfg;
            double lat, lon;
            if(
                !vscf_is_array(coord_cfg) || vscf_array_get_len(coord_cfg) != 2
                || !(lat_cfg = vscf_array_get_data(coord_cfg, 0))
                || !(lon_cfg = vscf_array_get_data(coord_cfg, 1))
                || !vscf_is_simple(lat_cfg)
                || !vscf_is_simple(lon_cfg)
                || !vscf_simple_get_as_double(lat_cfg, &lat)
                || !vscf_simple_get_as_double(lon_cfg, &lon)
                || lat > 90.0 || lat < -90.0
                || lon > 180.0 || lon < -180.0
            )
                log_fatal("plugin_geoip: map '%s': auto_dc_coords value for datacenter '%s' must be an array of two floating-point values representing a legal latitude and longitude in decimal degrees", map_name, dcname);
            info->coords[(dcidx * 2)] = lat * DEG2RAD;
            info->coords[(dcidx * 2) + 1] = lon * DEG2RAD;
        }
    }
    else {
        info->coords = NULL;
    }

    if(dc_auto_limit_cfg) {
        unsigned long auto_limit_ul;
        if(!vscf_is_simple(dc_auto_limit_cfg) || !vscf_simple_get_as_ulong(dc_auto_limit_cfg, &auto_limit_ul))
            log_fatal("plugin_geoip: map '%s': auto_dc_limit must be a single unsigned integer value", map_name);
        if(auto_limit_ul > num_auto || !auto_limit_ul)
            auto_limit_ul = num_auto;
        info->auto_limit = auto_limit_ul;
    }
    else {
        info->auto_limit = (num_auto > 3) ? 3 : num_auto;
    }

    return info;
}
Exemplo n.º 14
0
F_NONNULL
static void config_cnameset(const char* res_name, const char* stanza, cnset_t* cnset, const vscf_data_t* cfg) {
    dmn_assert(res_name); dmn_assert(stanza); dmn_assert(cnset); dmn_assert(cfg);

    if(!vscf_is_hash(cfg))
        log_fatal("plugin_weighted: resource '%s' stanza '%s' value must be a hash", res_name, stanza);

    cnset->count = vscf_hash_get_len(cfg);

    // service_types
    cnset->num_svcs = 0;
    const vscf_data_t* res_stypes = vscf_hash_get_data_byconstkey(cfg, "service_types", true);
    if (res_stypes) {
        cnset->count--; // minus one for service_types entry
        cnset->num_svcs = vscf_array_get_len(res_stypes);
        if(cnset->num_svcs) {
            cnset->svc_names = malloc(cnset->num_svcs * sizeof(char*));
            for(unsigned i = 0; i < cnset->num_svcs; i++) {
                const vscf_data_t* this_svc_cfg = vscf_array_get_data(res_stypes, i);
                if(!vscf_is_simple(this_svc_cfg))
                    log_fatal("plugin_weighted: resource '%s' (%s): service_types values must be strings", res_name, stanza);
                cnset->svc_names[i] = strdup(vscf_simple_get_data(this_svc_cfg));
            }
        }
    }
    else {
        cnset->num_svcs = 1;
        cnset->svc_names = malloc(sizeof(char*));
        cnset->svc_names[0] = strdup(DEFAULT_SVCNAME);
    }

    // up threshold as double
    double up_thresh = 0.5;
    const vscf_data_t* thresh_cfg = vscf_hash_get_data_byconstkey(cfg, "up_thresh", true);
    if(thresh_cfg) {
        cnset->count--; // minus one for up_thresh entry
        if(!vscf_is_simple(thresh_cfg) || !vscf_simple_get_as_double(thresh_cfg, &up_thresh)
           || up_thresh <= 0.0 || up_thresh > 1.0)
            log_fatal("plugin_weighted: resource '%s' (%s): 'up_thresh' must be a floating point value in the range (0.0 - 1.0]", res_name, stanza);
    }

    // multi option is processed for count-correctness, but ignored (it's not legal
    //   here, but may be present due to inheritance of defaults!)
    if(vscf_hash_get_data_byconstkey(cfg, "multi", true))
        cnset->count--;

    if(cnset->count > MAX_ITEMS_PER_SET)
        log_fatal("plugin_weighted: resource '%s' (%s): number of cnames cannot be more than %u", res_name, stanza, MAX_ITEMS_PER_SET);
    if(!cnset->count)
        log_fatal("plugin_weighted: resource '%s' (%s): empty cname sets not allowed", res_name, stanza);

    cnset->items = calloc(cnset->count, sizeof(res_citem_t));
    cname_iter_data_t cid = {
        .cnset = cnset,
        .res_name = res_name,
        .stanza = stanza,
        .item_idx = 0,
    };
    vscf_hash_iterate(cfg, true, config_item_cname, &cid);

    cnset->weight = 0;
    for(unsigned i = 0; i < cnset->count; i++) {
        const unsigned cwt = cnset->items[i].weight;
        dmn_assert(cwt);
        cnset->weight += cwt;
    }

    dmn_assert(cnset->weight);

    cnset->up_weight = ceil(up_thresh * cnset->weight);
}

F_NONNULL
static void config_auto(resource_t* res, const vscf_data_t* res_cfg) {
    dmn_assert(res); dmn_assert(res_cfg); dmn_assert(vscf_is_hash(res_cfg));

    // mark all possible parameter-keys
    vscf_hash_get_data_byconstkey(res_cfg, "service_types", true);
    vscf_hash_get_data_byconstkey(res_cfg, "multi", true);
    vscf_hash_get_data_byconstkey(res_cfg, "up_thresh", true);

    // make a copy that contains no parameters, only item-name keys
    vscf_data_t* res_cfg_noparams = vscf_clone(res_cfg, true);

    if(!vscf_hash_get_len(res_cfg_noparams))
        log_fatal("plugin_weighted: resource '%s' (direct) contains no weighted items", res->name);

    const char* first_name = vscf_hash_get_key_byindex(res_cfg_noparams, 0, NULL);
    const vscf_data_t* first_cfg = vscf_hash_get_data_byindex(res_cfg_noparams, 0);
    if(vscf_is_hash(first_cfg)) { // grouped address mode...
        if(!vscf_hash_get_len(first_cfg))
            log_fatal("plugin_weighted: resource '%s' (direct): group '%s': contains no addresses", res->name, first_name);
        const char* lb_name = vscf_hash_get_key_byindex(first_cfg, 0, NULL);
        const vscf_data_t* lb_cfg = vscf_hash_get_data_byindex(first_cfg, 0);
        if(!vscf_is_array(lb_cfg) || !vscf_array_get_len(lb_cfg) || !vscf_is_simple(vscf_array_get_data(lb_cfg, 0)))
            log_fatal("plugin_weighted: resource '%s' (direct): group '%s': item '%s': value must be an array of [ IP, weight ]", res->name, first_name, lb_name);
        const char* first_addr_txt = vscf_simple_get_data(vscf_array_get_data(lb_cfg, 0));
        dmn_anysin_t temp_sin;
        int addr_err = gdnsd_anysin_getaddrinfo(first_addr_txt, NULL, &temp_sin);
        if(addr_err)
            log_fatal("plugin_weighted: resource '%s' (direct): group '%s': item '%s': could not parse '%s' as an IP address: %s", res->name, first_name, lb_name, first_addr_txt, gai_strerror(addr_err));
        if(temp_sin.sa.sa_family == AF_INET6) {
            res->addrs_v6 = calloc(1, sizeof(addrset_t));
            config_addrset(res->name, "direct", true, res->addrs_v6, res_cfg);
        }
        else {
            dmn_assert(temp_sin.sa.sa_family == AF_INET);
            res->addrs_v4 = calloc(1, sizeof(addrset_t));
            config_addrset(res->name, "direct", false, res->addrs_v4, res_cfg);
        }
    }
    else if(vscf_is_array(first_cfg)) { // ungrouped address, or cnames
        const vscf_data_t* first_ac = vscf_array_get_data(first_cfg, 0);
        if(!first_ac || !vscf_is_simple(first_ac))
            log_fatal("plugin_weighted: resource '%s' (direct): item '%s': first element of array should be an IP address or CNAME string", res->name, first_name);
        dmn_anysin_t temp_sin;
        if(gdnsd_anysin_getaddrinfo(vscf_simple_get_data(first_ac), NULL, &temp_sin)) {
            // was not a valid address, try cnames mode
            res->cnames = calloc(1, sizeof(cnset_t));
            config_cnameset(res->name, "direct", res->cnames, res_cfg);
        }
        else {
            // was a valid address, try addrset mode
            if(temp_sin.sa.sa_family == AF_INET6) {
                res->addrs_v6 = calloc(1, sizeof(addrset_t));
                config_addrset(res->name, "direct", true, res->addrs_v6, res_cfg);
            }
            else {
                dmn_assert(temp_sin.sa.sa_family == AF_INET);
                res->addrs_v4 = calloc(1, sizeof(addrset_t));
                config_addrset(res->name, "direct", false, res->addrs_v4, res_cfg);
            }
        }
    }
    else {
        log_fatal("plugin_weighted: resource '%s' (direct): item '%s': resource type not detectable (should be array of [ IP, weight ], array of [ CNAME, weight ], or hashed address group ...)", res->name, first_name);
    }

    vscf_destroy(res_cfg_noparams);
}
Exemplo n.º 15
0
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) {
    dmn_assert(map_cfg); dmn_assert(dclists); dmn_assert(map_name);
    dmn_assert(vscf_is_hash(map_cfg));

    dcmap_t* dcmap = calloc(1, sizeof(dcmap_t));
    unsigned nchild = vscf_hash_get_len(map_cfg);

    const vscf_data_t* def_cfg = vscf_hash_get_data_byconstkey(map_cfg, "default", true);
    if(def_cfg) {
        if(!true_depth) {
            uint8_t newlist[256];
            int status = dclists_xlate_vscf(dclists, def_cfg, map_name, newlist, allow_auto);
            if(status) {
                dmn_assert(status == -1 && allow_auto);
                dcmap->def_dclist = -1;
            }
            else {
                dcmap->def_dclist = 0;
                dclists_replace_list0(dclists, (uint8_t*)strdup((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 ? -1 : 0;
        }
        else {
            dcmap->def_dclist = parent_def;
        }
    }

    const vscf_data_t* skip_cfg = vscf_hash_get_data_byconstkey(map_cfg, "skip_level", true);
    if(skip_cfg) {
        if(!vscf_is_simple(skip_cfg) || !vscf_simple_get_as_bool(skip_cfg, &dcmap->skip_level))
            log_fatal("plugin_geoip: map '%s': 'skip_level' must be a boolean value ('true' or 'false')", map_name);
        nchild--; // don't iterate "skip_level" later
    }

    if(nchild) {
        dcmap->num_children = nchild;
        dcmap->child_names = calloc(nchild, sizeof(char*));
        dcmap->child_dclists = calloc(nchild, sizeof(unsigned));
        dcmap->child_dcmaps = calloc(nchild, sizeof(dcmap_t*));
        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;
}

int dcmap_lookup_loc(const dcmap_t* dcmap, const char* locstr) {
    dmn_assert(dcmap); dmn_assert(locstr);

    if(*locstr && dcmap->skip_level)
        locstr += strlen(locstr) + 1;

    if(*locstr) {
        for(unsigned i = 0; i < dcmap->num_children; i++) {
            if(!strcasecmp(locstr, dcmap->child_names[i])) {
                if(dcmap->child_dcmaps[i])
                    return dcmap_lookup_loc(dcmap->child_dcmaps[i], locstr + strlen(locstr) + 1);
                return dcmap->child_dclists[i];
            }
        }
    }

    return dcmap->def_dclist;
}
Exemplo n.º 16
0
F_NONNULL
static void inject_child_plugin_config(dc_t* this_dc, const char* resname, vscf_data_t* cfg) {
    dmn_assert(this_dc); dmn_assert(resname); dmn_assert(cfg);

    char* child_resname = make_synth_resname(resname, this_dc->dc_name);
    this_dc->res_name = child_resname;

    // Move up 2 layers: dcX -> dcmap -> resX
    vscf_data_t* res_cfg = (vscf_data_t*)cfg;
    for(unsigned i = 0; i < 2; i++) {
        res_cfg = (vscf_data_t*)vscf_get_parent(res_cfg);
        dmn_assert(res_cfg);
    }

    // Move up 3 more layers:
    //   resX -> resources -> metafo|geoip -> plugins
    vscf_data_t* plugins_top = res_cfg;
    for(unsigned i = 0; i < 3; i++) {
        plugins_top = (vscf_data_t*)vscf_get_parent(plugins_top);
        dmn_assert(plugins_top);
    }

    // synth multifo stanza for: dc1 => 192.0.2.1, or dc1 => [ 192.0.2.1, ... ]
    bool cfg_synthed = false;
    if(!vscf_is_hash(cfg)) { // synthesize a hash for multifo for single/array
        vscf_data_t* newhash = vscf_hash_new();
        vscf_data_t* plugname_cfg = vscf_simple_new("multifo", 7);
        vscf_hash_add_val("plugin", 6, newhash, plugname_cfg);
        const unsigned alen = vscf_array_get_len(cfg);
        for(unsigned i = 0; i < alen; i++) {
            const vscf_data_t* this_addr_cfg = vscf_array_get_data(cfg, i);
            if(!vscf_is_simple(this_addr_cfg))
                log_fatal("plugin_" PNSTR ": resource '%s': datacenter '%s': if defined as an array, array values must all be address strings", resname, this_dc->dc_name);
            const unsigned lnum = i + 1;
            char lbuf[12];
            snprintf(lbuf, 12, "%u", lnum);
            vscf_hash_add_val(lbuf, strlen(lbuf), newhash, vscf_clone(this_addr_cfg, false));
        }
        cfg_synthed = true;
        cfg = newhash;
    }

    // inherit resource-level stuff down to dc-level
    vscf_hash_inherit_all(res_cfg, cfg, true);

    this_dc->plugin_name = get_defaulted_plugname(cfg, resname, this_dc->dc_name);
    if(!strcmp(this_dc->plugin_name, PNSTR))
        log_fatal("plugin_" PNSTR ": resource '%s': datacenter '%s': plugin_" PNSTR " cannot synthesize config for itself...", resname, this_dc->dc_name);

    // Create top-level plugins => { foo => {} } if necc
    vscf_data_t* plug_cfg = (vscf_data_t*)vscf_hash_get_data_bystringkey(plugins_top, this_dc->plugin_name, false);
    if(!plug_cfg) {
        plug_cfg = vscf_hash_new();
        vscf_hash_add_val(this_dc->plugin_name, strlen(this_dc->plugin_name), plugins_top, plug_cfg);
    }

    // special-case for geoip -> metafo synthesis, use resources sub-stanza
    if(!strcmp(this_dc->plugin_name, "metafo")) {
        vscf_data_t* synth_res_cfg = (vscf_data_t*)vscf_hash_get_data_byconstkey(plug_cfg, "resources", false);
        if(!synth_res_cfg) {
            synth_res_cfg = vscf_hash_new();
            vscf_hash_add_val("resources", strlen("resources"), plug_cfg, synth_res_cfg);
        }
        plug_cfg = synth_res_cfg; // for below
    }

    // Check if resource already exists
    if(vscf_hash_get_data_bystringkey(plug_cfg, child_resname, false))
        log_fatal("plugin_" PNSTR ": resource '%s': datacenter '%s': synthesis of resource '%s' for plugin '%s' failed (resource name already exists)", resname, this_dc->dc_name, child_resname, this_dc->plugin_name);

    // Add it, using clone() to skip marked key "plugin"
    vscf_hash_add_val(child_resname, strlen(child_resname), plug_cfg, vscf_clone(cfg, true));

    // destroy clone source if synthesized and disconnected from real config tree
    if(cfg_synthed) vscf_destroy(cfg);
}
Exemplo n.º 17
0
F_NONNULL
static void config_cnameset(const char* res_name, const char* stanza, cnset_t* cnset, const vscf_data_t* cfg) {
    dmn_assert(res_name); dmn_assert(stanza); dmn_assert(cnset); dmn_assert(cfg);

    if(!vscf_is_hash(cfg))
        log_fatal("plugin_weighted: resource '%s' stanza '%s' value must be a hash", res_name, stanza);

    cnset->count = vscf_hash_get_len(cfg);

    if(cnset->count > MAX_ITEMS_PER_SET)
        log_fatal("plugin_weighted: resource '%s' (%s): number of cnames cannot be more than %u", res_name, stanza, MAX_ITEMS_PER_SET);
    if(!cnset->count)
        log_fatal("plugin_weighted: resource '%s' (%s): empty cname sets not allowed", res_name, stanza);

    cnset->items = calloc(cnset->count, sizeof(res_citem_t));
    cname_iter_data_t cid = {
        .cnset = cnset,
        .res_name = res_name,
        .stanza = stanza,
        .item_idx = 0,
    };
    vscf_hash_iterate(cfg, true, config_item_cname, &cid);

    cnset->weight = 0;
    for(unsigned i = 0; i < cnset->count; i++) {
        const unsigned cwt = cnset->items[i].weight;
        dmn_assert(cwt);
        cnset->weight += cwt;
    }

    dmn_assert(cnset->weight);
}

F_NONNULL
static void config_auto(resource_t* res, const vscf_data_t* res_cfg) {
    dmn_assert(res); dmn_assert(res_cfg); dmn_assert(vscf_is_hash(res_cfg));

    // mark all possible parameter-keys
    vscf_hash_get_data_byconstkey(res_cfg, "service_types", true);
    vscf_hash_get_data_byconstkey(res_cfg, "multi", true);
    vscf_hash_get_data_byconstkey(res_cfg, "up_thresh", true);

    // make a copy that contains no parameters, only item-name keys
    vscf_data_t* res_cfg_noparams = vscf_clone(res_cfg, true);

    if(!vscf_hash_get_len(res_cfg_noparams))
        log_fatal("plugin_weighted: resource '%s' (direct) contains no weighted items", res->name);

    const char* first_name = vscf_hash_get_key_byindex(res_cfg_noparams, 0, NULL);
    const vscf_data_t* first_cfg = vscf_hash_get_data_byindex(res_cfg_noparams, 0);
    if(vscf_is_hash(first_cfg)) { // grouped address mode...
        if(!vscf_hash_get_len(first_cfg))
            log_fatal("plugin_weighted: resource '%s' (direct): group '%s': contains no addresses", res->name, first_name);
        const char* lb_name = vscf_hash_get_key_byindex(first_cfg, 0, NULL);
        const vscf_data_t* lb_cfg = vscf_hash_get_data_byindex(first_cfg, 0);
        if(!vscf_is_array(lb_cfg) || !vscf_array_get_len(lb_cfg) || !vscf_is_simple(vscf_array_get_data(lb_cfg, 0)))
            log_fatal("plugin_weighted: resource '%s' (direct): group '%s': item '%s': value must be an array of [ IP, weight ]", res->name, first_name, lb_name);
        const char* first_addr_txt = vscf_simple_get_data(vscf_array_get_data(lb_cfg, 0));
        anysin_t temp_sin;
        int addr_err = gdnsd_anysin_getaddrinfo(first_addr_txt, NULL, &temp_sin);
        if(addr_err)
            log_fatal("plugin_weighted: resource '%s' (direct): group '%s': item '%s': could not parse '%s' as an IP address: %s", res->name, first_name, lb_name, first_addr_txt, gai_strerror(addr_err));
        if(temp_sin.sa.sa_family == AF_INET6) {
            res->addrs_v6 = calloc(1, sizeof(addrset_t));
            config_addrset(res->name, "direct", true, res->addrs_v6, res_cfg);
        }
        else {
            dmn_assert(temp_sin.sa.sa_family == AF_INET);
            res->addrs_v4 = calloc(1, sizeof(addrset_t));
            config_addrset(res->name, "direct", false, res->addrs_v4, res_cfg);
        }
    }
    else if(vscf_is_array(first_cfg)) { // ungrouped address, or cnames
        const vscf_data_t* first_ac = vscf_array_get_data(first_cfg, 0);
        if(!first_ac || !vscf_is_simple(first_ac))
            log_fatal("plugin_weighted: resource '%s' (direct): item '%s': first element of array should be an IP address or CNAME string", res->name, first_name);
        anysin_t temp_sin;
        if(gdnsd_anysin_getaddrinfo(vscf_simple_get_data(first_ac), NULL, &temp_sin)) {
            // was not a valid address, try cnames mode
            res->cnames = calloc(1, sizeof(cnset_t));
            config_cnameset(res->name, "direct", res->cnames, res_cfg_noparams);
        }
        else {
            // was a valid address, try addrset mode
            if(temp_sin.sa.sa_family == AF_INET6) {
                res->addrs_v6 = calloc(1, sizeof(addrset_t));
                config_addrset(res->name, "direct", true, res->addrs_v6, res_cfg);
            }
            else {
                dmn_assert(temp_sin.sa.sa_family == AF_INET);
                res->addrs_v4 = calloc(1, sizeof(addrset_t));
                config_addrset(res->name, "direct", false, res->addrs_v4, res_cfg);
            }
        }
    }
    else {
        log_fatal("plugin_weighted: resource '%s' (direct): item '%s': resource type not detectable (should be array of [ IP, weight ], array of [ CNAME, weight ], or hashed address group ...)", res->name, first_name);
    }

    vscf_destroy(res_cfg_noparams);
}
Exemplo n.º 18
0
F_NONNULL
static void config_addrset(const char* res_name, const char* stanza, const bool ipv6, addrset_t* addrset, const vscf_data_t* cfg) {
    dmn_assert(res_name); dmn_assert(stanza); dmn_assert(addrset); dmn_assert(cfg);

    if(!vscf_is_hash(cfg))
        log_fatal("plugin_weighted: resource '%s' stanza '%s' value must be a hash", res_name, stanza);

    const vscf_data_t* parent = vscf_get_parent(cfg);

    // inherit down the applicable res-level parameters
    vscf_hash_inherit(parent, (vscf_data_t*)cfg, "service_types", true);
    vscf_hash_inherit(parent, (vscf_data_t*)cfg, "multi", true);
    vscf_hash_inherit(parent, (vscf_data_t*)cfg, "up_thresh", true);

    // Get a starting assumption of our item count
    addrset->count = vscf_hash_get_len(cfg);

    /////// Process the parameters...

    // service_types
    const vscf_data_t* res_stypes = vscf_hash_get_data_byconstkey(cfg, "service_types", true);
    if (res_stypes) {
        addrset->count--; // minus one for service_types entry
        addrset->num_svcs = vscf_array_get_len(res_stypes);
        if(!addrset->num_svcs)
            log_fatal("plugin_weighted: resource '%s' (%s): service_types cannot be an empty array", res_name, stanza);
        addrset->svc_names = malloc(addrset->num_svcs * sizeof(char*));
        for(unsigned i = 0; i < addrset->num_svcs; i++) {
            const vscf_data_t* this_svc_cfg = vscf_array_get_data(res_stypes, i);
            if(!vscf_is_simple(this_svc_cfg))
                log_fatal("plugin_weighted: resource '%s' (%s): service_types values must be strings", res_name, stanza);
            addrset->svc_names[i] = strdup(vscf_simple_get_data(this_svc_cfg));
        }
    }
    else {
        addrset->num_svcs = 1;
        addrset->svc_names = malloc(sizeof(char*));
        addrset->svc_names[0] = strdup("default");
    }

    // multi option
    addrset->multi = false;
    const vscf_data_t* multi_cfg = vscf_hash_get_data_byconstkey(cfg, "multi", true);
    if(multi_cfg) {
        addrset->count--; // minus one for multi entry
        if(!vscf_is_simple(multi_cfg) || !vscf_simple_get_as_bool(multi_cfg, &addrset->multi))
            log_fatal("plugin_weighted: resource '%s' (%s): 'multi' must be a boolean value ('true' or 'false')", res_name, stanza);
    }

    // up threshold as double
    double up_thresh = 0.5;
    const vscf_data_t* thresh_cfg = vscf_hash_get_data_byconstkey(cfg, "up_thresh", true);
    if(thresh_cfg) {
        addrset->count--; // minus one for up_thresh entry
        if(!vscf_is_simple(thresh_cfg) || !vscf_simple_get_as_double(thresh_cfg, &up_thresh)
           || up_thresh <= 0.0 || up_thresh > 1.0)
            log_fatal("plugin_weighted: resource '%s' (%s): 'up_thresh' must be a floating point value in the range (0.0 - 1.0]", res_name, stanza);
    }

    if(addrset->count > MAX_ITEMS_PER_SET)
        log_fatal("plugin_weighted: resource '%s' (%s): number of direct groups or addrs within one family cannot be more than %u", res_name, stanza, MAX_ITEMS_PER_SET);
    if(!addrset->count)
        log_fatal("plugin_weighted: resource '%s' (%s): empty address-family sets not allowed", res_name, stanza);

    // track maximum res-size actually configured
    if(cfg_max_items_per_res < addrset->count)
        cfg_max_items_per_res = addrset->count;

    addrset->items = calloc(addrset->count, sizeof(res_aitem_t));
    addrset->gmode = RES_ASET_UNKNOWN;
    addr_iter_data_t aid = {
        .item_idx = 0,
        .addrset = addrset,
        .res_name = res_name,
        .stanza = stanza,
        .ipv6 = ipv6
    };
    vscf_hash_iterate(cfg, true, config_addrset_item, &aid);

    addrset->weight = 0;
    addrset->max_weight = 0;
    for(unsigned i = 0; i < addrset->count; i++) {
        const unsigned iwt = addrset->items[i].weight;
        dmn_assert(iwt); dmn_assert(addrset->items[i].max_weight);
        addrset->weight += iwt;
        if(addrset->max_weight < iwt)
            addrset->max_weight = iwt;
    }

    dmn_assert(addrset->weight);
    dmn_assert(addrset->max_weight);

    addrset->up_weight = ceil(up_thresh * addrset->weight);
    dmn_assert(addrset->up_weight);
}

typedef struct {
    cnset_t* cnset;
    const char* res_name;
    const char* stanza;
    unsigned item_idx;
} cname_iter_data_t;

F_NONNULL
static bool config_item_cname(const char* item_name, unsigned klen V_UNUSED, const vscf_data_t* cfg_data, void* cid_asvoid) {
    dmn_assert(item_name); dmn_assert(cfg_data); dmn_assert(cid_asvoid);
    cname_iter_data_t* cid = (cname_iter_data_t*)cid_asvoid;

    cnset_t* cnset = cid->cnset;
    const char* res_name = cid->res_name;
    const char* stanza = cid->stanza;
    const unsigned item_idx = cid->item_idx++;
    res_citem_t* res_item = &cnset->items[item_idx];

    long wtemp = 0;
    if(!vscf_is_array(cfg_data)
            || (2 != vscf_array_get_len(cfg_data))
            || !vscf_is_simple(vscf_array_get_data(cfg_data, 0))
            || !vscf_is_simple(vscf_array_get_data(cfg_data, 1))
            || !vscf_simple_get_as_long(vscf_array_get_data(cfg_data, 1), &wtemp)
            || wtemp < 1 || wtemp > MAX_WEIGHT )
        log_fatal("plugin_weighted: resource '%s' (%s), item '%s': values in cname mode must be arrays of [ CNAME, WEIGHT ], where weight must be an integer in the range 1 - " MAX_WEIGHT_STR, res_name, stanza, item_name);
    res_item->weight = wtemp;

    uint8_t* dname = malloc(256);
    dname_status_t dnstat = vscf_simple_get_as_dname(vscf_array_get_data(cfg_data, 0), dname);
    if(dnstat == DNAME_INVALID)
        log_fatal("plugin_weighted: resource '%s' (%s), item '%s': '%s' is not a legal domainname", res_name, stanza, item_name, vscf_simple_get_data(vscf_array_get_data(cfg_data, 0)));
    if(dnstat == DNAME_VALID)
        dname = dname_trim(dname);
    res_item->cname = dname;

    log_debug("plugin_weighted: resource '%s' (%s), item '%s', CNAME '%s' added with weight %u", res_name, stanza, item_name, logf_dname(dname), res_item->weight);

    return true;
}