From 394b8dde17afc174fd1432897f2e368516dd8b41 Mon Sep 17 00:00:00 2001 From: Elizabeth Myers Date: Tue, 8 Mar 2016 02:53:25 -0600 Subject: [PATCH] authd: allow querying the list of DNS servers. This was an asston of pain, and it still feels "dirty" as it introduces an async call where there normally wouldn't be one. Better implementation more than welcome. --- authd/authd.c | 22 +++++ authd/authd.h | 2 + authd/dns.c | 38 +++++++++ authd/dns.h | 1 + authd/reslib.c | 6 +- include/dns.h | 14 ++-- ircd/authd.c | 31 ++++++- ircd/dns.c | 204 +++++++++++++++++++++++++++++++++++++++++----- modules/m_stats.c | 22 +++-- 9 files changed, 298 insertions(+), 42 deletions(-) diff --git a/authd/authd.c b/authd/authd.c index 36f5dae76..2e6861ae2 100644 --- a/authd/authd.c +++ b/authd/authd.c @@ -23,11 +23,33 @@ #define MAXPARA 10 +static void handle_stat(int parc, char *parv[]); + rb_helper *authd_helper = NULL; authd_cmd_handler authd_cmd_handlers[255] = { ['D'] = resolve_dns, + ['S'] = handle_stat, }; +authd_stat_handler authd_stat_handlers[255] = { + ['D'] = enumerate_nameservers, +}; + +static void +handle_stat(int parc, char *parv[]) +{ + authd_stat_handler handler; + + if(parc < 3) + /* XXX Should log this somehow */ + return; + + if (!(handler = authd_stat_handlers[parv[2][0]])) + return; + + handler(parv[1], parv[2][0]); +} + static void parse_request(rb_helper *helper) { diff --git a/authd/authd.h b/authd/authd.h index 08cd6c173..4770e0eb6 100644 --- a/authd/authd.h +++ b/authd/authd.h @@ -30,6 +30,8 @@ extern rb_helper *authd_helper; typedef void (*authd_cmd_handler)(int parc, char *parv[]); +typedef void (*authd_stat_handler)(const char *rid, const char letter); extern authd_cmd_handler authd_cmd_handlers[255]; +extern authd_stat_handler authd_stat_handlers[255]; #endif diff --git a/authd/dns.c b/authd/dns.c index cffaef1a7..9615d4534 100644 --- a/authd/dns.c +++ b/authd/dns.c @@ -20,6 +20,7 @@ #include "authd.h" #include "dns.h" +#include "res.h" static void submit_dns_answer(void *userdata, struct DNSReply *reply) @@ -136,3 +137,40 @@ resolve_dns(int parc, char *parv[]) else gethost_byaddr(&req->addr, &req->query); } + +void +enumerate_nameservers(const char *rid, const char letter) +{ + char buf[40 * IRCD_MAXNS]; /* Plenty */ + char *c = buf; + int i; + + if (!irc_nscount) + { + /* Shouldn't happen */ + rb_helper_write(authd_helper, "X %s %c NONAMESERVERS", rid, letter); + return; + } + + for(i = 0; i < irc_nscount; i++) + { + char addr[40]; + int ret; + + rb_inet_ntop_sock((struct sockaddr *)&irc_nsaddr_list[i], addr, sizeof(addr)); + + if (!addr[0]) + { + /* Shouldn't happen */ + rb_helper_write(authd_helper, "X %s %c INVALIDNAMESERVER", rid, letter); + return; + } + + ret = snprintf(c, 40, "%s ", addr); + c += (size_t)ret; + } + + *(--c) = '\0'; + + rb_helper_write(authd_helper, "Y %s %c %s", rid, letter, buf); +} diff --git a/authd/dns.h b/authd/dns.h index 9527bc0f5..c8c0f16fa 100644 --- a/authd/dns.h +++ b/authd/dns.h @@ -35,5 +35,6 @@ struct dns_request }; extern void resolve_dns(int parc, char *parv[]); +extern void enumerate_nameservers(const char *rid, const char letter); #endif diff --git a/authd/reslib.c b/authd/reslib.c index bd36da9be..6f0180db4 100644 --- a/authd/reslib.c +++ b/authd/reslib.c @@ -1111,9 +1111,9 @@ irc_dn_find(const unsigned char *domain, const unsigned char *msg, } /* - * * Thinking in noninternationalized USASCII (per the DNS spec), - * * convert this character to lower case if it's upper case. - * */ + * Thinking in noninternationalized USASCII (per the DNS spec), + * convert this character to lower case if it's upper case. + */ static int mklower(int ch) { diff --git a/include/dns.h b/include/dns.h index b06ab1e6c..b0be628c4 100644 --- a/include/dns.h +++ b/include/dns.h @@ -26,19 +26,17 @@ #ifndef CHARYBDIS_DNS_H #define CHARYBDIS_DNS_H +#include "stdinc.h" +#include "authd.h" + typedef void (*DNSCB)(const char *res, int status, int aftype, void *data); - -extern rb_helper *authd_helper; - -void init_authd(void); -void restart_authd(void); -void rehash_authd(void); -void check_authd(void); +typedef void (*DNSLISTCB)(int resc, const char *resv[], int status, void *data); uint16_t lookup_hostname(const char *hostname, int aftype, DNSCB callback, void *data); uint16_t lookup_ip(const char *hostname, int aftype, DNSCB callback, void *data); void cancel_lookup(uint16_t xid); void dns_results_callback(const char *callid, const char *status, const char *aftype, const char *results); -void report_dns_servers(struct Client *); +void dns_stats_results_callback(const char *callid, const char *status, int resc, const char *resv[]); +void report_dns_servers(struct Client *, char); #endif diff --git a/ircd/authd.c b/ircd/authd.c index 7fbdab632..491d02379 100644 --- a/ircd/authd.c +++ b/ircd/authd.c @@ -97,11 +97,11 @@ parse_authd_reply(rb_helper * helper) ssize_t len; int parc; char dnsBuf[READBUF_SIZE]; - char *parv[MAXPARA + 1]; + while((len = rb_helper_read(helper, dnsBuf, sizeof(dnsBuf))) > 0) { - parc = rb_string_to_array(dnsBuf, parv, MAXPARA+1); + parc = rb_string_to_array(dnsBuf, parv, MAXPARA+1); switch (*parv[0]) { @@ -114,6 +114,33 @@ parse_authd_reply(rb_helper * helper) } dns_results_callback(parv[1], parv[2], parv[3], parv[4]); break; + case 'X': + case 'Y': + case 'Z': + if(parc < 3) + { + ilog(L_MAIN, "authd sent a result with wrong number of arguments: got %d", parc); + restart_authd(); + return; + } + + /* Select by type */ + switch(*parv[2]) + { + case 'D': + /* parv[0] conveys status */ + if(parc < 4) + { + ilog(L_MAIN, "authd sent a result with wrong number of arguments: got %d", parc); + restart_authd(); + return; + } + dns_stats_results_callback(parv[1], parv[0], parc - 3, (const char **)&parv[3]); + break; + default: + break; + } + break; default: break; } diff --git a/ircd/dns.c b/ircd/dns.c index ed6cd57ed..416b9c491 100644 --- a/ircd/dns.c +++ b/ircd/dns.c @@ -22,28 +22,31 @@ * USA */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include "stdinc.h" +#include "rb_lib.h" +#include "client.h" +#include "ircd_defs.h" +#include "parse.h" +#include "dns.h" +#include "match.h" +#include "logger.h" +#include "s_conf.h" +#include "client.h" +#include "send.h" +#include "numeric.h" +#include "msg.h" +#include "hash.h" #define DNS_IDTABLE_SIZE 0x2000 +#define DNS_STATTABLE_SIZE 0x10 #define DNS_HOST_IPV4 ((char)'4') #define DNS_HOST_IPV6 ((char)'6') #define DNS_REVERSE_IPV4 ((char)'R') #define DNS_REVERSE_IPV6 ((char)'S') -static void submit_dns(uint16_t id, char type, const char *addr); +static void submit_dns(uint16_t uid, char type, const char *addr); +static void submit_dns_stat(uint16_t uid); struct dnsreq { @@ -51,7 +54,20 @@ struct dnsreq void *data; }; +struct dnsstatreq +{ + DNSLISTCB callback; + void *data; +}; + +struct dnsstatreq_data +{ + char uid[IDLEN]; + char statchar; +}; + static struct dnsreq querytable[DNS_IDTABLE_SIZE]; +static struct dnsstatreq stattable[DNS_STATTABLE_SIZE]; static uint16_t assign_dns_id(void) @@ -72,6 +88,25 @@ assign_dns_id(void) return (id); } +static uint8_t +assign_dns_stat_id(void) +{ + static uint8_t id = 1; + int loopcnt = 0; + while(1) + { + if(++loopcnt > DNS_STATTABLE_SIZE) + return 0; + if(id < DNS_STATTABLE_SIZE - 1 || id == 0) + id++; + else + id = 1; + if(stattable[id].callback == NULL) + break; + } + return (id); +} + static void handle_dns_failure(uint16_t xid) { @@ -86,6 +121,25 @@ handle_dns_failure(uint16_t xid) req->data = NULL; } +static void +handle_dns_stat_failure(uint8_t xid) +{ + struct dnsstatreq *req; + const char *err[] = { "Unknown failure" }; + + req = &stattable[xid]; + if(req->callback == NULL) + return; + + req->callback(1, err, 2, req->data); + + /* NOTE - this assumes req->data is on the heap */ + rb_free(req->data); + + req->callback = NULL; + req->data = NULL; +} + void cancel_lookup(uint16_t xid) { @@ -93,6 +147,16 @@ cancel_lookup(uint16_t xid) querytable[xid].data = NULL; } +void +cancel_dns_stats(uint16_t xid) +{ + /* NOTE - this assumes data is on the heap */ + rb_free(stattable[xid].data); + + stattable[xid].callback = NULL; + stattable[xid].data = NULL; +} + uint16_t lookup_hostname(const char *hostname, int aftype, DNSCB callback, void *data) { @@ -130,7 +194,7 @@ lookup_ip(const char *addr, int aftype, DNSCB callback, void *data) if((nid = assign_dns_id()) == 0) return 0; - + req = &querytable[nid]; req->callback = callback; @@ -147,6 +211,24 @@ lookup_ip(const char *addr, int aftype, DNSCB callback, void *data) return (nid); } +uint8_t +get_nameservers(DNSLISTCB callback, void *data) +{ + struct dnsstatreq *req; + uint8_t nid; + check_authd(); + + if((nid = assign_dns_stat_id()) == 0) + return 0; + + req = &stattable[nid]; + req->callback = callback; + req->data = data; + + submit_dns_stat(nid); + return (nid); +} + void dns_results_callback(const char *callid, const char *status, const char *type, const char *results) { @@ -160,7 +242,7 @@ dns_results_callback(const char *callid, const char *status, const char *type, c return; nid = (uint16_t)lnid; req = &querytable[nid]; - st = *status == 'O'; + st = (*status == 'O'); aft = *type == '6' || *type == 'S' ? 6 : 4; if(req->callback == NULL) { @@ -181,15 +263,82 @@ dns_results_callback(const char *callid, const char *status, const char *type, c } void -report_dns_servers(struct Client *source_p) +dns_stats_results_callback(const char *callid, const char *status, int resc, const char *resv[]) { -#if 0 - rb_dlink_node *ptr; - RB_DLINK_FOREACH(ptr, nameservers.head) + struct dnsstatreq *req; + uint8_t nid; + int st, i; + long lnid = strtol(callid, NULL, 16); + + if(lnid > DNS_STATTABLE_SIZE || lnid == 0) + return; + nid = (uint8_t)lnid; + req = &stattable[nid]; + + if(req->callback == NULL) { - sendto_one_numeric(source_p, RPL_STATSDEBUG, "A %s", (char *)ptr->data); + /* NOTE - this assumes req->data is strdup'd or such */ + rb_free(req->data); + req->data = NULL; + return; } -#endif + + switch(*status) + { + case 'Y': + st = 0; + break; + case 'X': + /* Error */ + st = 1; + break; + default: + /* Shouldn't happen... */ + return; + } + + /* Query complete */ + req->callback(resc, resv, st, stattable[nid].data); + + rb_free(req->data); + req->data = NULL; + req->callback = NULL; +} + +static void +report_dns_servers_cb(int resc, const char *resv[], int status, void *data) +{ + struct Client *source_p; + struct dnsstatreq_data *c_data = data; + + if(!(source_p = find_id(c_data->uid))) + /* Client's gone, oh well. */ + return; + + if(status == 0) + { + for(int i = 0; i < resc; i++) + sendto_one_numeric(source_p, RPL_STATSDEBUG, "A %s", resv[i]); + } + else + { + if(resc && resv[resc][0]) + /* XXX is this the right reply? */ + sendto_one_numeric(source_p, RPL_STATSDEBUG, "A Error: %s", resv[resc]); + } + + sendto_one_numeric(source_p, RPL_ENDOFSTATS, form_str(RPL_ENDOFSTATS), c_data->statchar); +} + +void +report_dns_servers(struct Client *source_p, char statchar) +{ + /* Use the UID to avoid a race where source_p goes away */ + struct dnsstatreq_data *data = rb_malloc(sizeof(struct dnsstatreq_data)); + rb_strlcpy(data->uid, source_p->id, IDLEN); + data->statchar = statchar; + + get_nameservers(report_dns_servers_cb, data); } static void @@ -202,3 +351,14 @@ submit_dns(uint16_t nid, char type, const char *addr) } rb_helper_write(authd_helper, "D %x %c %s", nid, type, addr); } + +static void +submit_dns_stat(uint16_t nid) +{ + if(authd_helper == NULL) + { + handle_dns_stat_failure(nid); + return; + } + rb_helper_write(authd_helper, "S %x D", nid); +} diff --git a/modules/m_stats.c b/modules/m_stats.c index a2cf3ceb7..cedd60edd 100644 --- a/modules/m_stats.c +++ b/modules/m_stats.c @@ -89,7 +89,7 @@ struct StatsStruct int need_admin; }; -static void stats_dns_servers(struct Client *); +static void stats_dns_servers(struct Client *, char); static void stats_delay(struct Client *); static void stats_hash(struct Client *); static void stats_connect(struct Client *); @@ -132,8 +132,8 @@ static void stats_capability(struct Client *); */ static struct StatsStruct stats_cmd_table[] = { /* letter function need_oper need_admin */ - {'a', stats_dns_servers, 1, 1, }, - {'A', stats_dns_servers, 1, 1, }, + {'a', NULL /* special */, 1, 1, }, + {'A', NULL /* special */, 1, 1, }, {'b', stats_delay, 1, 1, }, {'B', stats_hash, 1, 1, }, {'c', stats_connect, 0, 0, }, @@ -219,7 +219,7 @@ m_stats(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source_ if(hunt_server (client_p, source_p, ":%s STATS %s :%s", 2, parc, parv) != HUNTED_ISME) return 0; - if((statchar != 'L') && (statchar != 'l')) + if((statchar != 'L') && (statchar != 'l') && (statchar != 'A') && (statchar != 'a')) did_stats = stats_spy(source_p, statchar, NULL); /* if did_stats is true, a module grabbed this STATS request */ @@ -247,9 +247,17 @@ m_stats(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source_ break; } - /* Blah, stats L needs the parameters, none of the others do.. */ if(statchar == 'L' || statchar == 'l') + { + /* Blah, stats L needs the parameters, none of the others do.. */ stats_ltrace (source_p, parc, parv); + } + else if(statchar == 'a' || statchar == 'A') + { + /* Need to suppress RPL_ENDOFSTATS since this is an async call */ + stats_dns_servers(source_p, statchar); + return 0; + } else stats_cmd_table[i].handler (source_p); } @@ -264,9 +272,9 @@ stats_out: } static void -stats_dns_servers (struct Client *source_p) +stats_dns_servers (struct Client *source_p, char statchar) { - report_dns_servers (source_p); + report_dns_servers (source_p, statchar); } static void