394b8dde17
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.
365 lines
7 KiB
C
365 lines
7 KiB
C
/*
|
|
* dns.c: An interface to the resolver module in authd
|
|
* (based somewhat on ircd-ratbox dns.c)
|
|
*
|
|
* Copyright (C) 2005 Aaron Sethman <androsyn@ratbox.org>
|
|
* Copyright (C) 2005-2012 ircd-ratbox development team
|
|
* Copyright (C) 2016 William Pitcock <nenolod@dereferenced.org>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
|
* 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 "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 uid, char type, const char *addr);
|
|
static void submit_dns_stat(uint16_t uid);
|
|
|
|
struct dnsreq
|
|
{
|
|
DNSCB callback;
|
|
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)
|
|
{
|
|
static uint16_t id = 1;
|
|
int loopcnt = 0;
|
|
while(1)
|
|
{
|
|
if(++loopcnt > DNS_IDTABLE_SIZE)
|
|
return 0;
|
|
if(id < DNS_IDTABLE_SIZE - 1 || id == 0)
|
|
id++;
|
|
else
|
|
id = 1;
|
|
if(querytable[id].callback == NULL)
|
|
break;
|
|
}
|
|
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)
|
|
{
|
|
struct dnsreq *req;
|
|
|
|
req = &querytable[xid];
|
|
if(req->callback == NULL)
|
|
return;
|
|
|
|
req->callback("FAILED", 0, 0, req->data);
|
|
req->callback = 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
|
|
cancel_lookup(uint16_t xid)
|
|
{
|
|
querytable[xid].callback = 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
|
|
lookup_hostname(const char *hostname, int aftype, DNSCB callback, void *data)
|
|
{
|
|
struct dnsreq *req;
|
|
int aft;
|
|
uint16_t nid;
|
|
check_authd();
|
|
nid = assign_dns_id();
|
|
if((nid = assign_dns_id()) == 0)
|
|
return 0;
|
|
|
|
req = &querytable[nid];
|
|
|
|
req->callback = callback;
|
|
req->data = data;
|
|
|
|
#ifdef RB_IPV6
|
|
if(aftype == AF_INET6)
|
|
aft = 6;
|
|
else
|
|
#endif
|
|
aft = 4;
|
|
|
|
submit_dns(nid, aft == 4 ? DNS_HOST_IPV4 : DNS_HOST_IPV6, hostname);
|
|
return (nid);
|
|
}
|
|
|
|
uint16_t
|
|
lookup_ip(const char *addr, int aftype, DNSCB callback, void *data)
|
|
{
|
|
struct dnsreq *req;
|
|
int aft;
|
|
uint16_t nid;
|
|
check_authd();
|
|
|
|
if((nid = assign_dns_id()) == 0)
|
|
return 0;
|
|
|
|
req = &querytable[nid];
|
|
|
|
req->callback = callback;
|
|
req->data = data;
|
|
|
|
#ifdef RB_IPV6
|
|
if(aftype == AF_INET6)
|
|
aft = 6;
|
|
else
|
|
#endif
|
|
aft = 4;
|
|
|
|
submit_dns(nid, aft == 4 ? DNS_REVERSE_IPV4 : DNS_REVERSE_IPV6, addr);
|
|
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)
|
|
{
|
|
struct dnsreq *req;
|
|
uint16_t nid;
|
|
int st;
|
|
int aft;
|
|
long lnid = strtol(callid, NULL, 16);
|
|
|
|
if(lnid > DNS_IDTABLE_SIZE || lnid == 0)
|
|
return;
|
|
nid = (uint16_t)lnid;
|
|
req = &querytable[nid];
|
|
st = (*status == 'O');
|
|
aft = *type == '6' || *type == 'S' ? 6 : 4;
|
|
if(req->callback == NULL)
|
|
{
|
|
/* got cancelled..oh well */
|
|
req->data = NULL;
|
|
return;
|
|
}
|
|
#ifdef RB_IPV6
|
|
if(aft == 6)
|
|
aft = AF_INET6;
|
|
else
|
|
#endif
|
|
aft = AF_INET;
|
|
|
|
req->callback(results, st, aft, req->data);
|
|
req->callback = NULL;
|
|
req->data = NULL;
|
|
}
|
|
|
|
void
|
|
dns_stats_results_callback(const char *callid, const char *status, int resc, const char *resv[])
|
|
{
|
|
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)
|
|
{
|
|
/* NOTE - this assumes req->data is strdup'd or such */
|
|
rb_free(req->data);
|
|
req->data = NULL;
|
|
return;
|
|
}
|
|
|
|
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
|
|
submit_dns(uint16_t nid, char type, const char *addr)
|
|
{
|
|
if(authd_helper == NULL)
|
|
{
|
|
handle_dns_failure(nid);
|
|
return;
|
|
}
|
|
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);
|
|
}
|