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.
This commit is contained in:
Elizabeth Myers 2016-03-08 02:53:25 -06:00
parent cdf5ed6cc8
commit 394b8dde17
9 changed files with 298 additions and 42 deletions

View file

@ -23,11 +23,33 @@
#define MAXPARA 10 #define MAXPARA 10
static void handle_stat(int parc, char *parv[]);
rb_helper *authd_helper = NULL; rb_helper *authd_helper = NULL;
authd_cmd_handler authd_cmd_handlers[255] = { authd_cmd_handler authd_cmd_handlers[255] = {
['D'] = resolve_dns, ['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 static void
parse_request(rb_helper *helper) parse_request(rb_helper *helper)
{ {

View file

@ -30,6 +30,8 @@
extern rb_helper *authd_helper; extern rb_helper *authd_helper;
typedef void (*authd_cmd_handler)(int parc, char *parv[]); 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_cmd_handler authd_cmd_handlers[255];
extern authd_stat_handler authd_stat_handlers[255];
#endif #endif

View file

@ -20,6 +20,7 @@
#include "authd.h" #include "authd.h"
#include "dns.h" #include "dns.h"
#include "res.h"
static void static void
submit_dns_answer(void *userdata, struct DNSReply *reply) submit_dns_answer(void *userdata, struct DNSReply *reply)
@ -136,3 +137,40 @@ resolve_dns(int parc, char *parv[])
else else
gethost_byaddr(&req->addr, &req->query); 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);
}

View file

@ -35,5 +35,6 @@ struct dns_request
}; };
extern void resolve_dns(int parc, char *parv[]); extern void resolve_dns(int parc, char *parv[]);
extern void enumerate_nameservers(const char *rid, const char letter);
#endif #endif

View file

@ -1111,9 +1111,9 @@ irc_dn_find(const unsigned char *domain, const unsigned char *msg,
} }
/* /*
* * Thinking in noninternationalized USASCII (per the DNS spec), * Thinking in noninternationalized USASCII (per the DNS spec),
* * convert this character to lower case if it's upper case. * convert this character to lower case if it's upper case.
* */ */
static int static int
mklower(int ch) mklower(int ch)
{ {

View file

@ -26,19 +26,17 @@
#ifndef CHARYBDIS_DNS_H #ifndef CHARYBDIS_DNS_H
#define 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); typedef void (*DNSCB)(const char *res, int status, int aftype, void *data);
typedef void (*DNSLISTCB)(int resc, const char *resv[], int status, void *data);
extern rb_helper *authd_helper;
void init_authd(void);
void restart_authd(void);
void rehash_authd(void);
void check_authd(void);
uint16_t lookup_hostname(const char *hostname, int aftype, DNSCB callback, 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); uint16_t lookup_ip(const char *hostname, int aftype, DNSCB callback, void *data);
void cancel_lookup(uint16_t xid); void cancel_lookup(uint16_t xid);
void dns_results_callback(const char *callid, const char *status, const char *aftype, const char *results); 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 #endif

View file

@ -97,11 +97,11 @@ parse_authd_reply(rb_helper * helper)
ssize_t len; ssize_t len;
int parc; int parc;
char dnsBuf[READBUF_SIZE]; char dnsBuf[READBUF_SIZE];
char *parv[MAXPARA + 1]; char *parv[MAXPARA + 1];
while((len = rb_helper_read(helper, dnsBuf, sizeof(dnsBuf))) > 0) 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]) switch (*parv[0])
{ {
@ -114,6 +114,33 @@ parse_authd_reply(rb_helper * helper)
} }
dns_results_callback(parv[1], parv[2], parv[3], parv[4]); dns_results_callback(parv[1], parv[2], parv[3], parv[4]);
break; 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: default:
break; break;
} }

View file

@ -22,28 +22,31 @@
* USA * USA
*/ */
#include <stdinc.h> #include "stdinc.h"
#include <rb_lib.h> #include "rb_lib.h"
#include <client.h> #include "client.h"
#include <ircd_defs.h> #include "ircd_defs.h"
#include <parse.h> #include "parse.h"
#include <dns.h> #include "dns.h"
#include <match.h> #include "match.h"
#include <logger.h> #include "logger.h"
#include <s_conf.h> #include "s_conf.h"
#include <client.h> #include "client.h"
#include <send.h> #include "send.h"
#include <numeric.h> #include "numeric.h"
#include <msg.h> #include "msg.h"
#include "hash.h"
#define DNS_IDTABLE_SIZE 0x2000 #define DNS_IDTABLE_SIZE 0x2000
#define DNS_STATTABLE_SIZE 0x10
#define DNS_HOST_IPV4 ((char)'4') #define DNS_HOST_IPV4 ((char)'4')
#define DNS_HOST_IPV6 ((char)'6') #define DNS_HOST_IPV6 ((char)'6')
#define DNS_REVERSE_IPV4 ((char)'R') #define DNS_REVERSE_IPV4 ((char)'R')
#define DNS_REVERSE_IPV6 ((char)'S') #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 struct dnsreq
{ {
@ -51,7 +54,20 @@ struct dnsreq
void *data; 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 dnsreq querytable[DNS_IDTABLE_SIZE];
static struct dnsstatreq stattable[DNS_STATTABLE_SIZE];
static uint16_t static uint16_t
assign_dns_id(void) assign_dns_id(void)
@ -72,6 +88,25 @@ assign_dns_id(void)
return (id); 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 static void
handle_dns_failure(uint16_t xid) handle_dns_failure(uint16_t xid)
{ {
@ -86,6 +121,25 @@ handle_dns_failure(uint16_t xid)
req->data = NULL; 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 void
cancel_lookup(uint16_t xid) cancel_lookup(uint16_t xid)
{ {
@ -93,6 +147,16 @@ cancel_lookup(uint16_t xid)
querytable[xid].data = NULL; 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 uint16_t
lookup_hostname(const char *hostname, int aftype, DNSCB callback, void *data) 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) if((nid = assign_dns_id()) == 0)
return 0; return 0;
req = &querytable[nid]; req = &querytable[nid];
req->callback = callback; req->callback = callback;
@ -147,6 +211,24 @@ lookup_ip(const char *addr, int aftype, DNSCB callback, void *data)
return (nid); 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 void
dns_results_callback(const char *callid, const char *status, const char *type, const char *results) 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; return;
nid = (uint16_t)lnid; nid = (uint16_t)lnid;
req = &querytable[nid]; req = &querytable[nid];
st = *status == 'O'; st = (*status == 'O');
aft = *type == '6' || *type == 'S' ? 6 : 4; aft = *type == '6' || *type == 'S' ? 6 : 4;
if(req->callback == NULL) if(req->callback == NULL)
{ {
@ -181,15 +263,82 @@ dns_results_callback(const char *callid, const char *status, const char *type, c
} }
void 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 struct dnsstatreq *req;
rb_dlink_node *ptr; uint8_t nid;
RB_DLINK_FOREACH(ptr, nameservers.head) 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 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); 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);
}

View file

@ -89,7 +89,7 @@ struct StatsStruct
int need_admin; 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_delay(struct Client *);
static void stats_hash(struct Client *); static void stats_hash(struct Client *);
static void stats_connect(struct Client *); static void stats_connect(struct Client *);
@ -132,8 +132,8 @@ static void stats_capability(struct Client *);
*/ */
static struct StatsStruct stats_cmd_table[] = { static struct StatsStruct stats_cmd_table[] = {
/* letter function need_oper need_admin */ /* letter function need_oper need_admin */
{'a', stats_dns_servers, 1, 1, }, {'a', NULL /* special */, 1, 1, },
{'A', stats_dns_servers, 1, 1, }, {'A', NULL /* special */, 1, 1, },
{'b', stats_delay, 1, 1, }, {'b', stats_delay, 1, 1, },
{'B', stats_hash, 1, 1, }, {'B', stats_hash, 1, 1, },
{'c', stats_connect, 0, 0, }, {'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) if(hunt_server (client_p, source_p, ":%s STATS %s :%s", 2, parc, parv) != HUNTED_ISME)
return 0; return 0;
if((statchar != 'L') && (statchar != 'l')) if((statchar != 'L') && (statchar != 'l') && (statchar != 'A') && (statchar != 'a'))
did_stats = stats_spy(source_p, statchar, NULL); did_stats = stats_spy(source_p, statchar, NULL);
/* if did_stats is true, a module grabbed this STATS request */ /* 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; break;
} }
/* Blah, stats L needs the parameters, none of the others do.. */
if(statchar == 'L' || statchar == 'l') if(statchar == 'L' || statchar == 'l')
{
/* Blah, stats L needs the parameters, none of the others do.. */
stats_ltrace (source_p, parc, parv); 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 else
stats_cmd_table[i].handler (source_p); stats_cmd_table[i].handler (source_p);
} }
@ -264,9 +272,9 @@ stats_out:
} }
static void 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 static void