1 /* +------------------------------------+
\r * | Inspire Internet Relay Chat Daemon |
\r * +------------------------------------+
\r *
\r * InspIRCd: (C) 2002-2007 InspIRCd Development Team
\r * See: http://www.inspircd.org/wiki/index.php/Credits
\r *
\r * This program is free but copyrighted software; see
\r * the file COPYING for details.
\r *
\r * ---------------------------------------------------
\r */
\r\r#include "inspircd.h"
\r#include "xline.h"
\r#include "dns.h"
\r#include "users.h"
\r#include "channels.h"
\r#include "modules.h"
\r\r#ifndef WINDOWS
\r#include <sys/types.h>
\r#include <sys/socket.h>
\r#include <netinet/in.h>
\r#include <arpa/inet.h>
\r#endif
\r\r/* $ModDesc: Provides handling of DNS blacklists */
\r\r/* Class holding data for a single entry */
\rclass DNSBLConfEntry
\r{
\r public:
\r enum EnumBanaction { I_UNKNOWN, I_KILL, I_ZLINE, I_KLINE, I_GLINE };
\r std::string name, domain, reason;
\r EnumBanaction banaction;
\r long duration;
\r int bitmask;
\r unsigned long stats_hits, stats_misses;
\r DNSBLConfEntry(): duration(86400),bitmask(0),stats_hits(0), stats_misses(0) {}
\r ~DNSBLConfEntry() { }
\r};
\r\r\r/** Resolver for CGI:IRC hostnames encoded in ident/GECOS
\r */
\rclass DNSBLResolver : public Resolver
\r{
\r int theirfd;
\r userrec* them;
\r DNSBLConfEntry *ConfEntry;
\r\r public:
\r\r DNSBLResolver(Module *me, InspIRCd *ServerInstance, const std::string &hostname, userrec* u, int userfd, DNSBLConfEntry *conf, bool &cached)
\r : Resolver(ServerInstance, hostname, DNS_QUERY_A, cached, me)
\r {
\r theirfd = userfd;
\r them = u;
\r ConfEntry = conf;
\r }
\r\r virtual void OnLookupComplete(const std::string &result, unsigned int ttl, bool cached)
\r {
\r /* Check the user still exists */
\r if ((them) && (them == ServerInstance->SE->GetRef(theirfd)))
\r {
\r // Now we calculate the bitmask: 256*(256*(256*a+b)+c)+d
\r if(result.length())
\r {
\r unsigned int bitmask = 0;
\r bool show = false;
\r in_addr resultip;
\r\r /* Convert the result to an in_addr (we can gaurantee we got ipv4)
\r * Whoever did the loop that was here before, I AM CONFISCATING
\r * YOUR CRACKPIPE. you know who you are. -- Brain
\r */
\r inet_aton(result.c_str(), &resultip);
\r bitmask = resultip.s_addr >> 24; /* Last octet (network byte order */
\r\r bitmask &= ConfEntry->bitmask;
\r\r if (bitmask != 0)
\r {
\r std::string reason = ConfEntry->reason;
\r std::string::size_type x = reason.find("%ip%");
\r while (x != std::string::npos)
\r {
\r reason.erase(x, 4);
\r reason.insert(x, them->GetIPString());
\r x = reason.find("%ip%");
\r }
\r\r ConfEntry->stats_hits++;
\r\r switch (ConfEntry->banaction)
\r {
\r case DNSBLConfEntry::I_KILL:
\r {
\r userrec::QuitUser(ServerInstance, them, std::string("Killed (") + reason + ")");
\r break;
\r }
\r case DNSBLConfEntry::I_KLINE:
\r {
\r std::string ban = std::string("*@") + them->GetIPString();
\r if (show)
\r ServerInstance->XLines->apply_lines(APPLY_KLINES);
\r show = ServerInstance->XLines->add_kline(ConfEntry->duration, ServerInstance->Config->ServerName, reason.c_str(), ban.c_str());
\r FOREACH_MOD(I_OnAddKLine,OnAddKLine(ConfEntry->duration, NULL, reason, ban));
\r break;
\r }
\r case DNSBLConfEntry::I_GLINE:
\r {
\r std::string ban = std::string("*@") + them->GetIPString();
\r show = ServerInstance->XLines->add_gline(ConfEntry->duration, ServerInstance->Config->ServerName, reason.c_str(), ban.c_str());
\r if (show)
\r ServerInstance->XLines->apply_lines(APPLY_GLINES);
\r FOREACH_MOD(I_OnAddGLine,OnAddGLine(ConfEntry->duration, NULL, reason, ban));
\r break;
\r }
\r case DNSBLConfEntry::I_ZLINE:
\r {
\r show = ServerInstance->XLines->add_zline(ConfEntry->duration, ServerInstance->Config->ServerName, reason.c_str(), them->GetIPString());
\r if (show)
\r ServerInstance->XLines->apply_lines(APPLY_ZLINES);
\r FOREACH_MOD(I_OnAddZLine,OnAddZLine(ConfEntry->duration, NULL, reason, them->GetIPString()));
\r break;
\r }
\r case DNSBLConfEntry::I_UNKNOWN:
\r {
\r break;
\r }
\r break;
\r }
\r\r if (show)
\r {
\r ServerInstance->WriteOpers("*** Connecting user %s detected as being on a DNS blacklist (%s) with result %d", them->GetFullRealHost(), ConfEntry->name.c_str(), bitmask);
\r }
\r }
\r else
\r ConfEntry->stats_misses++;
\r }
\r else
\r ConfEntry->stats_misses++;
\r }
\r }
\r\r virtual void OnError(ResolverError e, const std::string &errormessage)
\r {
\r }
\r\r virtual ~DNSBLResolver()
\r {
\r }
\r};
\r\rclass ModuleDNSBL : public Module
\r{
\r private:
\r std::vector<DNSBLConfEntry *> DNSBLConfEntries;
\r\r /*
\r * Convert a string to EnumBanaction
\r */
\r DNSBLConfEntry::EnumBanaction str2banaction(const std::string &action)
\r {
\r if(action.compare("KILL")==0)
\r return DNSBLConfEntry::I_KILL;
\r if(action.compare("KLINE")==0)
\r return DNSBLConfEntry::I_KLINE;
\r if(action.compare("ZLINE")==0)
\r return DNSBLConfEntry::I_ZLINE;
\r if(action.compare("GLINE")==0)
\r return DNSBLConfEntry::I_GLINE;
\r\r return DNSBLConfEntry::I_UNKNOWN;
\r }
\r public:
\r ModuleDNSBL(InspIRCd *Me) : Module(Me)
\r {
\r ReadConf();
\r }
\r\r virtual ~ModuleDNSBL()
\r {
\r ClearEntries();
\r }
\r\r virtual Version GetVersion()
\r {
\r return Version(2, 0, 0, 1, VF_VENDOR, API_VERSION);
\r }
\r\r void Implements(char* List)
\r {
\r List[I_OnRehash] = List[I_OnUserRegister] = List[I_OnStats] = 1;
\r }
\r\r /** Clear entries and free the mem it was using
\r */
\r void ClearEntries()
\r {
\r std::vector<DNSBLConfEntry *>::iterator i;
\r for (std::vector<DNSBLConfEntry *>::iterator i = DNSBLConfEntries.begin(); i != DNSBLConfEntries.end(); i++)
\r delete *i;
\r DNSBLConfEntries.clear();
\r }
\r\r /** Fill our conf vector with data
\r */
\r virtual void ReadConf()
\r {
\r ConfigReader *MyConf = new ConfigReader(ServerInstance);
\r ClearEntries();
\r\r for (int i=0; i< MyConf->Enumerate("dnsbl"); i++)
\r {
\r DNSBLConfEntry *e = new DNSBLConfEntry();
\r\r e->name = MyConf->ReadValue("dnsbl", "name", i);
\r e->reason = MyConf->ReadValue("dnsbl", "reason", i);
\r e->domain = MyConf->ReadValue("dnsbl", "domain", i);
\r e->banaction = str2banaction(MyConf->ReadValue("dnsbl", "action", i));
\r e->duration = ServerInstance->Duration(MyConf->ReadValue("dnsbl", "duration", i));
\r e->bitmask = MyConf->ReadInteger("dnsbl", "bitmask", i, false);
\r\r /* yeah, logic here is a little messy */
\r if (e->bitmask <= 0)
\r {
\r ServerInstance->WriteOpers("*** DNSBL(#%d): invalid bitmask",i);
\r }
\r else if (e->name.empty())
\r {
\r ServerInstance->WriteOpers("*** DNSBL(#%d): Invalid name",i);
\r }
\r else if (e->domain.empty())
\r {
\r ServerInstance->WriteOpers("*** DNSBL(#%d): Invalid domain",i);
\r }
\r else if (e->banaction == DNSBLConfEntry::I_UNKNOWN)
\r {
\r ServerInstance->WriteOpers("*** DNSBL(#%d): Invalid banaction", i);
\r }
\r else
\r {
\r if (e->reason.empty())
\r {
\r ServerInstance->WriteOpers("*** DNSBL(#%d): empty reason, using defaults",i);
\r e->reason = "Your IP has been blacklisted.";
\r }
\r\r /* add it, all is ok */
\r DNSBLConfEntries.push_back(e);
\r continue;
\r }
\r\r /* delete and drop it, error somewhere */
\r delete e;
\r }
\r\r delete MyConf;
\r }
\r\r virtual void OnRehash(userrec* user, const std::string ¶meter)
\r {
\r ReadConf();
\r }
\r\r virtual int OnUserRegister(userrec* user)
\r {
\r /* only do lookups on local users */
\r if (IS_LOCAL(user))
\r {
\r /* following code taken from bopm, reverses an IP address. */
\r struct in_addr in;
\r unsigned char a, b, c, d;
\r char reversedipbuf[128];
\r std::string reversedip;
\r bool success = false;
\r\r if (!inet_aton(user->GetIPString(), &in))
\r {
\r#ifdef IPV6
\r /* We could have an ipv6 address here */
\r std::string x = user->GetIPString();
\r /* Is it a 4in6 address? (Compensate for this kernel kludge that people love) */
\r if (x.find("0::ffff:") == 0)
\r {
\r x.erase(x.begin(), x.begin() + 8);
\r if (inet_aton(x.c_str(), &in))
\r success = true;
\r }
\r#endif
\r }
\r else
\r {
\r success = true;
\r }
\r\r if (!success)
\r return 0;
\r\r d = (unsigned char) (in.s_addr >> 24) & 0xFF;
\r c = (unsigned char) (in.s_addr >> 16) & 0xFF;
\r b = (unsigned char) (in.s_addr >> 8) & 0xFF;
\r a = (unsigned char) in.s_addr & 0xFF;
\r\r snprintf(reversedipbuf, 128, "%d.%d.%d.%d", d, c, b, a);
\r reversedip = std::string(reversedipbuf);
\r\r // For each DNSBL, we will run through this lookup
\r for (std::vector<DNSBLConfEntry *>::iterator i = DNSBLConfEntries.begin(); i != DNSBLConfEntries.end(); i++)
\r {
\r // Fill hostname with a dnsbl style host (d.c.b.a.domain.tld)
\r std::string hostname = reversedip + "." + (*i)->domain;
\r\r /* now we'd need to fire off lookups for `hostname'. */
\r bool cached;
\r DNSBLResolver *r = new DNSBLResolver(this, ServerInstance, hostname, user, user->GetFd(), *i, cached);
\r ServerInstance->AddResolver(r, cached);
\r }
\r }
\r\r /* don't do anything with this hot potato */
\r return 0;
\r }
\r \r virtual int OnStats(char symbol, userrec* user, string_list &results)
\r {
\r if (symbol != 'd')
\r return 0;
\r \r unsigned long total_hits = 0, total_misses = 0;
\r\r for (std::vector<DNSBLConfEntry*>::iterator i = DNSBLConfEntries.begin(); i != DNSBLConfEntries.end(); i++)
\r {
\r total_hits += (*i)->stats_hits;
\r total_misses += (*i)->stats_misses;
\r \r results.push_back(std::string(ServerInstance->Config->ServerName) + " 304 " + user->nick + " :DNSBLSTATS DNSbl \"" + (*i)->name + "\" had " +
\r ConvToStr((*i)->stats_hits) + " hits and " + ConvToStr((*i)->stats_misses) + " misses");
\r }
\r \r results.push_back(std::string(ServerInstance->Config->ServerName) + " 304 " + user->nick + " :DNSBLSTATS Total hits: " + ConvToStr(total_hits));
\r results.push_back(std::string(ServerInstance->Config->ServerName) + " 304 " + user->nick + " :DNSBLSTATS Total misses: " + ConvToStr(total_misses));
\r \r return 0;
\r }
\r};
\r\rMODULE_INIT(ModuleDNSBL)
\r