429 lines
7.8 KiB
C
429 lines
7.8 KiB
C
/*
|
|
* resolver.c: dns resolving daemon for ircd-ratbox
|
|
* Based on many things ripped from ratbox-services
|
|
* and ircd-ratbox itself and who knows what else
|
|
*
|
|
* Copyright (C) 2003-2005 Lee Hardy <leeh@leeh.co.uk>
|
|
* Copyright (C) 2003-2012 ircd-ratbox development team
|
|
* Copyright (C) 2005-2008 Aaron Sethman <androsyn@ratbox.org>
|
|
*
|
|
* $Id: resolver.c 29265 2015-12-20 04:35:39Z androsyn $
|
|
*/
|
|
|
|
#define READBUF_SIZE 16384
|
|
|
|
#include "setup.h"
|
|
#include <ratbox_lib.h>
|
|
#include "res.h"
|
|
#include "reslib.h"
|
|
|
|
#define MAXPARA 10
|
|
#define REQIDLEN 10
|
|
|
|
#define REQREV 0
|
|
#define REQFWD 1
|
|
|
|
#define REVIPV4 0
|
|
#define REVIPV6 1
|
|
#define REVIPV6INT 2
|
|
#define FWDHOST 4
|
|
|
|
#define EmptyString(x) (!(x) || (*(x) == '\0'))
|
|
|
|
static int do_rehash;
|
|
static rb_helper *res_helper;
|
|
|
|
static char readBuf[READBUF_SIZE];
|
|
static void resolve_ip(char **parv);
|
|
static void resolve_host(char **parv);
|
|
static void report_nameservers(void);
|
|
|
|
#ifdef RB_IPV6
|
|
struct in6_addr ipv6_addr;
|
|
#endif
|
|
struct in_addr ipv4_addr;
|
|
|
|
|
|
struct dns_request
|
|
{
|
|
struct DNSQuery query;
|
|
char reqid[REQIDLEN];
|
|
struct rb_sockaddr_storage addr;
|
|
int reqtype;
|
|
int revfwd;
|
|
};
|
|
|
|
|
|
#ifndef WINDOWS
|
|
static void
|
|
dummy_handler(int sig)
|
|
{
|
|
return;
|
|
}
|
|
|
|
static void
|
|
rehash(int sig)
|
|
{
|
|
do_rehash = 1;
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
setup_signals()
|
|
{
|
|
#ifndef WINDOWS
|
|
struct sigaction act;
|
|
|
|
act.sa_flags = 0;
|
|
act.sa_handler = SIG_IGN;
|
|
sigemptyset(&act.sa_mask);
|
|
sigaddset(&act.sa_mask, SIGPIPE);
|
|
sigaction(SIGPIPE, &act, 0);
|
|
sigaddset(&act.sa_mask, SIGALRM);
|
|
sigaction(SIGALRM, &act, 0);
|
|
sigaddset(&act.sa_mask, SIGINT);
|
|
sigaction(SIGINT, &act, 0);
|
|
#ifdef SIGTRAP
|
|
sigaddset(&act.sa_mask, SIGTRAP);
|
|
sigaction(SIGTRAP, &act, 0);
|
|
#endif
|
|
|
|
#ifdef SIGWINCH
|
|
sigaddset(&act.sa_mask, SIGWINCH);
|
|
sigaction(SIGWINCH, &act, 0);
|
|
#endif
|
|
sigaction(SIGPIPE, &act, 0);
|
|
#ifdef SIGTRAP
|
|
sigaction(SIGTRAP, &act, 0);
|
|
#endif
|
|
|
|
act.sa_handler = dummy_handler;
|
|
sigaddset(&act.sa_mask, SIGALRM);
|
|
sigaction(SIGALRM, &act, 0);
|
|
act.sa_handler = rehash;
|
|
sigaddset(&act.sa_mask, SIGHUP);
|
|
sigaction(SIGHUP, &act, 0);
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
error_cb(rb_helper *helper)
|
|
{
|
|
exit(1);
|
|
}
|
|
|
|
|
|
static void
|
|
send_answer(void *vptr, struct DNSReply *reply)
|
|
{
|
|
struct dns_request *req = (struct dns_request *)vptr;
|
|
char response[64];
|
|
int result = 0;
|
|
int aftype = 0;
|
|
strcpy(response, "FAILED");
|
|
if(reply != NULL)
|
|
{
|
|
switch (req->revfwd)
|
|
{
|
|
case REQREV:
|
|
{
|
|
if(req->reqtype == REVIPV4)
|
|
{
|
|
struct sockaddr_in *ip, *ip_fwd;
|
|
ip = (struct sockaddr_in *)&req->addr;
|
|
ip_fwd = (struct sockaddr_in *)&reply->addr;
|
|
aftype = 4;
|
|
if(ip->sin_addr.s_addr != ip_fwd->sin_addr.s_addr)
|
|
{
|
|
result = 0;
|
|
break;
|
|
}
|
|
}
|
|
#ifdef RB_IPV6
|
|
else if(req->reqtype == REVIPV6)
|
|
{
|
|
struct sockaddr_in6 *ip, *ip_fwd;
|
|
ip = (struct sockaddr_in6 *)&req->addr;
|
|
ip_fwd = (struct sockaddr_in6 *)&reply->addr;
|
|
aftype = 6;
|
|
if(memcmp
|
|
(&ip->sin6_addr, &ip_fwd->sin6_addr,
|
|
sizeof(struct in6_addr)) != 0)
|
|
{
|
|
result = 0;
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
else
|
|
{
|
|
/* uhh wut? */
|
|
result = 0;
|
|
break;
|
|
}
|
|
|
|
if(strlen(reply->h_name) < 63)
|
|
{
|
|
strcpy(response, reply->h_name);
|
|
result = 1;
|
|
}
|
|
else
|
|
{
|
|
strcpy(response, "HOSTTOOLONG");
|
|
result = 0;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case REQFWD:
|
|
{
|
|
#ifdef RB_IPV6
|
|
if(GET_SS_FAMILY(&reply->addr) == AF_INET6)
|
|
{
|
|
char tmpres[65];
|
|
rb_inet_ntop_sock((struct sockaddr *)&reply->addr,
|
|
tmpres, sizeof(tmpres) - 1);
|
|
aftype = 6;
|
|
if(*tmpres == ':')
|
|
{
|
|
strcpy(response, "0");
|
|
strcat(response, tmpres);
|
|
}
|
|
else
|
|
strcpy(response, tmpres);
|
|
result = 1;
|
|
break;
|
|
}
|
|
else
|
|
#endif
|
|
if(GET_SS_FAMILY(&reply->addr) == AF_INET)
|
|
{
|
|
result = 1;
|
|
aftype = 4;
|
|
rb_inet_ntop_sock((struct sockaddr *)&reply->addr,
|
|
response, sizeof(response));
|
|
break;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
rb_helper_write(res_helper, "R %s %d %d %s\n", req->reqid, result, aftype, response);
|
|
rb_free(req);
|
|
}
|
|
|
|
static void
|
|
set_bind(char **parv)
|
|
{
|
|
char *ipv4 = parv[2];
|
|
#ifdef RB_IPV6
|
|
char *ipv6 = parv[3];
|
|
#endif
|
|
if(!strcmp(ipv4, "0"))
|
|
ipv4_addr.s_addr = INADDR_ANY;
|
|
else
|
|
rb_inet_pton(AF_INET, ipv4, &ipv4_addr);
|
|
#ifdef RB_IPV6
|
|
if(!strcmp(ipv6, "0"))
|
|
memcpy(&ipv6_addr, &in6addr_any, sizeof(ipv6_addr));
|
|
else
|
|
rb_inet_pton(AF_INET6, ipv6, &ipv6_addr);
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
request protocol:
|
|
|
|
INPUTS:
|
|
|
|
IPTYPE: 4, 5, 6, ipv4, ipv6.int/arpa, ipv6 respectively
|
|
requestid: identifier of the request
|
|
|
|
|
|
RESIP requestid IPTYPE IP
|
|
RESHST requestid IPTYPE hostname
|
|
|
|
OUTPUTS:
|
|
ERR error string = daemon failed and is going to shutdown
|
|
otherwise
|
|
|
|
FWD requestid PASS/FAIL hostname or reason for failure
|
|
REV requestid PASS/FAIL IP or reason
|
|
|
|
*/
|
|
|
|
|
|
static void
|
|
parse_request(rb_helper *helper)
|
|
{
|
|
int len;
|
|
static char *parv[MAXPARA + 1];
|
|
int parc;
|
|
while((len = rb_helper_read(helper, readBuf, sizeof(readBuf))) > 0)
|
|
{
|
|
parc = rb_string_to_array(readBuf, parv, MAXPARA);
|
|
switch (*parv[0])
|
|
{
|
|
case 'I':
|
|
if(parc != 4)
|
|
abort();
|
|
resolve_ip(parv);
|
|
break;
|
|
case 'H':
|
|
if(parc != 4)
|
|
abort();
|
|
resolve_host(parv);
|
|
break;
|
|
case 'B':
|
|
if(parc != 4)
|
|
abort();
|
|
set_bind(parv);
|
|
break;
|
|
case 'R':
|
|
restart_resolver();
|
|
report_nameservers();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
resolve_host(char **parv)
|
|
{
|
|
struct dns_request *req;
|
|
char *requestid = parv[1];
|
|
char *iptype = parv[2];
|
|
char *rec = parv[3];
|
|
int flags;
|
|
|
|
req = rb_malloc(sizeof(struct dns_request));
|
|
strcpy(req->reqid, requestid);
|
|
|
|
req->revfwd = REQFWD;
|
|
req->reqtype = FWDHOST;
|
|
|
|
switch (*iptype)
|
|
{
|
|
case 6:
|
|
flags = T_AAAA;
|
|
break;
|
|
default:
|
|
flags = T_A;
|
|
break;
|
|
}
|
|
|
|
req->query.ptr = req;
|
|
req->query.callback = send_answer;
|
|
gethost_byname_type(rec, &req->query, flags);
|
|
}
|
|
|
|
static void
|
|
resolve_ip(char **parv)
|
|
{
|
|
char *requestid = parv[1];
|
|
char *iptype = parv[2];
|
|
char *rec = parv[3];
|
|
int aftype;
|
|
struct dns_request *req;
|
|
if(strlen(requestid) >= REQIDLEN)
|
|
exit(3);
|
|
|
|
req = rb_malloc(sizeof(struct dns_request));
|
|
req->revfwd = REQREV;
|
|
strcpy(req->reqid, requestid);
|
|
|
|
if(!rb_inet_pton_sock(rec, (struct sockaddr *)&req->addr))
|
|
exit(6);
|
|
|
|
aftype = GET_SS_FAMILY(&req->addr);
|
|
switch (*iptype)
|
|
{
|
|
case '4':
|
|
req->reqtype = REVIPV4;
|
|
if(aftype != AF_INET)
|
|
exit(6);
|
|
break;
|
|
case '6':
|
|
req->reqtype = REVIPV6;
|
|
if(aftype != AF_INET6)
|
|
exit(6);
|
|
break;
|
|
default:
|
|
exit(7);
|
|
}
|
|
req->query.ptr = req;
|
|
req->query.callback = send_answer;
|
|
gethost_byaddr(&req->addr, &req->query);
|
|
}
|
|
|
|
extern int irc_nscount;
|
|
extern struct rb_sockaddr_storage irc_nsaddr_list[];
|
|
|
|
static void
|
|
report_nameservers(void)
|
|
{
|
|
int i;
|
|
char ipaddr[HOSTIPLEN + 1];
|
|
char buf[512];
|
|
buf[0] = '\0';
|
|
for(i = 0; i < irc_nscount; i++)
|
|
{
|
|
if(!rb_inet_ntop_sock
|
|
((struct sockaddr *)&(irc_nsaddr_list[i]), ipaddr, sizeof(ipaddr)))
|
|
{
|
|
rb_strlcpy(ipaddr, "?", sizeof(ipaddr));
|
|
}
|
|
rb_snprintf_append(buf, sizeof(buf), "%s ", ipaddr);
|
|
}
|
|
rb_helper_write(res_helper, "A %s", buf);
|
|
|
|
}
|
|
|
|
static void
|
|
check_rehash(void *unused)
|
|
{
|
|
if(do_rehash)
|
|
{
|
|
restart_resolver();
|
|
do_rehash = 0;
|
|
report_nameservers();
|
|
}
|
|
}
|
|
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
res_helper = rb_helper_child(parse_request, error_cb, NULL, NULL, NULL, 256, 1024, 256, 256); /* XXX fix me */
|
|
|
|
if(res_helper == NULL)
|
|
{
|
|
fprintf(stderr,
|
|
"This is ircd-ratbox resolver. You know you aren't supposed to run me directly?\n");
|
|
fprintf(stderr,
|
|
"You get an Id tag for this: $Id: resolver.c 29265 2015-12-20 04:35:39Z androsyn $\n");
|
|
fprintf(stderr, "Have a nice life\n");
|
|
exit(1);
|
|
}
|
|
rb_set_time();
|
|
setup_signals();
|
|
init_resolver();
|
|
rb_init_prng(NULL, RB_PRNG_DEFAULT);
|
|
rb_event_add("check_rehash", check_rehash, NULL, 5);
|
|
report_nameservers();
|
|
rb_helper_loop(res_helper, 0);
|
|
return 1;
|
|
}
|