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