The InspIRCd Project
Home | Developers | Wiki | Forums | Bug Tracker | SVN | Download
Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members

m_dnsbl.cpp

Go to the documentation of this file.
00001 /*       +------------------------------------+
00002  *       | Inspire Internet Relay Chat Daemon |
00003  *       +------------------------------------+
00004  *
00005  *  InspIRCd: (C) 2002-2008 InspIRCd Development Team
00006  * See: http://www.inspircd.org/wiki/index.php/Credits
00007  *
00008  * This program is free but copyrighted software; see
00009  *            the file COPYING for details.
00010  *
00011  * ---------------------------------------------------
00012  */
00013 
00014 #include "inspircd.h"
00015 #include "xline.h"
00016 
00017 #ifndef WINDOWS
00018 #include <sys/types.h>
00019 #include <sys/socket.h>
00020 #include <netinet/in.h>
00021 #include <arpa/inet.h>
00022 #endif
00023 
00024 /* $ModDesc: Provides handling of DNS blacklists */
00025 
00026 /* Class holding data for a single entry */
00027 class DNSBLConfEntry : public classbase
00028 {
00029         public:
00030                 enum EnumBanaction { I_UNKNOWN, I_KILL, I_ZLINE, I_KLINE, I_GLINE };
00031                 enum EnumType { A_RECORD, A_BITMASK };
00032                 std::string name, domain, reason;
00033                 EnumBanaction banaction;
00034                 EnumType type;
00035                 long duration;
00036                 int bitmask;
00037                 unsigned char records[256];
00038                 unsigned long stats_hits, stats_misses;
00039                 DNSBLConfEntry(): type(A_BITMASK),duration(86400),bitmask(0),stats_hits(0), stats_misses(0) {}
00040                 ~DNSBLConfEntry() { }
00041 };
00042 
00043 
00046 class DNSBLResolver : public Resolver
00047 {
00048         int theirfd;
00049         User* them;
00050         DNSBLConfEntry *ConfEntry;
00051 
00052  public:
00053 
00054         DNSBLResolver(Module *me, InspIRCd *Instance, const std::string &hostname, User* u, int userfd, DNSBLConfEntry *conf, bool &cached)
00055                 : Resolver(Instance, hostname, DNS_QUERY_A, cached, me)
00056         {
00057                 theirfd = userfd;
00058                 them = u;
00059                 ConfEntry = conf;
00060         }
00061 
00062         /* Note: This may be called multiple times for multiple A record results */
00063         virtual void OnLookupComplete(const std::string &result, unsigned int ttl, bool cached, int resultnum = 0)
00064         {
00065                 /* for bitmask reply types, we arent interested in any but the first result (number 0) */
00066                 if ((ConfEntry->type == DNSBLConfEntry::A_BITMASK) && (resultnum))
00067                         return;
00068 
00069                 /* Check the user still exists */
00070                 if ((them) && (them == ServerInstance->SE->GetRef(theirfd)))
00071                 {
00072                         // Now we calculate the bitmask: 256*(256*(256*a+b)+c)+d
00073                         if(result.length())
00074                         {
00075                                 unsigned int bitmask = 0, record = 0;
00076                                 bool show = false, match = false;
00077                                 in_addr resultip;
00078 
00079                                 inet_aton(result.c_str(), &resultip);
00080 
00081                                 switch (ConfEntry->type)
00082                                 {
00083                                         case DNSBLConfEntry::A_BITMASK:
00084                                                 bitmask = resultip.s_addr >> 24; /* Last octet (network byte order) */
00085                                                 bitmask &= ConfEntry->bitmask;
00086                                                 match = (bitmask != 0);
00087                                         break;
00088                                         case DNSBLConfEntry::A_RECORD:
00089                                                 record = resultip.s_addr >> 24; /* Last octet */
00090                                                 match = (ConfEntry->records[record] == 1);
00091                                         break;
00092                                 }
00093 
00094                                 if (match)
00095                                 {
00096                                         std::string reason = ConfEntry->reason;
00097                                         std::string::size_type x = reason.find("%ip%");
00098                                         while (x != std::string::npos)
00099                                         {
00100                                                 reason.erase(x, 4);
00101                                                 reason.insert(x, them->GetIPString());
00102                                                 x = reason.find("%ip%");
00103                                         }
00104 
00105                                         ConfEntry->stats_hits++;
00106 
00107                                         switch (ConfEntry->banaction)
00108                                         {
00109                                                 case DNSBLConfEntry::I_KILL:
00110                                                 {
00111                                                         ServerInstance->Users->QuitUser(them, std::string("Killed (") + reason + ")");
00112                                                         break;
00113                                                 }
00114                                                 case DNSBLConfEntry::I_KLINE:
00115                                                 {
00116                                                         KLine* kl = new KLine(ServerInstance, ServerInstance->Time(), ConfEntry->duration, ServerInstance->Config->ServerName, reason.c_str(),
00117                                                                         "*", them->GetIPString());
00118                                                         if (ServerInstance->XLines->AddLine(kl,NULL))
00119                                                         {
00120                                                                 ServerInstance->XLines->ApplyLines();
00121                                                         }
00122                                                         else
00123                                                                 delete kl;
00124                                                         break;
00125                                                 }
00126                                                 case DNSBLConfEntry::I_GLINE:
00127                                                 {
00128                                                         GLine* gl = new GLine(ServerInstance, ServerInstance->Time(), ConfEntry->duration, ServerInstance->Config->ServerName, reason.c_str(),
00129                                                                         "*", them->GetIPString());
00130                                                         if (ServerInstance->XLines->AddLine(gl,NULL))
00131                                                         {
00132                                                                 ServerInstance->XLines->ApplyLines();
00133                                                         }
00134                                                         else
00135                                                                 delete gl;
00136                                                         break;
00137                                                 }
00138                                                 case DNSBLConfEntry::I_ZLINE:
00139                                                 {
00140                                                         ZLine* zl = new ZLine(ServerInstance, ServerInstance->Time(), ConfEntry->duration, ServerInstance->Config->ServerName, reason.c_str(),
00141                                                                         them->GetIPString());
00142                                                         if (ServerInstance->XLines->AddLine(zl,NULL))
00143                                                         {
00144                                                                 ServerInstance->XLines->ApplyLines();
00145                                                         }
00146                                                         else
00147                                                                 delete zl;
00148                                                         break;
00149                                                 }
00150                                                 case DNSBLConfEntry::I_UNKNOWN:
00151                                                 {
00152                                                         break;
00153                                                 }
00154                                                 break;
00155                                         }
00156 
00157                                         if (show)
00158                                         {
00159                                                 ServerInstance->SNO->WriteToSnoMask('A', "Connecting user %s detected as being on a DNS blacklist (%s) with result %d", them->GetFullRealHost().c_str(), ConfEntry->name.c_str(), bitmask);
00160                                         }
00161                                 }
00162                                 else
00163                                         ConfEntry->stats_misses++;
00164                         }
00165                         else
00166                                 ConfEntry->stats_misses++;
00167                 }
00168         }
00169 
00170         virtual void OnError(ResolverError e, const std::string &errormessage)
00171         {
00172         }
00173 
00174         virtual ~DNSBLResolver()
00175         {
00176         }
00177 };
00178 
00179 class ModuleDNSBL : public Module
00180 {
00181  private:
00182         std::vector<DNSBLConfEntry *> DNSBLConfEntries;
00183 
00184         /*
00185          *      Convert a string to EnumBanaction
00186          */
00187         DNSBLConfEntry::EnumBanaction str2banaction(const std::string &action)
00188         {
00189                 if(action.compare("KILL")==0)
00190                         return DNSBLConfEntry::I_KILL;
00191                 if(action.compare("KLINE")==0)
00192                         return DNSBLConfEntry::I_KLINE;
00193                 if(action.compare("ZLINE")==0)
00194                         return DNSBLConfEntry::I_ZLINE;
00195                 if(action.compare("GLINE")==0)
00196                         return DNSBLConfEntry::I_GLINE;
00197 
00198                 return DNSBLConfEntry::I_UNKNOWN;
00199         }
00200  public:
00201         ModuleDNSBL(InspIRCd *Me) : Module(Me)
00202         {
00203                 ReadConf();
00204                 Implementation eventlist[] = { I_OnRehash, I_OnUserRegister, I_OnStats };
00205                 ServerInstance->Modules->Attach(eventlist, this, 3);
00206         }
00207 
00208         virtual ~ModuleDNSBL()
00209         {
00210                 ClearEntries();
00211         }
00212 
00213         virtual Version GetVersion()
00214         {
00215                 return Version("$Id: m_dnsbl.cpp 10781 2008-11-01 20:20:12Z w00t $", VF_VENDOR, API_VERSION);
00216         }
00217 
00218 
00221         void ClearEntries()
00222         {
00223                 for (std::vector<DNSBLConfEntry *>::iterator i = DNSBLConfEntries.begin(); i != DNSBLConfEntries.end(); i++)
00224                         delete *i;
00225                 DNSBLConfEntries.clear();
00226         }
00227 
00230         virtual void ReadConf()
00231         {
00232                 ConfigReader *MyConf = new ConfigReader(ServerInstance);
00233                 ClearEntries();
00234 
00235                 for (int i=0; i< MyConf->Enumerate("dnsbl"); i++)
00236                 {
00237                         DNSBLConfEntry *e = new DNSBLConfEntry();
00238 
00239                         e->name = MyConf->ReadValue("dnsbl", "name", i);
00240                         e->reason = MyConf->ReadValue("dnsbl", "reason", i);
00241                         e->domain = MyConf->ReadValue("dnsbl", "domain", i);
00242 
00243                         if (MyConf->ReadValue("dnsbl", "type", i) == "bitmask")
00244                         {
00245                                 e->type = DNSBLConfEntry::A_BITMASK;
00246                                 e->bitmask = MyConf->ReadInteger("dnsbl", "bitmask", i, false);
00247                         }
00248                         else
00249                         {
00250                                 memset(e->records, 0, sizeof(e->records));
00251                                 e->type = DNSBLConfEntry::A_RECORD;
00252                                 irc::portparser portrange(MyConf->ReadValue("dnsbl", "records", i), false);
00253                                 long item = -1;
00254                                 while ((item = portrange.GetToken()))
00255                                         e->records[item] = 1;
00256                         }
00257 
00258                         e->banaction = str2banaction(MyConf->ReadValue("dnsbl", "action", i));
00259                         e->duration = ServerInstance->Duration(MyConf->ReadValue("dnsbl", "duration", i));
00260 
00261                         /* Use portparser for record replies */
00262 
00263                         /* yeah, logic here is a little messy */
00264                         if ((e->bitmask <= 0) && (DNSBLConfEntry::A_BITMASK == e->type))
00265                         {
00266                                 ServerInstance->SNO->WriteToSnoMask('A', "DNSBL(#%d): invalid bitmask",i);
00267                         }
00268                         else if (e->name.empty())
00269                         {
00270                                 ServerInstance->SNO->WriteToSnoMask('A', "DNSBL(#%d): Invalid name",i);
00271                         }
00272                         else if (e->domain.empty())
00273                         {
00274                                 ServerInstance->SNO->WriteToSnoMask('A', "DNSBL(#%d): Invalid domain",i);
00275                         }
00276                         else if (e->banaction == DNSBLConfEntry::I_UNKNOWN)
00277                         {
00278                                 ServerInstance->SNO->WriteToSnoMask('A', "DNSBL(#%d): Invalid banaction", i);
00279                         }
00280                         else
00281                         {
00282                                 if (e->reason.empty())
00283                                 {
00284                                         ServerInstance->SNO->WriteToSnoMask('A', "DNSBL(#%d): empty reason, using defaults",i);
00285                                         e->reason = "Your IP has been blacklisted.";
00286                                 }
00287 
00288                                 /* add it, all is ok */
00289                                 DNSBLConfEntries.push_back(e);
00290                                 continue;
00291                         }
00292 
00293                         /* delete and drop it, error somewhere */
00294                         delete e;
00295                 }
00296 
00297                 delete MyConf;
00298         }
00299 
00300         virtual void OnRehash(User* user, const std::string &parameter)
00301         {
00302                 ReadConf();
00303         }
00304 
00305         virtual int OnUserRegister(User* user)
00306         {
00307                 /* only do lookups on local users */
00308                 if (IS_LOCAL(user))
00309                 {
00310                         /* following code taken from bopm, reverses an IP address. */
00311                         struct in_addr in;
00312                         unsigned char a, b, c, d;
00313                         char reversedipbuf[128];
00314                         std::string reversedip;
00315                         bool success;
00316 
00317                         success = inet_aton(user->GetIPString(), &in);
00318 
00319                         if (!success)
00320                                 return 0;
00321 
00322                         d = (unsigned char) (in.s_addr >> 24) & 0xFF;
00323                         c = (unsigned char) (in.s_addr >> 16) & 0xFF;
00324                         b = (unsigned char) (in.s_addr >> 8) & 0xFF;
00325                         a = (unsigned char) in.s_addr & 0xFF;
00326 
00327                         snprintf(reversedipbuf, 128, "%d.%d.%d.%d", d, c, b, a);
00328                         reversedip = std::string(reversedipbuf);
00329 
00330                         // For each DNSBL, we will run through this lookup
00331                         for (std::vector<DNSBLConfEntry *>::iterator i = DNSBLConfEntries.begin(); i != DNSBLConfEntries.end(); i++)
00332                         {
00333                                 // Fill hostname with a dnsbl style host (d.c.b.a.domain.tld)
00334                                 std::string hostname = reversedip + "." + (*i)->domain;
00335 
00336                                 /* now we'd need to fire off lookups for `hostname'. */
00337                                 bool cached;
00338                                 DNSBLResolver *r = new DNSBLResolver(this, ServerInstance, hostname, user, user->GetFd(), *i, cached);
00339                                 ServerInstance->AddResolver(r, cached);
00340                         }
00341                 }
00342 
00343                 /* don't do anything with this hot potato */
00344                 return 0;
00345         }
00346 
00347         virtual int OnStats(char symbol, User* user, string_list &results)
00348         {
00349                 if (symbol != 'd')
00350                         return 0;
00351 
00352                 unsigned long total_hits = 0, total_misses = 0;
00353 
00354                 for (std::vector<DNSBLConfEntry*>::iterator i = DNSBLConfEntries.begin(); i != DNSBLConfEntries.end(); i++)
00355                 {
00356                         total_hits += (*i)->stats_hits;
00357                         total_misses += (*i)->stats_misses;
00358 
00359                         results.push_back(std::string(ServerInstance->Config->ServerName) + " 304 " + user->nick + " :DNSBLSTATS DNSbl \"" + (*i)->name + "\" had " +
00360                                         ConvToStr((*i)->stats_hits) + " hits and " + ConvToStr((*i)->stats_misses) + " misses");
00361                 }
00362 
00363                 results.push_back(std::string(ServerInstance->Config->ServerName) + " 304 " + user->nick + " :DNSBLSTATS Total hits: " + ConvToStr(total_hits));
00364                 results.push_back(std::string(ServerInstance->Config->ServerName) + " 304 " + user->nick + " :DNSBLSTATS Total misses: " + ConvToStr(total_misses));
00365 
00366                 return 0;
00367         }
00368 };
00369 
00370 MODULE_INIT(ModuleDNSBL)