/* * 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 * Copyright (C) 2003-2012 ircd-ratbox development team * Copyright (C) 2005-2008 Aaron Sethman * * $Id: resolver.c 29265 2015-12-20 04:35:39Z androsyn $ */ #define READBUF_SIZE 16384 #include "setup.h" #include #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; }