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; }
static void mon_child_cb(struct ev_loop* loop, ev_child* w, int revents V_UNUSED) { dmn_assert(loop); dmn_assert(w); dmn_assert(revents == EV_CHILD); ev_child_stop(loop, w); // always single-shot mon_t* this_mon = w->data; ev_timer_stop(loop, this_mon->cmd_timeout); this_mon->cmd_pid = 0; bool failed = true; int status = w->rstatus; if(WIFEXITED(status)) { if(!WEXITSTATUS(status)) failed = false; } else { if(WIFSIGNALED(status)) dmn_log_warn("Monitor child process for '%s' terminated by signal %u", this_mon->cmd->desc, WTERMSIG(status)); else dmn_log_warn("Monitor child process for '%s' terminated abnormally...", this_mon->cmd->desc); } // If timeout already sent a failure, don't double-send // here when we reap the SIGKILL'd child if(this_mon->result_pending) { if(!killed_by) { sendq_enq(emc_encode_mon(this_mon->cmd->idx, failed)); ev_io_start(loop, plugin_write_watcher); } if (num_proc > 0) { num_proc--; } this_mon->result_pending = false; } }
F_NONNULL static unsigned ntree_lookup_inner(const ntree_t* tree, const anysin_t* client_addr, unsigned* scope_mask) { dmn_assert(tree); dmn_assert(client_addr); dmn_assert(scope_mask); unsigned rv; if(client_addr->sa.sa_family == AF_INET) { rv = ntree_lookup_v4(tree, ntohl(client_addr->sin.sin_addr.s_addr), scope_mask); } else { dmn_assert(client_addr->sa.sa_family == AF_INET6); unsigned mask_adj = 0; // for v4-like conversions... const uint32_t ipv4 = v6_v4fixup(client_addr->sin6.sin6_addr.s6_addr, &mask_adj); if(mask_adj) { unsigned temp_mask; rv = ntree_lookup_v4(tree, ipv4, &temp_mask); *scope_mask = temp_mask + mask_adj; } else { rv = ntree_lookup_v6(tree, client_addr->sin6.sin6_addr.s6_addr, scope_mask); } } return rv; }
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; }
F_NONNULL static uint32_t geoip2_get_dclist_cached(geoip2_t* db, const uint32_t offset) { dmn_assert(db); // In GeoIP2, an offset of zero means not found in the DB, so default it. // (even if it works in geoip2_get_dclist(), the offset cache can't handle // an offset of zero efficiently anyways). if(!offset) return 0; unsigned bucket_size = 0; const unsigned ndx = offset % OFFSET_CACHE_SIZE; if(db->offset_cache[ndx]) { for(bucket_size = 0; db->offset_cache[ndx][bucket_size].offset; bucket_size++) if(db->offset_cache[ndx][bucket_size].offset == offset) return db->offset_cache[ndx][bucket_size].dclist; } const uint32_t dclist = geoip2_get_dclist(db, offset); db->offset_cache[ndx] = xrealloc(db->offset_cache[ndx], sizeof(offset_cache_item_t) * (bucket_size + 2)); dmn_assert(db->offset_cache[ndx]); db->offset_cache[ndx][bucket_size].offset = offset; db->offset_cache[ndx][bucket_size].dclist = dclist; db->offset_cache[ndx][bucket_size + 1].offset = 0; dmn_assert(dclist <= DCLIST_MAX); // auto not allowed here, should have been resolved earlier return dclist; }
F_NONNULL static void die_gracefully(struct ev_loop* loop) { dmn_assert(loop); dmn_assert(killed_by); static bool done_once = false; if(!done_once) { // avoid repetition done_once = true; // send friendly death message to plugin sendq_enq(emc_encode_exit()); ev_io_start(loop, plugin_write_watcher); // kill interval timers for future invocations // and immediately clamp the remaining timeout // for any running commands to 2.0s. for(unsigned i = 0; i < num_mons; i++) { ev_timer_stop(loop, mons[i].interval_timer); if(ev_is_active(mons[i].cmd_timeout)) { if(ev_timer_remaining(loop, mons[i].cmd_timeout) > 2.0) { ev_timer_stop(loop, mons[i].cmd_timeout); ev_timer_set(mons[i].cmd_timeout, 2.0, 0.); ev_timer_start(loop, mons[i].cmd_timeout); } } } } }
static plugin_t* gdnsd_plugin_load(const char* pname) { dmn_assert(pname); dmn_assert(psearch); plugin_t* plug = plugin_allocate(pname); void* pptr = plugin_dlopen(pname); const gdnsd_apiv_cb_t apiv = (gdnsd_apiv_cb_t)plugin_dlsym(pptr, pname, "get_api_version"); if(!apiv) log_fatal("Plugin '%s' does not appear to be a valid gdnsd plugin", pname); // The raw number for the API version is now split into two 16-bit chunks: // the bottom 16 bits are still a regular version number // the top 16 bits are build option flags that affect binary compatibility // (these come from <gdnsd/bopt.h> for 3rd party plugins) const uint32_t this_version = apiv(); if(this_version != GDNSD_PLUGIN_API_VERSION) { unsigned apiv_vers = GDNSD_PLUGIN_API_VERSION & 0xFFFF; unsigned apiv_bopt = GDNSD_PLUGIN_API_VERSION >> 16; unsigned this_vers = this_version & 0xFFFF; unsigned this_bopt = this_version >> 16; if(apiv_vers != this_vers) log_fatal("Plugin '%s' needs to be recompiled! (wanted API version %u, got %u)", pname, apiv_vers, this_vers); else log_fatal("Plugin '%s' needs to be recompiled! (wanted build options %x, got %x)", pname, apiv_bopt, this_bopt); }
void gdmaps_test_lookup_check(const gdmaps_t* gdmaps, const char* map_name, const char* addr_txt, const char* dclist_cmp, const unsigned scope_cmp) { dmn_assert(gdmaps); dmn_assert(map_name); dmn_assert(addr_txt); dmn_assert(dclist_cmp); const int rv = gdmaps_name2idx(gdmaps, map_name); if(rv < 0) log_fatal("Map name '%s' not found in configuration", map_name); const unsigned map_idx = (unsigned)rv; client_info_t cinfo; cinfo.edns_client_mask = 128U; unsigned scope = 175U; const int addr_err = gdnsd_anysin_getaddrinfo(addr_txt, NULL, &cinfo.edns_client); if(addr_err) log_fatal("Cannot parse address '%s': %s", addr_txt, gai_strerror(addr_err)); const uint8_t* dclist = gdmaps_lookup(gdmaps, map_idx, &cinfo, &scope); ok(!strcmp((const char*)dclist, dclist_cmp), "gdmaps_lookup(%s, %s) returns dclist %s (got %s)", map_name, addr_txt, gdmaps_logf_dclist(gdmaps, map_idx, (const uint8_t*)dclist_cmp), gdmaps_logf_dclist(gdmaps, map_idx, dclist)); ok(scope == scope_cmp, "gdmaps_lookup(%s, %s) returns scope %u (got %u)", map_name, addr_txt, scope_cmp, scope); }
// grow a dnhash_t's hashtable size by doubling F_NONNULL static void dnhash_grow(dnhash_t* dnhash) { dmn_assert(dnhash); dmn_assert(dnhash->count); // assert that dnhash->mask is still 2^n-1 and >0 dmn_assert(dnhash->mask); dmn_assert(!((dnhash->mask + 1U) & dnhash->mask)); const uint8_t** old_table = dnhash->table; const unsigned old_mask = dnhash->mask; const unsigned new_mask = (old_mask << 1U) | 1U; const uint8_t** new_table = xcalloc(new_mask + 1U, sizeof(uint8_t*)); for(unsigned i = 0; i <= old_mask; i++) { const uint8_t* item = old_table[i]; if(item) { unsigned jmpby = 1U; unsigned new_slot = dname_hash(item) & new_mask; while(new_table[new_slot]) { new_slot += jmpby++; new_slot &= new_mask; } new_table[new_slot] = item; } } free(dnhash->table); dnhash->table = new_table; dnhash->mask = new_mask; }
static void plugin_write_cb(struct ev_loop* loop, ev_io* w, int revents V_UNUSED) { dmn_assert(loop); dmn_assert(w); dmn_assert(revents == EV_WRITE); while(!sendq_empty()) { const uint32_t data = sendq_deq_peek(); int rv = write(plugin_write_fd, &data, 4); if(rv != 4) { if(rv < 0) { if(errno == EAGAIN) return; // pipe full, wait for more libev notification of write-ready else if(errno == EINTR) continue; // try this write again immediately else { ev_break(loop, EVBREAK_ALL); return; } } else if(rv == 0) { ev_break(loop, EVBREAK_ALL); return; } else { log_fatal("BUG: atomic pipe write of 4 bytes was not atomic, retval was %u", rv); } } sendq_deq_commit(); } ev_io_stop(loop, w); // queue now empty }
static void plugin_write_cb(struct ev_loop* loop, ev_io* w, int revents V_UNUSED) { dmn_assert(loop); dmn_assert(w); dmn_assert(revents == EV_WRITE); dmn_assert(plugin_write_fd > -1); while(!sendq_empty()) { const uint32_t data = sendq_deq_peek(); ssize_t write_rv = write(plugin_write_fd, &data, 4); if(write_rv != 4) { if(write_rv < 0) { if(errno == EAGAIN || errno == EWOULDBLOCK) return; // pipe full, wait for more libev notification of write-ready else if(errno == EINTR) continue; // try this write again immediately else { ev_break(loop, EVBREAK_ALL); return; } } else if(write_rv == 0) { ev_break(loop, EVBREAK_ALL); return; } else { log_fatal("BUG: atomic pipe write of 4 bytes was not atomic, retval was %zi", write_rv); } } sendq_deq_commit(); } ev_io_stop(loop, w); // queue now empty if(killed_by) { // we've sent our final message, close close(plugin_write_fd); plugin_write_fd = -1; } }
F_NONNULL static void make_resource(resource_t* res, const char* res_name, const vscf_data_t* res_cfg) { dmn_assert(res); dmn_assert(res_name); dmn_assert(res_cfg); res->name = strdup(res_name); if(!vscf_is_hash(res_cfg)) log_fatal("plugin_" PNSTR ": the value of resource '%s' must be a hash", res_name); res->map = res_get_mapnum(res_cfg, res_name); unsigned dc_count = map_get_len(res->map); dmn_assert(dc_count); // empty lists not allowed! // the core item: dcmap (dc -> result map) const vscf_data_t* dcs_cfg = vscf_hash_get_data_byconstkey(res_cfg, "dcmap", true); if(!dcs_cfg) log_fatal("plugin_" PNSTR ": resource '%s': missing required stanza 'dcmap'", res_name); // Get/check datacenter count res->num_dcs = vscf_hash_get_len(dcs_cfg); if(res->num_dcs != dc_count) log_fatal("plugin_" PNSTR ": resource '%s': the dcmap does not match the datacenters list", res_name); res->dcs = config_res_perdc(res->map, dcs_cfg, res_name); }
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; }
F_NONNULL static void dnhash_destroy(dnhash_t* dnhash) { dmn_assert(dnhash); dmn_assert(dnhash->table); dmn_assert(dnhash->mask); free(dnhash->table); free(dnhash); }
F_NONNULL static void terminal_signal(struct ev_loop* loop, struct ev_signal *w, const int revents V_UNUSED) { dmn_assert(loop); dmn_assert(w); dmn_assert(revents == EV_SIGNAL); dmn_assert(w->signum == SIGTERM || w->signum == SIGINT); log_info("Received terminating signal %i, exiting", w->signum); ev_break(loop, EVBREAK_ALL); }
void plugin_http_status_init_monitors(struct ev_loop* mon_loop) { dmn_assert(mon_loop); for(unsigned int i = 0; i < num_mons; i++) { ev_timer* ival_watcher = mons[i]->interval_watcher; dmn_assert(mons[i]->sock == -1); ev_timer_set(ival_watcher, 0, 0); ev_timer_start(mon_loop, ival_watcher); } }
F_MALLOC F_WUNUSED static dnhash_t* dnhash_new(void) { dmn_assert(INIT_DNHASH_MASK); dmn_assert(!((INIT_DNHASH_MASK + 1U) & INIT_DNHASH_MASK)); // 2^n-1 dnhash_t* rv = xmalloc(sizeof(dnhash_t)); rv->count = 0; rv->mask = INIT_DNHASH_MASK; rv->table = xcalloc(INIT_DNHASH_MASK + 1U, sizeof(uint8_t*)); return rv; }
F_NONNULL static gen_func_ptr plugin_dlsym(void* handle, const char* pname, const char* sym_suffix) { dmn_assert(handle); dmn_assert(pname); dmn_assert(sym_suffix); // If you see an aliasing warning here, it's ok to ignore it char* symname = gdnsd_str_combine_n(4, "plugin_", pname, "_", sym_suffix); gen_func_ptr rval; *(void**)(&rval) = dlsym(handle, symname); free(symname); return rval; }
F_NONNULL static void statio_fill_outbuf_csv(struct iovec* outbufs) { dmn_assert(outbufs); populate_stats(); dmn_assert(pop_statio_time >= start_time); outbufs[1].iov_len = snprintf(outbufs[1].iov_base, data_buffer_size, csv_fixed, (uint64_t)pop_statio_time - start_time, statio.dns_noerror, statio.dns_refused, statio.dns_nxdomain, statio.dns_notimp, statio.dns_badvers, statio.dns_formerr, statio.dns_dropped, statio.dns_v6, statio.dns_edns, statio.dns_edns_clientsub, statio.udp_reqs, statio.udp_recvfail, statio.udp_sendfail, statio.udp_tc, statio.udp_edns_big, statio.udp_edns_tc, statio.tcp_reqs, statio.tcp_recvfail, statio.tcp_sendfail); outbufs[1].iov_len += monio_stats_out_csv(ADDVOID(outbufs[1].iov_base, outbufs[1].iov_len)); outbufs[0].iov_len = snprintf(outbufs[0].iov_base, hdr_buffer_size, http_headers, "text/plain", (unsigned)outbufs[1].iov_len); }
F_NONNULL static unsigned map_get_dcidx(const unsigned mapnum, const char* dcname) { dmn_assert(dcname); dmn_assert(mapnum < num_dclists); dclist_t* this_map = dclists[mapnum]; for(unsigned i = 1; i <= this_map->num_dcs; i++) if(!strcmp(dcname, this_map->dc_names[i])) return i; return 0; }
F_NONNULL static void do_lookup(const gdmaps_t* gdmaps, const char* map_name, const char* ip_arg) { dmn_assert(gdmaps); dmn_assert(map_name); dmn_assert(ip_arg); int map_idx = gdmaps_name2idx(gdmaps, map_name); if(map_idx < 0) { log_err("Mapping name '%s' not found in configuration", map_name); return; } client_info_t cinfo; // mostly ignored, but needs to be nonzero, and 150 is interesting in that // it easily differentiates source -> scope copies from actual database scope netmasks, // since it's larger than any legal netmask in the database. cinfo.edns_client_mask = 150U; const int addr_err = gdnsd_anysin_getaddrinfo(ip_arg, NULL, &cinfo.edns_client); if(addr_err) { log_err("Could not parse address '%s': %s", ip_arg, gai_strerror(addr_err)); return; } // To void gdmaps fallback pitfalls memcpy(&cinfo.dns_source, &cinfo.edns_client, sizeof(dmn_anysin_t)); // w/ edns_client_mask set, scope_mask should *always* be set by gdmaps_lookup(); // (and regardless, dclist should also always be set and contain something) unsigned scope_mask = 175U; const uint8_t* dclist = gdmaps_lookup(gdmaps, map_idx, &cinfo, &scope_mask); dmn_assert(scope_mask != 175U); dmn_assert(dclist); // Scope was set to Source. Since we always query as edns, this implies // the database was V4-only and the address input was a non-v4-compat v6 address, // and the lookup code fell back to the default dclist (1). if(scope_mask == 150U) { printf( "%s => %s => %s\n", map_name, dmn_logf_anysin_noport(&cinfo.edns_client), gdmaps_logf_dclist(gdmaps, map_idx, dclist) ); } else { printf( "%s => %s/%u => %s\n", map_name, dmn_logf_anysin_noport(&cinfo.edns_client), scope_mask, gdmaps_logf_dclist(gdmaps, map_idx, dclist) ); } dmn_fmtbuf_reset(); }
unsigned ntree_add_node(ntree_t* tree) { dmn_assert(tree); dmn_assert(tree->alloc); if(tree->count == tree->alloc) { tree->alloc <<= 1; tree->store = realloc(tree->store, tree->alloc * sizeof(nnode_t)); } const unsigned rv = tree->count; dmn_assert(rv < (1U << 24)); tree->count++; return rv; }
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; }
F_NONNULL static const vscf_data_t* conf_load(const char* cfg_file) { dmn_assert(cfg_file); char* vscf_err; const vscf_data_t* cfg_root = vscf_scan_filename(cfg_file, &vscf_err); if(!cfg_root) log_fatal("Configuration load failed: %s", vscf_err); dmn_assert(vscf_is_hash(cfg_root)); return cfg_root; }
// returns either a node offset for the true ipv4 root // node at exactly ::/96, or a terminal dclist // for a wholly enclosing supernet. This is cached // for the tree to make various ipv4-related lookups // faster and simpler. F_NONNULL static unsigned ntree_find_v4root(const ntree_t* tree) { dmn_assert(tree); unsigned offset = 0; unsigned mask_depth = 96; do { dmn_assert(offset < tree->count); offset = tree->store[offset].zero; } while(--mask_depth && !NN_IS_DCLIST(offset)); return offset; }
void plugin_http_status_start_monitors(struct ev_loop* mon_loop) { dmn_assert(mon_loop); for(unsigned int i = 0; i < num_mons; i++) { http_events_t* mon = mons[i]; dmn_assert(mon->sock == -1); const unsigned ival = mon->http_svc->interval; const double stagger = (((double)i) / ((double)num_mons)) * ((double)ival); ev_timer* ival_watcher = mon->interval_watcher; ev_timer_set(ival_watcher, stagger, ival); ev_timer_start(mon_loop, ival_watcher); } }
F_NONNULL F_NOINLINE static void isolate_jmp(geoip2_t* db, nlist_t** nl) { dmn_assert(db); dmn_assert(nl); *nl = nlist_new(db->map_name, true); if(!sigsetjmp(db->jbuf, 0)) { geoip2_list_xlate(db, *nl); nlist_finish(*nl); } else { nlist_destroy(*nl); *nl = NULL; } }
char* gdnsd_str_combine(const char* s1, const char* s2, const char** s2_offs) { dmn_assert(s1); dmn_assert(s2); const unsigned s1_len = strlen(s1); const unsigned s2_len = strlen(s2); char* out = xmalloc(s1_len + s2_len + 1); char* work = out; memcpy(work, s1, s1_len); work += s1_len; memcpy(work, s2, s2_len); work[s2_len] = 0; if(s2_offs) *s2_offs = work; return out; }
F_NONNULL static void mon_read_cb(struct ev_loop* loop, struct ev_io* io, const int revents V_UNUSED) { dmn_assert(loop); dmn_assert(io); dmn_assert(revents == EV_READ); http_events_t* md = (http_events_t*)io->data; dmn_assert(md); dmn_assert(md->hstate == HTTP_STATE_READING); dmn_assert(ev_is_active(md->read_watcher)); dmn_assert(!ev_is_active(md->write_watcher)); dmn_assert(md->sock > -1); bool final_status = false; const int to_recv = 13 - md->done; const int recvd = recv(md->sock, md->res_buf + md->done, to_recv, 0); if(unlikely(recvd == -1)) { switch(errno) { case EAGAIN: case EINTR: return; case ETIMEDOUT: case ENOTCONN: case ECONNRESET: case EPIPE: break; default: log_err("plugin_http_status: read() from monitoring socket failed, possible local problem: %s", logf_errno()); } } else if(recvd < to_recv) { md->done += recvd; return; } else { md->res_buf[13] = '\0'; char code_str[4] = { 0 }; if(1 == sscanf(md->res_buf, "HTTP/1.%*1[01]%*1[ ]%3c%*1[ ]", code_str)) { unsigned long lcode = strtoul(code_str, NULL, 10); for(unsigned i = 0; i < md->http_svc->num_ok_codes; i++) { if(lcode == md->http_svc->ok_codes[i]) { final_status = true; break; } } } } // I don't believe we actually need to read the rest of the response before // shutdown/close in order to avoid bad TCP behavior, but I could be wrong. log_debug("plugin_http_status: State poll of %s %s", md->smgr->desc, final_status ? "succeeded" : "failed"); shutdown(md->sock, SHUT_RDWR); close(md->sock); md->sock = -1; ev_io_stop(loop, md->read_watcher); ev_timer_stop(loop, md->timeout_watcher); md->hstate = HTTP_STATE_WAITING; gdnsd_mon_state_updater(md->smgr, final_status); }
static void make_req_data(http_svc_t* s, const char* url_path, const char* vhost) { dmn_assert(s); dmn_assert(url_path); const unsigned url_len = strlen(url_path); if(vhost) { s->req_data_len = 25 + url_len + strlen(vhost); s->req_data = malloc(s->req_data_len + 1); snprintf(s->req_data, s->req_data_len + 1, "GET %s HTTP/1.0\r\nHost: %s\r\n\r\n", url_path, vhost); } else { s->req_data_len = 17 + url_len; s->req_data = malloc(s->req_data_len + 1); snprintf(s->req_data, s->req_data_len + 1, "GET %s HTTP/1.0\r\n\r\n", url_path); } }