diff options
Diffstat (limited to 'src/dnsbl.c')
-rw-r--r-- | src/dnsbl.c | 300 |
1 files changed, 300 insertions, 0 deletions
diff --git a/src/dnsbl.c b/src/dnsbl.c new file mode 100644 index 0000000..4973842 --- /dev/null +++ b/src/dnsbl.c @@ -0,0 +1,300 @@ +/* + * Copyright (c) 2002-2003 Erik Fears + * Copyright (c) 2014-2018 ircd-hybrid development team + * + * 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 "setup.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netdb.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <assert.h> + +#include "compat.h" +#include "config.h" +#include "dnsbl.h" +#include "list.h" +#include "log.h" +#include "main.h" +#include "match.h" +#include "memory.h" +#include "scan.h" +#include "irc.h" +#include "stats.h" + + +/* + * Work out the DNSBL zones and send the dns query + */ +void +dnsbl_add(struct scan_struct *ss) +{ + char lookup[128]; + node_t *node; + struct addrinfo hints, *addr_res; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_flags = AI_NUMERICHOST; + + if (getaddrinfo(ss->ip, NULL, &hints, &addr_res)) + { + log_printf("DNSBL -> Invalid address '%s', ignoring.", ss->ip); + return; + } + + LIST_FOREACH(node, OpmItem.blacklists.head) + { + struct BlacklistConf *bl = node->data; + + if (addr_res->ai_family == AF_INET && bl->ipv4) + { + const struct sockaddr_in *v4 = (const struct sockaddr_in *)addr_res->ai_addr; + const uint8_t *b = (const uint8_t *)&v4->sin_addr.s_addr; + + snprintf(lookup, sizeof(lookup), "%u.%u.%u.%u.%s", + (unsigned int)(b[3]), (unsigned int)(b[2]), + (unsigned int)(b[1]), (unsigned int)(b[0]), bl->name); + } + else if (addr_res->ai_family == AF_INET6 && bl->ipv6) + { + const struct sockaddr_in6 *v6 = (const struct sockaddr_in6 *)addr_res->ai_addr; + const uint8_t *b = (const uint8_t *)&v6->sin6_addr.s6_addr; + + snprintf(lookup, sizeof(lookup), + "%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x." + "%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%s", + (unsigned int)(b[15] & 0xF), (unsigned int)(b[15] >> 4), + (unsigned int)(b[14] & 0xF), (unsigned int)(b[14] >> 4), + (unsigned int)(b[13] & 0xF), (unsigned int)(b[13] >> 4), + (unsigned int)(b[12] & 0xF), (unsigned int)(b[12] >> 4), + (unsigned int)(b[11] & 0xF), (unsigned int)(b[11] >> 4), + (unsigned int)(b[10] & 0xF), (unsigned int)(b[10] >> 4), + (unsigned int)(b[9] & 0xF), (unsigned int)(b[9] >> 4), + (unsigned int)(b[8] & 0xF), (unsigned int)(b[8] >> 4), + (unsigned int)(b[7] & 0xF), (unsigned int)(b[7] >> 4), + (unsigned int)(b[6] & 0xF), (unsigned int)(b[6] >> 4), + (unsigned int)(b[5] & 0xF), (unsigned int)(b[5] >> 4), + (unsigned int)(b[4] & 0xF), (unsigned int)(b[4] >> 4), + (unsigned int)(b[3] & 0xF), (unsigned int)(b[3] >> 4), + (unsigned int)(b[2] & 0xF), (unsigned int)(b[2] >> 4), + (unsigned int)(b[1] & 0xF), (unsigned int)(b[1] >> 4), + (unsigned int)(b[0] & 0xF), (unsigned int)(b[0] >> 4), bl->name); + } + else + continue; + + struct dnsbl_scan *ds = xcalloc(sizeof *ds); + ds->ss = ss; + ds->bl = bl; + + if (OPT_DEBUG) + log_printf("DNSBL -> Passed '%s' to resolver", lookup); + + int res = firedns_getip(FDNS_QRY_A, lookup, ds); + if (res == -1 && firedns_errno != FDNS_ERR_FDLIMIT) + { + log_printf("DNSBL -> Error sending dns lookup for '%s': %s", lookup, firedns_strerror(firedns_errno)); + xfree(ds); + } + else + ++ss->scans; /* Increase scan count - one for each blacklist */ + } + + freeaddrinfo(addr_res); +} + +static void +dnsbl_positive(struct scan_struct *ss, struct BlacklistConf *bl, unsigned char type) +{ + char text_type[128] = ""; + node_t *node; + + if (bl->type == A_BITMASK) + { + LIST_FOREACH(node, bl->reply.head) + { + const struct BlacklistReplyConf *item = node->data; + + if (item->number & type) + { + strlcat(text_type, item->type, sizeof(text_type)); + strlcat(text_type, ", ", sizeof(text_type)); + } + } + + if (text_type[0]) + *(strrchr(text_type, ',')) = '\0'; + } + else + { + LIST_FOREACH(node, bl->reply.head) + { + const struct BlacklistReplyConf *item = node->data; + + if (item->number == type) + { + strlcpy(text_type, item->type, sizeof(text_type)); + break; + } + } + } + + if (text_type[0] == '\0' && bl->ban_unknown == 0) + { + if (OPT_DEBUG) + log_printf("DNSBL -> Unknown result from BL zone %s (%d)", bl->name, type); + + return; + } + + if (ss->manual_target) + irc_send("PRIVMSG %s :CHECK -> DNSBL -> %s appears in BL zone %s (%s)", + ss->manual_target, ss->ip, bl->name, text_type); + else if (ss->positive == 0) + { + /* Only report it if no other scans have found positives yet. */ + scan_positive(ss, (EmptyString(bl->kline) ? IRCItem.kline : bl->kline), text_type); + + irc_send_channels("DNSBL -> %s!%s@%s [%s] appears in BL zone %s (%s)", + ss->irc_nick, ss->irc_username, ss->irc_hostname, ss->ip, bl->name, + text_type); + log_printf("DNSBL -> %s!%s@%s [%s] appears in BL zone %s (%s)", + ss->irc_nick, ss->irc_username, ss->irc_hostname, ss->ip, bl->name, + text_type); + } + + /* Record stat */ + stats_dnsblrecv(bl); +} + +void +dnsbl_result(struct firedns_result *res) +{ + struct dnsbl_scan *const ds = res->info; + + if (OPT_DEBUG) + { + if (ds->ss->manual_target) + log_printf("DNSBL -> Lookup result for %s (%s) %d.%d.%d.%d (error: %d)", + ds->ss->ip, + res->lookup, + (unsigned char)res->text[0], + (unsigned char)res->text[1], + (unsigned char)res->text[2], + (unsigned char)res->text[3], firedns_errno); + else + log_printf("DNSBL -> Lookup result for %s!%s@%s (%s) %d.%d.%d.%d (error: %d)", + ds->ss->irc_nick, + ds->ss->irc_username, + ds->ss->irc_hostname, + res->lookup, + (unsigned char)res->text[0], + (unsigned char)res->text[1], + (unsigned char)res->text[2], + (unsigned char)res->text[3], firedns_errno); + } + + /* Everything is OK */ + if (res->text[0] == '\0' && firedns_errno == FDNS_ERR_NXDOMAIN) + { + if (ds->ss->manual_target) + irc_send("PRIVMSG %s :CHECK -> DNSBL -> %s does not appear in BL zone %s", + ds->ss->manual_target, ds->ss->ip, ds->bl->name); + + --ds->ss->scans; /* We are done with ss here */ + scan_checkfinished(ds->ss); /* This could free ss, don't use ss after this point */ + xfree(ds); /* No longer need our information */ + return; + } + + /* Either an error, or a positive lookup */ + if (firedns_errno == FDNS_ERR_NONE) + dnsbl_positive(ds->ss, ds->bl, (unsigned char)res->text[3]); + else + { + log_printf("DNSBL -> Lookup error on %s: %s", res->lookup, + firedns_strerror(firedns_errno)); + + if (firedns_errno != FDNS_ERR_TIMEOUT) + irc_send_channels("DNSBL -> Lookup error on %s: %s", res->lookup, + firedns_strerror(firedns_errno)); + } + + /* Check if ss has any remaining scans */ + --ds->ss->scans; /* We are done with ss here */ + scan_checkfinished(ds->ss); /* This could free ss, don't use ss after this point */ + xfree(ds); /* Finished with dnsbl_scan too */ +} + +void +dnsbl_cycle(void) +{ + firedns_cycle(); +} + +/* + * Send an email to report this open proxy. + */ +void +dnsbl_report(const struct scan_struct *ss) +{ + char buf[2048], cmdbuf[256]; + FILE *fp; + + assert(ss->ip); + + if (EmptyString(OpmItem.dnsbl_to) || EmptyString(OpmItem.dnsbl_from) || EmptyString(OpmItem.sendmail)) + return; + + snprintf(cmdbuf, sizeof(cmdbuf), "%s -t", OpmItem.sendmail); + snprintf(buf, sizeof(buf), + "From: %s <%s>\n" + "To: %s\n" + "Subject: HOPM Report\n" + "X-HOPM-Version: %s\n\n" + "%s: %s:%d\n\n" + "%s\n", IRCItem.nick, OpmItem.dnsbl_from, OpmItem.dnsbl_to, + VERSION, scan_gettype(ss->remote->protocol), ss->ip, + ss->remote->port, ss->proof); + + if (OPT_DEBUG >= 3) + log_printf("DNSBL -> Sending following email:\n%s\n", buf); + + if ((fp = popen(cmdbuf, "w")) == NULL) + { + log_printf("DNSBL -> Failed to create pipe to '%s' for email report!", cmdbuf); + irc_send_channels("I was trying to create a pipe to '%s' to send a DNSBL " + "report, and it failed! I'll give up for now.", + cmdbuf); + return; + } + + fputs(buf, fp); + pclose(fp); + + log_printf("DNSBL -> Sent report to %s [%s]", OpmItem.dnsbl_to, ss->ip); + + /* Record send in stats */ + stats_dnsblsend(); +} |