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
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)
{

View file

@ -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

View file

@ -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);
}

View file

@ -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

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),
* * 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)
{

View file

@ -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

View file

@ -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;
}

View file

@ -22,28 +22,31 @@
* USA
*/
#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 "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);
}

View file

@ -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