diff --git a/include/dns.h b/include/dns.h new file mode 100644 index 00000000..df8dafa4 --- /dev/null +++ b/include/dns.h @@ -0,0 +1,41 @@ +/* + * charybdis + * dns.h: A header with the DNS functions. + * + * Copyright (C) 1990 Jarkko Oikarinen and University of Oulu, Co Center + * Copyright (C) 1996-2002 Hybrid Development Team + * Copyright (C) 2002-2012 ircd-ratbox development team + * Copyright (C) 2016 William Pitcock + * + * 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 + */ + +#ifndef CHARYBDIS_DNS_H +#define CHARYBDIS_DNS_H + +typedef void (*DNSCB)(const char *res, int status, int aftype, void *data); + +void init_authd(void); +void restart_authd(void); +void rehash_authd(void); + +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 report_dns_servers(struct Client *); + +#endif diff --git a/ircd/Makefile.am b/ircd/Makefile.am index 9c1da2c7..8b8f4fee 100644 --- a/ircd/Makefile.am +++ b/ircd/Makefile.am @@ -23,6 +23,7 @@ libircd_la_SOURCES = \ chmode.c \ class.c \ client.c \ + dns.c \ extban.c \ getopt.c \ hash.c \ diff --git a/ircd/Makefile.in b/ircd/Makefile.in index bd99720a..12891c00 100644 --- a/ircd/Makefile.in +++ b/ircd/Makefile.in @@ -137,8 +137,8 @@ am__installdirs = "$(DESTDIR)$(libircddir)" "$(DESTDIR)$(bindir)" LTLIBRARIES = $(libircd_LTLIBRARIES) libircd_la_DEPENDENCIES = am_libircd_la_OBJECTS = bandbi.lo blacklist.lo cache.lo capability.lo \ - channel.lo chmode.lo class.lo client.lo extban.lo getopt.lo \ - hash.lo hook.lo hostmask.lo ipv4_from_ipv6.lo \ + channel.lo chmode.lo class.lo client.lo dns.lo extban.lo \ + getopt.lo hash.lo hook.lo hostmask.lo ipv4_from_ipv6.lo \ irc_dictionary.lo ircd.lo ircd_parser.lo ircd_lexer.lo \ ircd_signal.lo listener.lo logger.lo match.lo modules.lo \ monitor.lo newconf.lo operhash.lo packet.lo parse.lo \ @@ -437,6 +437,7 @@ libircd_la_SOURCES = \ chmode.c \ class.c \ client.c \ + dns.c \ extban.c \ getopt.c \ hash.c \ @@ -627,6 +628,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/chmode.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/class.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/client.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dns.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/extban.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getopt.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hash.Plo@am__quote@ diff --git a/ircd/dns.c b/ircd/dns.c new file mode 100644 index 00000000..2162e0c0 --- /dev/null +++ b/ircd/dns.c @@ -0,0 +1,328 @@ +/* + * dns.c: An interface to the resolver module in authd + * (based somewhat on ircd-ratbox dns.c) + * + * Copyright (C) 2005 Aaron Sethman + * Copyright (C) 2005-2012 ircd-ratbox development team + * Copyright (C) 2016 William Pitcock + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DNS_IDTABLE_SIZE 0x2000 + +#define DNS_HOST ((char)'H') +#define DNS_REVERSE ((char)'I') + +static void submit_dns(const char, uint16_t id, int aftype, const char *addr); + +static int start_authd(void); +static void parse_authd_reply(rb_helper * helper); +static void restart_authd_cb(rb_helper * helper); + +static rb_helper *authd_helper; + +struct dnsreq +{ + DNSCB callback; + void *data; +}; + +static struct dnsreq querytable[DNS_IDTABLE_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 inline void +check_authd(void) +{ + if(authd_helper == NULL) + restart_authd(); +} + +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; +} + +void +cancel_lookup(uint16_t xid) +{ + querytable[xid].callback = NULL; + querytable[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(DNS_HOST, nid, aft, 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(DNS_REVERSE, nid, aft, addr); + return (nid); +} + +static void +results_callback(const char *callid, const char *status, const char *aftype, 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 = atoi(status); + aft = atoi(aftype); + 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; +} + + +static char *resolver_path; + +static int +start_authd(void) +{ + char fullpath[PATH_MAX + 1]; +#ifdef _WIN32 + const char *suffix = ".exe"; +#else + const char *suffix = ""; +#endif + if(resolver_path == NULL) + { + snprintf(fullpath, sizeof(fullpath), "%s/authd%s", PKGLIBEXECDIR, suffix); + + if(access(fullpath, X_OK) == -1) + { + snprintf(fullpath, sizeof(fullpath), "%s/libexec/charybdis/authd%s", + ConfigFileEntry.dpath, suffix); + if(access(fullpath, X_OK) == -1) + { + ilog(L_MAIN, + "Unable to execute authd in %s or %s/libexec/charybdis", + PKGLIBEXECDIR, ConfigFileEntry.dpath); + sendto_realops_snomask(SNO_GENERAL, L_ALL, + "Unable to execute resolver in %s or %s/libexec/charybdis", + PKGLIBEXECDIR, ConfigFileEntry.dpath); + return 1; + } + + } + + resolver_path = rb_strdup(fullpath); + } + + authd_helper = rb_helper_start("authd", resolver_path, parse_authd_reply, restart_authd_cb); + + if(authd_helper == NULL) + { + ilog(L_MAIN, "Unable to start authd helper: %s", strerror(errno)); + sendto_realops_snomask(SNO_GENERAL, L_ALL, "Unable to start authd helper: %s", strerror(errno)); + return 1; + } + ilog(L_MAIN, "resolver helper started"); + sendto_realops_snomask(SNO_GENERAL, L_ALL, "resolver helper started"); + rb_helper_run(authd_helper); + return 0; +} + +void +report_dns_servers(struct Client *source_p) +{ +#if 0 + rb_dlink_node *ptr; + RB_DLINK_FOREACH(ptr, nameservers.head) + { + sendto_one_numeric(source_p, RPL_STATSDEBUG, "A %s", (char *)ptr->data); + } +#endif +} + +static void +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); + + if(*parv[0] == 'R') + { + if(parc != 5) + { + ilog(L_MAIN, "authd sent a result with wrong number of arguments: got %d", parc); + restart_authd(); + return; + } + results_callback(parv[1], parv[2], parv[3], parv[4]); + } + else + return; + } +} + +static void +submit_dns(char type, uint16_t nid, int aftype, const char *addr) +{ + if(authd_helper == NULL) + { + handle_dns_failure(nid); + return; + } + rb_helper_write(authd_helper, "%c %x %d %s", type, nid, aftype, addr); +} + +void +init_authd(void) +{ + if(start_authd()) + { + ilog(L_MAIN, "Unable to start authd helper: %s", strerror(errno)); + exit(0); + } +} + +static void +restart_authd_cb(rb_helper * helper) +{ + ilog(L_MAIN, "authd: restart_authd_cb called, authd died?"); + sendto_realops_snomask(SNO_GENERAL, L_ALL, "authd - restart_authd_cb called, authd died?"); + if(helper != NULL) + { + rb_helper_close(helper); + authd_helper = NULL; + } + start_authd(); +} + +void +restart_authd(void) +{ + restart_authd_cb(authd_helper); +} + +void +rehash_resolver(void) +{ + rb_helper_write(authd_helper, "R"); +} + diff --git a/ircd/res.c b/ircd/res.c index 5fe6fc6d..75cdae74 100644 --- a/ircd/res.c +++ b/ircd/res.c @@ -920,18 +920,3 @@ static struct DNSReply *make_dnsreply(struct reslist *request) memcpy(&cp->addr, &request->addr, sizeof(cp->addr)); return (cp); } - -void report_dns_servers(struct Client *source_p) -{ - int i; - char ipaddr[128]; - - 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); - sendto_one_numeric(source_p, RPL_STATSDEBUG, - "A %s %d", ipaddr, ns_failure_count[i]); - } -}