]> git.netwichtig.de Git - user/henk/code/inspircd.git/commitdiff
Update m_cloaking to use free-form keys instead of weakening the hash IV
authordanieldg <danieldg@e03df62e-2008-0410-955e-edbf42e46eb7>
Sat, 10 Oct 2009 17:06:52 +0000 (17:06 +0000)
committerdanieldg <danieldg@e03df62e-2008-0410-955e-edbf42e46eb7>
Sat, 10 Oct 2009 17:06:52 +0000 (17:06 +0000)
git-svn-id: http://svn.inspircd.org/repository/trunk/inspircd@11820 e03df62e-2008-0410-955e-edbf42e46eb7

14 files changed:
include/inspstring.h
include/u_listmode.h [deleted file]
make/calcdep.pl
src/inspstring.cpp
src/modules/m_cloaking.cpp
src/modules/m_hash.h
src/modules/m_md5.cpp
src/modules/m_password_hash.cpp
src/modules/m_ripemd160.cpp
src/modules/m_sha256.cpp
src/modules/m_spanningtree/hmac.cpp
src/modules/m_sqlauth.cpp
src/modules/m_sqloper.cpp
src/modules/u_listmode.h [new file with mode: 0644]

index 4a7fa627144fa36f0db090e637fe393f09d396c1..abf8d9cf36e46813b0974334af0e3a3bae32a0ca 100644 (file)
@@ -39,5 +39,8 @@ CoreExport int charlcat(char* x,char y,int z);
  */
 CoreExport bool charremove(char* mp, char remove);
 
+/** Binary to hexadecimal conversion */
+std::string BinToHex(const std::string& data);
+
 #endif
 
diff --git a/include/u_listmode.h b/include/u_listmode.h
deleted file mode 100644 (file)
index 981e2a3..0000000
+++ /dev/null
@@ -1,414 +0,0 @@
-/*       +------------------------------------+
- *       | Inspire Internet Relay Chat Daemon |
- *       +------------------------------------+
- *
- *  InspIRCd: (C) 2002-2009 InspIRCd Development Team
- * See: http://wiki.inspircd.org/Credits
- *
- * This program is free but copyrighted software; see
- *         the file COPYING for details.
- *
- * ---------------------------------------------------
- */
-
-#ifndef INSPIRCD_LISTMODE_PROVIDER
-#define INSPIRCD_LISTMODE_PROVIDER
-
-/** Get the time as a string
- */
-inline std::string stringtime()
-{
-       std::ostringstream TIME;
-       TIME << ServerInstance->Time();
-       return TIME.str();
-}
-
-/** An item in a listmode's list
- */
-class ListItem : public classbase
-{
-public:
-       std::string nick;
-       std::string mask;
-       std::string time;
-};
-
-/** The number of items a listmode's list may contain
- */
-class ListLimit : public classbase
-{
-public:
-       std::string mask;
-       unsigned int limit;
-};
-
-/** Items stored in the channel's list
- */
-typedef std::list<ListItem> modelist;
-/** Max items per channel by name
- */
-typedef std::list<ListLimit> limitlist;
-
-/** The base class for list modes, should be inherited.
- */
-class ListModeBase : public ModeHandler
-{
- protected:
-       /** Numeric to use when outputting the list
-        */
-       unsigned int listnumeric;
-       /** Numeric to indicate end of list
-        */
-       unsigned int endoflistnumeric;
-       /** String to send for end of list
-        */
-       std::string endofliststring;
-       /** Automatically tidy up entries
-        */
-       bool tidy;
-       /** Config tag to check for max items per channel
-        */
-       std::string configtag;
-       /** Limits on a per-channel basis read from the tag
-        * specified in ListModeBase::configtag
-        */
-       limitlist chanlimits;
-
- public:
-       /** Storage key
-        */
-       SimpleExtItem<modelist> extItem;
-
-       /** Constructor.
-        * @param Instance The creator of this class
-        * @param modechar Mode character
-        * @param eolstr End of list string
-        * @pram lnum List numeric
-        * @param eolnum End of list numeric
-        * @param autotidy Automatically tidy list entries on add
-        * @param ctag Configuration tag to get limits from
-        */
-       ListModeBase(Module* Creator, const std::string& Name, char modechar, const std::string &eolstr, unsigned int lnum, unsigned int eolnum, bool autotidy, const std::string &ctag = "banlist")
-               : ModeHandler(Creator, Name, modechar, PARAM_ALWAYS, MODETYPE_CHANNEL), 
-               listnumeric(lnum), endoflistnumeric(eolnum), endofliststring(eolstr), tidy(autotidy),
-               configtag(ctag), extItem("listbase_mode_" + name + "_list", Creator)
-       {
-               list = true;
-               this->DoRehash();
-               Extensible::Register(&extItem);
-       }
-
-       /** See mode.h
-        */
-       std::pair<bool,std::string> ModeSet(User*, User*, Channel* channel, const std::string &parameter)
-       {
-               modelist* el = extItem.get(channel);
-               if (el)
-               {
-                       for (modelist::iterator it = el->begin(); it != el->end(); it++)
-                       {
-                               if(parameter == it->mask)
-                               {
-                                       return std::make_pair(true, parameter);
-                               }
-                       }
-               }
-               return std::make_pair(false, parameter);
-       }
-
-       /** Display the list for this mode
-        * @param user The user to send the list to
-        * @param channel The channel the user is requesting the list for
-        */
-       virtual void DisplayList(User* user, Channel* channel)
-       {
-               modelist* el = extItem.get(channel);
-               if (el)
-               {
-                       for (modelist::reverse_iterator it = el->rbegin(); it != el->rend(); ++it)
-                       {
-                               user->WriteNumeric(listnumeric, "%s %s %s %s %s", user->nick.c_str(), channel->name.c_str(), it->mask.c_str(), (it->nick.length() ? it->nick.c_str() : ServerInstance->Config->ServerName.c_str()), it->time.c_str());
-                       }
-               }
-               user->WriteNumeric(endoflistnumeric, "%s %s :%s", user->nick.c_str(), channel->name.c_str(), endofliststring.c_str());
-       }
-
-       virtual void DisplayEmptyList(User* user, Channel* channel)
-       {
-               user->WriteNumeric(endoflistnumeric, "%s %s :%s", user->nick.c_str(), channel->name.c_str(), endofliststring.c_str());
-       }
-
-       /** Remove all instances of the mode from a channel.
-        * See mode.h
-        * @param channel The channel to remove all instances of the mode from
-        */
-       virtual void RemoveMode(Channel* channel, irc::modestacker* stack)
-       {
-               modelist* el = extItem.get(channel);
-               if (el)
-               {
-                       irc::modestacker modestack(false);
-
-                       for (modelist::iterator it = el->begin(); it != el->end(); it++)
-                       {
-                               if (stack)
-                                       stack->Push(this->GetModeChar(), it->mask);
-                               else
-                                       modestack.Push(this->GetModeChar(), it->mask);
-                       }
-
-                       if (stack)
-                               return;
-
-                       std::vector<std::string> stackresult;
-                       stackresult.push_back(channel->name);
-                       while (modestack.GetStackedLine(stackresult))
-                       {
-                               ServerInstance->SendMode(stackresult, ServerInstance->FakeClient);
-                               stackresult.clear();
-                               stackresult.push_back(channel->name);
-                       }
-               }
-       }
-
-       /** See mode.h
-        */
-       virtual void RemoveMode(User*, irc::modestacker* stack)
-       {
-               /* Listmodes dont get set on users */
-       }
-
-       /** Perform a rehash of this mode's configuration data
-        */
-       virtual void DoRehash()
-       {
-               ConfigReader Conf;
-
-               chanlimits.clear();
-
-               for (int i = 0; i < Conf.Enumerate(configtag); i++)
-               {
-                       // For each <banlist> tag
-                       ListLimit limit;
-                       limit.mask = Conf.ReadValue(configtag, "chan", i);
-                       limit.limit = Conf.ReadInteger(configtag, "limit", i, true);
-
-                       if (limit.mask.size() && limit.limit > 0)
-                               chanlimits.push_back(limit);
-               }
-               if (chanlimits.size() == 0)
-               {
-                       ListLimit limit;
-                       limit.mask = "*";
-                       limit.limit = 64;
-                       chanlimits.push_back(limit);
-               }
-       }
-
-       /** Populate the Implements list with the correct events for a List Mode
-        */
-       virtual void DoImplements(Module* m)
-       {
-               Implementation eventlist[] = { I_OnChannelDelete, I_OnSyncChannel, I_OnRehash };
-               ServerInstance->Modules->Attach(eventlist, m, 3);
-       }
-
-       /** Handle the list mode.
-        * See mode.h
-        */
-       virtual ModeAction OnModeChange(User* source, User*, Channel* channel, std::string &parameter, bool adding)
-       {
-               // Try and grab the list
-               modelist* el = extItem.get(channel);
-
-               if (adding)
-               {
-                       // If there was no list
-                       if (!el)
-                       {
-                               // Make one
-                               el = new modelist;
-                               extItem.set(channel, el);
-                       }
-
-                       // Clean the mask up
-                       if (this->tidy)
-                               ModeParser::CleanMask(parameter);
-
-                       // Check if the item already exists in the list
-                       for (modelist::iterator it = el->begin(); it != el->end(); it++)
-                       {
-                               if (parameter == it->mask)
-                               {
-                                       /* Give a subclass a chance to error about this */
-                                       TellAlreadyOnList(source, channel, parameter);
-
-                                       // it does, deny the change
-                                       return MODEACTION_DENY;
-                               }
-                       }
-
-                       unsigned int maxsize = 0;
-
-                       for (limitlist::iterator it = chanlimits.begin(); it != chanlimits.end(); it++)
-                       {
-                               if (InspIRCd::Match(channel->name, it->mask))
-                               {
-                                       // We have a pattern matching the channel...
-                                       maxsize = el->size();
-                                       if (IS_LOCAL(source) || (maxsize < it->limit))
-                                       {
-                                               /* Ok, it *could* be allowed, now give someone subclassing us
-                                                * a chance to validate the parameter.
-                                                * The param is passed by reference, so they can both modify it
-                                                * and tell us if we allow it or not.
-                                                *
-                                                * eg, the subclass could:
-                                                * 1) allow
-                                                * 2) 'fix' parameter and then allow
-                                                * 3) deny
-                                                */
-                                               if (ValidateParam(source, channel, parameter))
-                                               {
-                                                       // And now add the mask onto the list...
-                                                       ListItem e;
-                                                       e.mask = parameter;
-                                                       e.nick = source->nick;
-                                                       e.time = stringtime();
-
-                                                       el->push_back(e);
-                                                       return MODEACTION_ALLOW;
-                                               }
-                                               else
-                                               {
-                                                       /* If they deny it they have the job of giving an error message */
-                                                       return MODEACTION_DENY;
-                                               }
-                                       }
-                               }
-                       }
-
-                       /* List is full, give subclass a chance to send a custom message */
-                       if (!TellListTooLong(source, channel, parameter))
-                       {
-                               source->WriteNumeric(478, "%s %s %s :Channel ban/ignore list is full", source->nick.c_str(), channel->name.c_str(), parameter.c_str());
-                       }
-
-                       parameter = "";
-                       return MODEACTION_DENY;
-               }
-               else
-               {
-                       // We're taking the mode off
-                       if (el)
-                       {
-                               for (modelist::iterator it = el->begin(); it != el->end(); it++)
-                               {
-                                       if (parameter == it->mask)
-                                       {
-                                               el->erase(it);
-                                               if (el->size() == 0)
-                                               {
-                                                       extItem.unset(channel);
-                                               }
-                                               return MODEACTION_ALLOW;
-                                       }
-                               }
-                               /* Tried to remove something that wasn't set */
-                               TellNotSet(source, channel, parameter);
-                               parameter = "";
-                               return MODEACTION_DENY;
-                       }
-                       else
-                       {
-                               /* Hmm, taking an exception off a non-existant list, DIE */
-                               TellNotSet(source, channel, parameter);
-                               parameter = "";
-                               return MODEACTION_DENY;
-                       }
-               }
-               return MODEACTION_DENY;
-       }
-
-       /** Syncronize channel item list with another server.
-        * See modules.h
-        * @param chan Channel to syncronize
-        * @param proto Protocol module pointer
-        * @param opaque Opaque connection handle
-        */
-       virtual void DoSyncChannel(Channel* chan, Module* proto, void* opaque)
-       {
-               modelist* mlist = extItem.get(chan);
-               irc::modestacker modestack(true);
-               std::vector<std::string> stackresult;
-               std::vector<TranslateType> types;
-               types.push_back(TR_TEXT);
-               if (mlist)
-               {
-                       for (modelist::iterator it = mlist->begin(); it != mlist->end(); it++)
-                       {
-                               modestack.Push(std::string(1, mode)[0], it->mask);
-                       }
-               }
-               while (modestack.GetStackedLine(stackresult))
-               {
-                       types.assign(stackresult.size(), this->GetTranslateType());
-                       proto->ProtoSendMode(opaque, TYPE_CHANNEL, chan, stackresult, types);
-                       stackresult.clear();
-               }
-       }
-
-       /** Clean up module on unload
-        * @param target_type Type of target to clean
-        * @param item Item to clean
-        */
-       virtual void DoCleanup(int, void*)
-       {
-       }
-
-       /** Validate parameters.
-        * Overridden by implementing module.
-        * @param source Source user adding the parameter
-        * @param channel Channel the parameter is being added to
-        * @param parameter The actual parameter being added
-        * @return true if the parameter is valid
-        */
-       virtual bool ValidateParam(User*, Channel*, std::string&)
-       {
-               return true;
-       }
-
-       /** Tell the user the list is too long.
-        * Overridden by implementing module.
-        * @param source Source user adding the parameter
-        * @param channel Channel the parameter is being added to
-        * @param parameter The actual parameter being added
-        * @return Ignored
-        */
-       virtual bool TellListTooLong(User*, Channel*, std::string&)
-       {
-               return false;
-       }
-
-       /** Tell the user an item is already on the list.
-        * Overridden by implementing module.
-        * @param source Source user adding the parameter
-        * @param channel Channel the parameter is being added to
-        * @param parameter The actual parameter being added
-        */
-       virtual void TellAlreadyOnList(User*, Channel*, std::string&)
-       {
-       }
-
-       /** Tell the user that the parameter is not in the list.
-        * Overridden by implementing module.
-        * @param source Source user removing the parameter
-        * @param channel Channel the parameter is being removed from
-        * @param parameter The actual parameter being removed
-        */
-       virtual void TellNotSet(User*, Channel*, std::string&)
-       {
-       }
-};
-
-#endif
index 890d8d2658e250b6b8330f082781bf01779761bc..2a0ea5d92a7390112b8935810c2c481abf0bb1cd 100755 (executable)
@@ -33,22 +33,21 @@ sub run() {
 # Autogenerated by calcdep
 VPATH = \$(SOURCEPATH)/src
 
-all: bin/inspircd modules
+all: inspircd commands modules
 
 END
-       my @core_deps;
+       my(@core_deps, @cmdlist, @modlist);
        for my $file (<*.cpp>, <modes/*.cpp>, <socketengines/*.cpp>, "threadengines/threadengine_pthread.cpp") {
                my $out = find_output $file;
                dep_cpp $file, $out;
                next if $file =~ m#^socketengines/# && $file ne "socketengines/$ENV{SOCKETENGINE}.cpp";
                push @core_deps, $out;
        }
-       
-       my @modlist;
+
        for my $file (<commands/*.cpp>) {
                my $out = find_output $file;
                dep_cpp $file, $out;
-               push @modlist, $out;
+               push @cmdlist, $out;
        }
 
        opendir my $moddir, 'modules';
@@ -72,6 +71,7 @@ END
        }
        
        my $core_mk = join ' ', @core_deps;
+       my $cmds = join ' ', @cmdlist;
        my $mods = join ' ', @modlist;
        print MAKE <<END;
 
@@ -79,9 +79,12 @@ bin/inspircd: $core_mk
        \$(RUNCC) -o \$\@ \$(CORELDFLAGS) \$(LDLIBS) \$^ \$>
 
 inspircd: bin/inspircd
+
+commands: $cmds
+
 modules: $mods
 
-.PHONY: inspircd modules
+.PHONY: inspircd commands modules
 
 END
 }
index f11ea1248b218fe3a4157957878bbad12fcf2496..ff9c1875f2e6809080a8bf9f68c396c203b825ed 100644 (file)
@@ -136,3 +136,19 @@ CoreExport bool charremove(char* mp, char remove)
 
        return shift_down;
 }
+
+static const char hextable[] = "0123456789abcdef";
+
+std::string BinToHex(const std::string& data)
+{
+       int l = data.length();
+       std::string rv;
+       rv.reserve(l * 2);
+       for(int i=0; i < l; i++)
+       {
+               unsigned char c = data[i];
+               rv.append(1, hextable[c >> 4]);
+               rv.append(1, hextable[c & 0xF]);
+       }
+       return rv;
+}
index 8f650d7a27629c970d8dfa28eb8f8c5c947024b2..5a0fb910297b88fb5f0c18e13625ff0ea7753779 100644 (file)
 #include "m_hash.h"
 
 /* $ModDesc: Provides masking of user hostnames */
-/* $ModDep: m_hash.h */
+
+enum CloakMode
+{
+       /** 1.2-compatible host-based cloak */
+       MODE_COMPAT_HOST,
+       /** 1.2-compatible IP-only cloak */
+       MODE_COMPAT_IPONLY,
+       /** 2.0 cloak of "half" of the hostname plus the full IP hash */
+       MODE_HALF_CLOAK,
+       /** 2.0 cloak of IP hash, split at 2 common CIDR range points */
+       MODE_OPAQUE
+};
+
+// lowercase-only encoding similar to base64, used for hash output
+static const char base32[] = "0123456789abcdefghijklmnopqrstuv";
 
 /** Handles user mode +x
  */
 class CloakUser : public ModeHandler
 {
  public:
-       std::string prefix;
-       unsigned int key1;
-       unsigned int key2;
-       unsigned int key3;
-       unsigned int key4;
-       bool ipalways;
-       Module* HashProvider;
-       const char *xtab[4];
        LocalStringExt ext;
 
-       /** This function takes a domain name string and returns just the last two domain parts,
-        * or the last domain part if only two are available. Failing that it just returns what it was given.
-        *
-        * For example, if it is passed "svn.inspircd.org" it will return ".inspircd.org".
-        * If it is passed "brainbox.winbot.co.uk" it will return ".co.uk",
-        * and if it is passed "localhost.localdomain" it will return ".localdomain".
-        *
-        * This is used to ensure a significant part of the host is always cloaked (see Bug #216)
-        */
-       std::string LastTwoDomainParts(const std::string &host)
-       {
-               int dots = 0;
-               std::string::size_type splitdot = host.length();
-
-               for (std::string::size_type x = host.length() - 1; x; --x)
-               {
-                       if (host[x] == '.')
-                       {
-                               splitdot = x;
-                               dots++;
-                       }
-                       if (dots >= 3)
-                               break;
-               }
-
-               if (splitdot == host.length())
-                       return host;
-               else
-                       return host.substr(splitdot);
-       }
-
-       CloakUser(Module* source, Module* Hash)
-               : ModeHandler(source, "cloak", 'x', PARAM_NONE, MODETYPE_USER), HashProvider(Hash),
+       CloakUser(Module* source)
+               : ModeHandler(source, "cloak", 'x', PARAM_NONE, MODETYPE_USER),
                ext("cloaked_host", source)
        {
        }
 
        ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
        {
-               /* For remote clients, we dont take any action, we just allow it.
+               /* For remote clients, we don't take any action, we just allow it.
                 * The local server where they are will set their cloak instead.
-                * This is fine, as we will recieve it later.
+                * This is fine, as we will receive it later.
                 */
                if (!IS_LOCAL(dest))
                {
@@ -129,9 +104,76 @@ class CloakUser : public ModeHandler
                return MODEACTION_DENY;
        }
 
-       std::string Cloak4(const char* ip)
+};
+
+
+class ModuleCloaking : public Module
+{
+ private:
+       CloakUser cu;
+       CloakMode mode;
+       std::string prefix;
+       std::string key;
+       unsigned int compatkey[4];
+       const char* xtab[4];
+       Module* HashProvider;
+
+ public:
+       ModuleCloaking() : cu(this)
+       {
+               /* Attempt to locate the md5 service provider, bail if we can't find it */
+               HashProvider = ServerInstance->Modules->Find("m_md5.so");
+               if (!HashProvider)
+                       throw ModuleException("Can't find m_md5.so. Please load m_md5.so before m_cloaking.so.");
+
+               OnRehash(NULL);
+
+               /* Register it with the core */
+               if (!ServerInstance->Modes->AddMode(&cu))
+                       throw ModuleException("Could not add new modes!");
+
+               ServerInstance->Modules->UseInterface("HashRequest");
+               Extensible::Register(&cu.ext);
+
+               Implementation eventlist[] = { I_OnRehash, I_OnCheckBan, I_OnUserConnect };
+               ServerInstance->Modules->Attach(eventlist, this, 3);
+
+               CloakExistingUsers();
+       }
+
+       /** This function takes a domain name string and returns just the last two domain parts,
+        * or the last domain part if only two are available. Failing that it just returns what it was given.
+        *
+        * For example, if it is passed "svn.inspircd.org" it will return ".inspircd.org".
+        * If it is passed "brainbox.winbot.co.uk" it will return ".co.uk",
+        * and if it is passed "localhost.localdomain" it will return ".localdomain".
+        *
+        * This is used to ensure a significant part of the host is always cloaked (see Bug #216)
+        */
+       std::string LastTwoDomainParts(const std::string &host)
+       {
+               int dots = 0;
+               std::string::size_type splitdot = host.length();
+
+               for (std::string::size_type x = host.length() - 1; x; --x)
+               {
+                       if (host[x] == '.')
+                       {
+                               splitdot = x;
+                               dots++;
+                       }
+                       if (dots >= 3)
+                               break;
+               }
+
+               if (splitdot == host.length())
+                       return host;
+               else
+                       return host.substr(splitdot);
+       }
+
+       std::string CompatCloak4(const char* ip)
        {
-               unsigned int iv[] = { key1, key2, key3, key4 };
                irc::sepstream seps(ip, '.');
                std::string octet[4];
                int i[4];
@@ -153,7 +195,7 @@ class CloakUser : public ModeHandler
                /* Send the Hash module a different hex table for each octet group's Hash sum */
                for (int k = 0; k < 4; k++)
                {
-                       HashRequestIV hash(creator, HashProvider, iv, xtab[(iv[k]+i[k]) % 4], octet[k]);
+                       HashRequestIV hash(this, HashProvider, compatkey, xtab[(compatkey[k]+i[k]) % 4], octet[k]);
                        rv.append(hash.result.substr(0,6));
                        if (k < 3)
                                rv.append(".");
@@ -162,9 +204,8 @@ class CloakUser : public ModeHandler
                return rv;
        }
 
-       std::string Cloak6(const char* ip)
+       std::string CompatCloak6(const char* ip)
        {
-               unsigned int iv[] = { key1, key2, key3, key4 };
                std::vector<std::string> hashies;
                std::string item;
                int rounds = 0;
@@ -176,7 +217,7 @@ class CloakUser : public ModeHandler
                        item += *input;
                        if (item.length() > 7)
                        {
-                               HashRequestIV hash(creator, HashProvider, iv, xtab[(key1+rounds) % 4], item);
+                               HashRequestIV hash(this, HashProvider, compatkey, xtab[(compatkey[1]+rounds) % 4], item);
                                hashies.push_back(hash.result.substr(0,8));
                                item.clear();
                        }
@@ -184,111 +225,81 @@ class CloakUser : public ModeHandler
                }
                if (!item.empty())
                {
-                       HashRequestIV hash(creator, HashProvider, iv, xtab[(key1+rounds) % 4], item);
+                       HashRequestIV hash(this, HashProvider, compatkey, xtab[(compatkey[1]+rounds) % 4], item);
                        hashies.push_back(hash.result.substr(0,8));
                }
                /* Stick them all together */
                return irc::stringjoiner(":", hashies, 0, hashies.size() - 1).GetJoined();
        }
 
-       void DoRehash()
+       std::string ReversePartialIP(const irc::sockets::sockaddrs& ip)
        {
-               ConfigReader Conf;
-               bool lowercase;
-
-               /* These are *not* using the need_positive parameter of ReadInteger -
-                * that will limit the valid values to only the positive values in a
-                * signed int. Instead, accept any value that fits into an int and
-                * cast it to an unsigned int. That will, a bit oddly, give us the full
-                * spectrum of an unsigned integer. - Special
-                *
-                * We must limit the keys or else we get different results on
-                * amd64/x86 boxes. - psychon */
-               const unsigned int limit = 0x80000000;
-               key1 = key2 = key3 = key4 = 0;
-               key1 = (unsigned int) Conf.ReadInteger("cloak","key1",0,false);
-               key2 = (unsigned int) Conf.ReadInteger("cloak","key2",0,false);
-               key3 = (unsigned int) Conf.ReadInteger("cloak","key3",0,false);
-               key4 = (unsigned int) Conf.ReadInteger("cloak","key4",0,false);
-               prefix = Conf.ReadValue("cloak","prefix",0);
-               ipalways = Conf.ReadFlag("cloak", "ipalways", 0);
-               lowercase = Conf.ReadFlag("cloak", "lowercase", 0);
-
-               if (!lowercase)
+               char rv[50];
+               if (ip.sa.sa_family == AF_INET6)
                {
-                       xtab[0] = "F92E45D871BCA630";
-                       xtab[1] = "A1B9D80C72E653F4";
-                       xtab[2] = "1ABC078934DEF562";
-                       xtab[3] = "ABCDEF5678901234";
+                       snprintf(rv, 50, "%02x%02x.%02x%02x.%02x%02x.IP",
+                               ip.in6.sin6_addr.s6_addr[4], ip.in6.sin6_addr.s6_addr[5],
+                               ip.in6.sin6_addr.s6_addr[2], ip.in6.sin6_addr.s6_addr[3],
+                               ip.in6.sin6_addr.s6_addr[0], ip.in6.sin6_addr.s6_addr[1]);
                }
                else
                {
-                       xtab[0] = "f92e45d871bca630";
-                       xtab[1] = "a1b9d80c72e653f4";
-                       xtab[2] = "1abc078934def562";
-                       xtab[3] = "abcdef5678901234";
-               }
-
-               if (prefix.empty())
-                       prefix = ServerInstance->Config->Network;
-
-               if (!key1 || !key2 || !key3 || !key4 || key1 >= limit || key2 >= limit || key3 >= limit || key4 >= limit)
-               {
-                       std::string detail;
-                       if (!key1 || key1 >= limit)
-                               detail = "<cloak:key1> is not valid, it may be set to a too high/low value, or it may not exist.";
-                       else if (!key2 || key2 >= limit)
-                               detail = "<cloak:key2> is not valid, it may be set to a too high/low value, or it may not exist.";
-                       else if (!key3 || key3 >= limit)
-                               detail = "<cloak:key3> is not valid, it may be set to a too high/low value, or it may not exist.";
-                       else if (!key4 || key4 >= limit)
-                               detail = "<cloak:key4> is not valid, it may be set to a too high/low value, or it may not exist.";
-
-                       throw ModuleException("You have not defined cloak keys for m_cloaking!!! THIS IS INSECURE AND SHOULD BE CHECKED! - " + detail);
+                       const char* ip4 = (const char*)&ip.in4.sin_addr;
+                       snprintf(rv, 50, "%d.%d.IP", ip4[1], ip4[0]);
                }
+               return rv;
        }
-};
 
-
-class ModuleCloaking : public Module
-{
- private:
-       CloakUser* cu;
-
- public:
-       ModuleCloaking()
+       std::string SegmentIP(const irc::sockets::sockaddrs& ip)
        {
-               /* Attempt to locate the md5 service provider, bail if we can't find it */
-               Module* HashModule = ServerInstance->Modules->Find("m_md5.so");
-               if (!HashModule)
-                       throw ModuleException("Can't find m_md5.so. Please load m_md5.so before m_cloaking.so.");
-
-               cu = new CloakUser(this, HashModule);
-
-               try
+               std::string bindata;
+               int hop1, hop2;
+               if (ip.sa.sa_family == AF_INET6)
                {
-                       OnRehash(NULL);
+                       bindata = std::string((const char*)ip.in6.sin6_addr.s6_addr, 16);
+                       hop1 = 8;
+                       hop2 = 6;
                }
-               catch (ModuleException &e)
+               else
                {
-                       delete cu;
-                       throw e;
+                       bindata = std::string((const char*)&ip.in4.sin_addr, 4);
+                       hop1 = 3;
+                       hop2 = 2;
                }
 
-               /* Register it with the core */
-               if (!ServerInstance->Modes->AddMode(cu))
+               std::string rv;
+               rv.reserve(prefix.length() + 30);
+               rv.append(prefix);
+               rv.append(SegmentCloak(bindata, 2));
+               rv.append(1, '.');
+               bindata.erase(hop1);
+               rv.append(SegmentCloak(bindata, 3));
+               rv.append(1, '.');
+               bindata.erase(hop2);
+               rv.append(SegmentCloak(bindata, 4));
+               rv.append(".IP");
+               return rv;
+       }
+
+       std::string SegmentCloak(const std::string& item, char id)
+       {
+               std::string input;
+               input.reserve(key.length() + 3 + item.length());
+               input.append(1, id);
+               input.append(key);
+               input.append(1, 0); // null does not terminate a C++ string
+               input.append(item);
+
+               HashRequest hash(this, HashProvider, input);
+               std::string rv = hash.binresult.substr(0,6);
+               for(int i=0; i < 6; i++)
                {
-                       delete cu;
-                       throw ModuleException("Could not add new modes!");
+                       // this discards 3 bits per byte. We have an
+                       // overabundance of bits in the hash output, doesn't
+                       // matter which ones we are discarding.
+                       rv[i] = base32[rv[i] & 0x1F];
                }
-
-               ServerInstance->Modules->UseInterface("HashRequest");
-               Extensible::Register(&cu->ext);
-
-               Implementation eventlist[] = { I_OnRehash, I_OnCheckBan, I_OnUserConnect };
-               ServerInstance->Modules->Attach(eventlist, this, 3);
-
-               CloakExistingUsers();
+               return rv;
        }
 
        void CloakExistingUsers()
@@ -296,7 +307,7 @@ class ModuleCloaking : public Module
                std::string* cloak;
                for (std::vector<User*>::iterator u = ServerInstance->Users->local_users.begin(); u != ServerInstance->Users->local_users.end(); u++)
                {
-                       cloak = cu->ext.get(*u);
+                       cloak = cu.ext.get(*u);
                        if (!cloak)
                        {
                                OnUserConnect(*u);
@@ -307,7 +318,7 @@ class ModuleCloaking : public Module
        ModResult OnCheckBan(User* user, Channel* chan, const std::string& mask)
        {
                char cmask[MAXBUF];
-               std::string* cloak = cu->ext.get(user);
+               std::string* cloak = cu.ext.get(user);
                /* Check if they have a cloaked host, but are not using it */
                if (cloak && *cloak != user->dhost)
                {
@@ -318,19 +329,14 @@ class ModuleCloaking : public Module
                return MOD_RES_PASSTHRU;
        }
 
-       void Prioritize()
+       void Prioritize()
        {
                /* Needs to be after m_banexception etc. */
                ServerInstance->Modules->SetPriority(this, I_OnCheckBan, PRIORITY_LAST);
-
-               /* but before m_conn_umodes, so host is generated ready to apply */
-               Module *um = ServerInstance->Modules->Find("m_conn_umodes.so");
-               ServerInstance->Modules->SetPriority(this, I_OnUserConnect, PRIORITY_AFTER, &um);
        }
 
        ~ModuleCloaking()
        {
-               delete cu;
                ServerInstance->Modules->DoneWithInterface("HashRequest");
        }
 
@@ -343,63 +349,136 @@ class ModuleCloaking : public Module
 
        void OnRehash(User* user)
        {
-               cu->DoRehash();
+               ConfigReader Conf;
+               prefix = Conf.ReadValue("cloak","prefix",0);
+
+               std::string modestr = Conf.ReadValue("cloak", "mode", 0);
+               if (modestr == "compat-host")
+                       mode = MODE_COMPAT_HOST;
+               else if (modestr == "compat-ip")
+                       mode = MODE_COMPAT_IPONLY;
+               else if (modestr == "half")
+                       mode = MODE_HALF_CLOAK;
+               else if (modestr == "full")
+                       mode = MODE_OPAQUE;
+               else
+                       throw ModuleException("Bad value for <cloak:mode>; must be one of compat-host, compat-ip, half, full");
+
+               if (mode == MODE_COMPAT_HOST || mode == MODE_COMPAT_IPONLY)
+               {
+                       bool lowercase = Conf.ReadFlag("cloak", "lowercase", 0);
+
+                       /* These are *not* using the need_positive parameter of ReadInteger -
+                        * that will limit the valid values to only the positive values in a
+                        * signed int. Instead, accept any value that fits into an int and
+                        * cast it to an unsigned int. That will, a bit oddly, give us the full
+                        * spectrum of an unsigned integer. - Special
+                        *
+                        * We must limit the keys or else we get different results on
+                        * amd64/x86 boxes. - psychon */
+                       const unsigned int limit = 0x80000000;
+                       compatkey[1] = (unsigned int) Conf.ReadInteger("cloak","key1",0,false);
+                       compatkey[2] = (unsigned int) Conf.ReadInteger("cloak","key2",0,false);
+                       compatkey[3] = (unsigned int) Conf.ReadInteger("cloak","key3",0,false);
+                       compatkey[4] = (unsigned int) Conf.ReadInteger("cloak","key4",0,false);
+
+                       if (!lowercase)
+                       {
+                               xtab[0] = "F92E45D871BCA630";
+                               xtab[1] = "A1B9D80C72E653F4";
+                               xtab[2] = "1ABC078934DEF562";
+                               xtab[3] = "ABCDEF5678901234";
+                       }
+                       else
+                       {
+                               xtab[0] = "f92e45d871bca630";
+                               xtab[1] = "a1b9d80c72e653f4";
+                               xtab[2] = "1abc078934def562";
+                               xtab[3] = "abcdef5678901234";
+                       }
+
+                       if (prefix.empty())
+                               prefix = ServerInstance->Config->Network;
+
+                       if (!compatkey[1] || !compatkey[2] || !compatkey[3] || !compatkey[4] ||
+                               compatkey[1] >= limit || compatkey[2] >= limit || compatkey[3] >= limit || compatkey[4] >= limit)
+                       {
+                               std::string detail;
+                               if (!compatkey[1] || compatkey[1] >= limit)
+                                       detail = "<cloak:key1> is not valid, it may be set to a too high/low value, or it may not exist.";
+                               else if (!compatkey[2] || compatkey[2] >= limit)
+                                       detail = "<cloak:key2> is not valid, it may be set to a too high/low value, or it may not exist.";
+                               else if (!compatkey[3] || compatkey[3] >= limit)
+                                       detail = "<cloak:key3> is not valid, it may be set to a too high/low value, or it may not exist.";
+                               else if (!compatkey[4] || compatkey[4] >= limit)
+                                       detail = "<cloak:key4> is not valid, it may be set to a too high/low value, or it may not exist.";
+
+                               throw ModuleException("You have not defined cloak keys for m_cloaking!!! THIS IS INSECURE AND SHOULD BE CHECKED! - " + detail);
+                       }
+               }
+               else
+               {
+                       key = Conf.ReadFlag("cloak", "key", 0);
+                       if (key.empty() || key == "secret")
+                               throw ModuleException("You have not defined cloak keys for m_cloaking. Define <cloak:key> as a network-wide secret.");
+               }
        }
 
        void OnUserConnect(User* dest)
        {
-               std::string* cloak = cu->ext.get(dest);
+               std::string* cloak = cu.ext.get(dest);
                if (cloak)
                        return;
 
-               if (dest->host.find('.') != std::string::npos || dest->host.find(':') != std::string::npos)
+               if (dest->host.find('.') == std::string::npos && dest->host.find(':') == std::string::npos)
+                       return;
+
+               std::string ipstr = dest->GetIPString();
+               std::string chost;
+
+               switch (mode)
                {
-                       unsigned int iv[] = { cu->key1, cu->key2, cu->key3, cu->key4 };
-                       std::string a = cu->LastTwoDomainParts(dest->host);
-                       std::string b;
-
-                       /* InspIRCd users have two hostnames; A displayed
-                        * hostname which can be modified by modules (e.g.
-                        * to create vhosts, implement chghost, etc) and a
-                        * 'real' hostname which you shouldnt write to.
-                        */
-
-                       /* 2008/08/18: add <cloak:ipalways> which always cloaks
-                        * the IP, for anonymity. --nenolod
-                        */
-                       if (!cu->ipalways)
+                       case MODE_COMPAT_HOST:
                        {
-                               /** Reset the Hash module, and send it our IV and hex table */
-                               HashRequestIV hash(this, cu->HashProvider, iv, cu->xtab[(dest->host[0]) % 4], dest->host);
+                               if (ipstr != dest->host)
+                               {
+                                       std::string tail = LastTwoDomainParts(dest->host);
 
-                               /* Generate a cloak using specialized Hash */
-                               std::string hostcloak = cu->prefix + "-" + hash.result.substr(0,8) + a;
+                                       /** Reset the Hash module, and send it our IV and hex table */
+                                       HashRequestIV hash(this, HashProvider, compatkey, xtab[(dest->host[0]) % 4], dest->host);
 
-                               /* Fix by brain - if the cloaked host is > the max length of a host (64 bytes
-                                * according to the DNS RFC) then tough titty, they get cloaked as an IP.
-                                * Their ISP shouldnt go to town on subdomains, or they shouldnt have a kiddie
-                                * vhost.
-                                */
-                               std::string testaddr;
-                               int testport;
-                               if (!irc::sockets::satoap(&dest->client_sa, testaddr, testport) && (hostcloak.length() <= 64))
-                                       /* not a valid address, must have been a host, so cloak as a host */
-                                       b = hostcloak;
-                               else if (dest->client_sa.sa.sa_family == AF_INET6)
-                                       b = cu->Cloak6(dest->GetIPString());
-                               else
-                                       b = cu->Cloak4(dest->GetIPString());
+                                       /* Generate a cloak using specialized Hash */
+                                       chost = prefix + "-" + hash.result.substr(0,8) + tail;
+
+                                       /* Fix by brain - if the cloaked host is > the max length of a host (64 bytes
+                                        * according to the DNS RFC) then they get cloaked as an IP.
+                                        */
+                                       if (chost.length() <= 64)
+                                               break;
+                               }
+                               // fall through to IP cloak
                        }
-                       else
-                       {
+                       case MODE_COMPAT_IPONLY:
                                if (dest->client_sa.sa.sa_family == AF_INET6)
-                                       b = cu->Cloak6(dest->GetIPString());
+                                       chost = CompatCloak6(ipstr.c_str());
                                else
-                                       b = cu->Cloak4(dest->GetIPString());
+                                       chost = CompatCloak4(ipstr.c_str());
+                               break;
+                       case MODE_HALF_CLOAK:
+                       {
+                               std::string tail;
+                               if (ipstr != dest->host)
+                                       tail = LastTwoDomainParts(dest->host);
+                               if (tail.empty() || tail.length() > 50)
+                                       tail = ReversePartialIP(dest->client_sa);
+                               chost = prefix + SegmentCloak(dest->host, 1) + "." + tail;
+                               break;
                        }
-
-                       cu->ext.set(dest,b);
+                       case MODE_OPAQUE:
+                       default:
+                               chost = SegmentIP(dest->client_sa);
                }
+               cu.ext.set(dest,chost);
        }
 
 };
index f39d0eee748b676db2a0556c3fb6861a0ca5bc8a..08a24eb8548f99e1a18c25efbe06c1b430c57b20 100644 (file)
@@ -46,7 +46,7 @@ struct HashNameRequest : public Request
 struct HashRequest : public Request
 {
        const std::string data;
-       std::string result;
+       std::string binresult;
        /** Initialize HashSumRequest for sending.
         * @param Me A pointer to the sending module
         * @param Target A pointer to the hashing module
@@ -57,6 +57,10 @@ struct HashRequest : public Request
        {
                Send();
        }
+       inline std::string hex()
+       {
+               return BinToHex(binresult);
+       }
 };
 
 /** Allows the IVs for the hash to be specified. As the choice of initial IV is
index 00d3fb1e9260fa1ddcc8247b1b814bfa5c500d29..9fa4894fdce51c44eb843e78398a25d5413e627d 100644 (file)
@@ -276,10 +276,10 @@ class ModuleMD5 : public Module
        {
                if (strcmp("HASH", request.id) == 0)
                {
-                       char res[33];
+                       char res[16];
                        HashRequest& req = static_cast<HashRequest&>(request);
-                       GenHash(req.data.data(), res, "0123456789abcdef", NULL, req.data.length());
-                       req.result = res;
+                       MyMD5(res, (void*)req.data.data(), req.data.length(), NULL);
+                       req.binresult.assign(res, 16);
                }
                else if (strcmp("HASH-IV", request.id) == 0)
                {
index b9fcb63a4cef88216d541d687dbc664edfa00e8f..afc6cdd7946213ee468b8acbba71ceafbe20e21b 100644 (file)
@@ -40,7 +40,7 @@ class CommandMkpasswd : public Command
                        HashRequest hash(creator, x->second, stuff);
                        /* Now attempt to generate a hash */
                        user->WriteServ("NOTICE %s :%s hashed password for %s is %s",
-                               user->nick.c_str(), algo, stuff, hash.result.c_str());
+                               user->nick.c_str(), algo, stuff, hash.hex().c_str());
                }
                else if (names.empty())
                {
@@ -139,7 +139,7 @@ class ModuleOperHash : public Module
                if (x != hashers.end())
                {
                        /* Compare the hash in the config to the generated hash */
-                       if (!strcasecmp(data.c_str(), HashRequest(this, x->second, input).result.c_str()))
+                       if (!strcasecmp(data.c_str(), HashRequest(this, x->second, input).hex().c_str()))
                                return MOD_RES_ALLOW;
                        /* No match, and must be hashed, forbid */
                        else
index 3e94efb0ac2509b304d6ad097b8744a3219bfcba..15eab59cfe437ae7c742fa49eb29a7fefdd8da7e 100644 (file)
@@ -149,8 +149,6 @@ typedef             uint32_t                dword;
    }
 
 
-const char* const chars = "0123456789abcdef";
-
 class ModuleRIPEMD160 : public Module
 {
 
@@ -455,17 +453,9 @@ class ModuleRIPEMD160 : public Module
        {
                if (strcmp("HASH", request.id) == 0)
                {
-                       char res[41];
                        HashRequest& req = static_cast<HashRequest&>(request);
-                       unsigned char* data = (unsigned char*)RMD((byte*)req.data.data(), req.data.length(), NULL);
-                       int j = 0;
-                       for (int i = 0; i < RMDsize / 8; i++)
-                       {
-                               res[j++] = chars[data[i] / 16];
-                               res[j++] = chars[data[i] % 16];
-                       }
-                       res[j] = '\0';
-                       req.result = res;
+                       char* data = (char*)RMD((byte*)req.data.data(), req.data.length(), NULL);
+                       req.binresult.assign(data, RMDsize / 8);
                }
                else if (strcmp("NAME", request.id) == 0)
                {
index 07c9ea04b674a3a2d00ea6a3666b2183b56965fd..f1b649f30c23c3d30c5e9db0cb3f48810d6d6b2b 100644 (file)
@@ -132,8 +132,6 @@ uint32_t sha256_k[64] =
        0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
 };
 
-const char* const hxc("0123456789abcdef");
-
 class ModuleSHA256 : public Module
 {
        void SHA256Init(SHA256Context *ctx, const unsigned int* ikey)
@@ -238,22 +236,12 @@ class ModuleSHA256 : public Module
                        UNPACK32(ctx->h[i], &digest[i << 2]);
        }
 
-       void SHA256(const char *src, char *dest, unsigned int len)
+       void SHA256(const char *src, unsigned char *dest, unsigned int len)
        {
-               // Generate the hash
-               unsigned char bytehash[SHA256_DIGEST_SIZE];
                SHA256Context ctx;
                SHA256Init(&ctx, NULL);
                SHA256Update(&ctx, (unsigned char *)src, len);
-               SHA256Final(&ctx, bytehash);
-               // Convert it to hex
-               int j=0;
-               for (int i = 0; i < SHA256_DIGEST_SIZE; i++)
-               {
-                       dest[j++] = hxc[bytehash[i] / 16];
-                       dest[j++] = hxc[bytehash[i] % 16];
-               }
-               dest[j] = '\0';
+               SHA256Final(&ctx, dest);
        }
 
  public:
@@ -272,10 +260,10 @@ class ModuleSHA256 : public Module
        {
                if (strcmp("HASH", request.id) == 0)
                {
-                       char res[65];
+                       unsigned char bytes[SHA256_DIGEST_SIZE];
                        HashRequest& req = static_cast<HashRequest&>(request);
-                       SHA256(req.data.data(), res, req.data.length());
-                       req.result = res;
+                       SHA256(req.data.data(), bytes, req.data.length());
+                       req.binresult.assign((char*)bytes, SHA256_DIGEST_SIZE);
                }
                else if (strcmp("NAME", request.id) == 0)
                {
index ad35a585f4a1294ffd008a165f7032a18f155301..00f908dc878946bfe3ea358663ca15fda1b3cfef 100644 (file)
@@ -77,10 +77,10 @@ std::string TreeSocket::MakePass(const std::string &password, const std::string
                }
 
                hmac2 += challenge;
-               hmac2 = HashRequest(Utils->Creator, sha256, hmac2).result;
+               hmac2 = HashRequest(Utils->Creator, sha256, hmac2).hex();
                
                std::string hmac = hmac1 + hmac2;
-               hmac = HashRequest(Utils->Creator, sha256, hmac).result;
+               hmac = HashRequest(Utils->Creator, sha256, hmac).hex();
 
                return "HMAC-SHA256:"+ hmac;
        }
index aefd54ef7759acc3b52d48c82371dc2fcedcfcdd..61f8b8488f060faf9ed68b3328c78a109f5f7968 100644 (file)
@@ -109,14 +109,14 @@ public:
 
                if (HashMod)
                {
-                       SearchAndReplace(thisquery, std::string("$md5pass"), HashRequest(this, HashMod, user->password).result);
+                       SearchAndReplace(thisquery, std::string("$md5pass"), HashRequest(this, HashMod, user->password).hex());
                }
 
                HashMod = ServerInstance->Modules->Find("m_sha256.so");
 
                if (HashMod)
                {
-                       SearchAndReplace(thisquery, std::string("$sha256pass"), HashRequest(this, HashMod, user->password).result);
+                       SearchAndReplace(thisquery, std::string("$sha256pass"), HashRequest(this, HashMod, user->password).hex());
                }
 
                /* Build the query */
index 36bda5c58d468bbc8a69f8eb8ee2d9dfb5c9195f..cf5bb3da53dff2d8ea68bfb11fe8b53e56d36185 100644 (file)
@@ -150,7 +150,7 @@ public:
                                return false;
 
                        /* Make an MD5 hash of the password for using in the query */
-                       std::string md5_pass_hash = HashRequest(this, x->second, password).result;
+                       std::string md5_pass_hash = HashRequest(this, x->second, password).hex();
 
                        /* We generate our own sum here because some database providers (e.g. SQLite) dont have a builtin md5/sha256 function,
                         * also hashing it in the module and only passing a remote query containing a hash is more secure.
diff --git a/src/modules/u_listmode.h b/src/modules/u_listmode.h
new file mode 100644 (file)
index 0000000..981e2a3
--- /dev/null
@@ -0,0 +1,414 @@
+/*       +------------------------------------+
+ *       | Inspire Internet Relay Chat Daemon |
+ *       +------------------------------------+
+ *
+ *  InspIRCd: (C) 2002-2009 InspIRCd Development Team
+ * See: http://wiki.inspircd.org/Credits
+ *
+ * This program is free but copyrighted software; see
+ *         the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef INSPIRCD_LISTMODE_PROVIDER
+#define INSPIRCD_LISTMODE_PROVIDER
+
+/** Get the time as a string
+ */
+inline std::string stringtime()
+{
+       std::ostringstream TIME;
+       TIME << ServerInstance->Time();
+       return TIME.str();
+}
+
+/** An item in a listmode's list
+ */
+class ListItem : public classbase
+{
+public:
+       std::string nick;
+       std::string mask;
+       std::string time;
+};
+
+/** The number of items a listmode's list may contain
+ */
+class ListLimit : public classbase
+{
+public:
+       std::string mask;
+       unsigned int limit;
+};
+
+/** Items stored in the channel's list
+ */
+typedef std::list<ListItem> modelist;
+/** Max items per channel by name
+ */
+typedef std::list<ListLimit> limitlist;
+
+/** The base class for list modes, should be inherited.
+ */
+class ListModeBase : public ModeHandler
+{
+ protected:
+       /** Numeric to use when outputting the list
+        */
+       unsigned int listnumeric;
+       /** Numeric to indicate end of list
+        */
+       unsigned int endoflistnumeric;
+       /** String to send for end of list
+        */
+       std::string endofliststring;
+       /** Automatically tidy up entries
+        */
+       bool tidy;
+       /** Config tag to check for max items per channel
+        */
+       std::string configtag;
+       /** Limits on a per-channel basis read from the tag
+        * specified in ListModeBase::configtag
+        */
+       limitlist chanlimits;
+
+ public:
+       /** Storage key
+        */
+       SimpleExtItem<modelist> extItem;
+
+       /** Constructor.
+        * @param Instance The creator of this class
+        * @param modechar Mode character
+        * @param eolstr End of list string
+        * @pram lnum List numeric
+        * @param eolnum End of list numeric
+        * @param autotidy Automatically tidy list entries on add
+        * @param ctag Configuration tag to get limits from
+        */
+       ListModeBase(Module* Creator, const std::string& Name, char modechar, const std::string &eolstr, unsigned int lnum, unsigned int eolnum, bool autotidy, const std::string &ctag = "banlist")
+               : ModeHandler(Creator, Name, modechar, PARAM_ALWAYS, MODETYPE_CHANNEL), 
+               listnumeric(lnum), endoflistnumeric(eolnum), endofliststring(eolstr), tidy(autotidy),
+               configtag(ctag), extItem("listbase_mode_" + name + "_list", Creator)
+       {
+               list = true;
+               this->DoRehash();
+               Extensible::Register(&extItem);
+       }
+
+       /** See mode.h
+        */
+       std::pair<bool,std::string> ModeSet(User*, User*, Channel* channel, const std::string &parameter)
+       {
+               modelist* el = extItem.get(channel);
+               if (el)
+               {
+                       for (modelist::iterator it = el->begin(); it != el->end(); it++)
+                       {
+                               if(parameter == it->mask)
+                               {
+                                       return std::make_pair(true, parameter);
+                               }
+                       }
+               }
+               return std::make_pair(false, parameter);
+       }
+
+       /** Display the list for this mode
+        * @param user The user to send the list to
+        * @param channel The channel the user is requesting the list for
+        */
+       virtual void DisplayList(User* user, Channel* channel)
+       {
+               modelist* el = extItem.get(channel);
+               if (el)
+               {
+                       for (modelist::reverse_iterator it = el->rbegin(); it != el->rend(); ++it)
+                       {
+                               user->WriteNumeric(listnumeric, "%s %s %s %s %s", user->nick.c_str(), channel->name.c_str(), it->mask.c_str(), (it->nick.length() ? it->nick.c_str() : ServerInstance->Config->ServerName.c_str()), it->time.c_str());
+                       }
+               }
+               user->WriteNumeric(endoflistnumeric, "%s %s :%s", user->nick.c_str(), channel->name.c_str(), endofliststring.c_str());
+       }
+
+       virtual void DisplayEmptyList(User* user, Channel* channel)
+       {
+               user->WriteNumeric(endoflistnumeric, "%s %s :%s", user->nick.c_str(), channel->name.c_str(), endofliststring.c_str());
+       }
+
+       /** Remove all instances of the mode from a channel.
+        * See mode.h
+        * @param channel The channel to remove all instances of the mode from
+        */
+       virtual void RemoveMode(Channel* channel, irc::modestacker* stack)
+       {
+               modelist* el = extItem.get(channel);
+               if (el)
+               {
+                       irc::modestacker modestack(false);
+
+                       for (modelist::iterator it = el->begin(); it != el->end(); it++)
+                       {
+                               if (stack)
+                                       stack->Push(this->GetModeChar(), it->mask);
+                               else
+                                       modestack.Push(this->GetModeChar(), it->mask);
+                       }
+
+                       if (stack)
+                               return;
+
+                       std::vector<std::string> stackresult;
+                       stackresult.push_back(channel->name);
+                       while (modestack.GetStackedLine(stackresult))
+                       {
+                               ServerInstance->SendMode(stackresult, ServerInstance->FakeClient);
+                               stackresult.clear();
+                               stackresult.push_back(channel->name);
+                       }
+               }
+       }
+
+       /** See mode.h
+        */
+       virtual void RemoveMode(User*, irc::modestacker* stack)
+       {
+               /* Listmodes dont get set on users */
+       }
+
+       /** Perform a rehash of this mode's configuration data
+        */
+       virtual void DoRehash()
+       {
+               ConfigReader Conf;
+
+               chanlimits.clear();
+
+               for (int i = 0; i < Conf.Enumerate(configtag); i++)
+               {
+                       // For each <banlist> tag
+                       ListLimit limit;
+                       limit.mask = Conf.ReadValue(configtag, "chan", i);
+                       limit.limit = Conf.ReadInteger(configtag, "limit", i, true);
+
+                       if (limit.mask.size() && limit.limit > 0)
+                               chanlimits.push_back(limit);
+               }
+               if (chanlimits.size() == 0)
+               {
+                       ListLimit limit;
+                       limit.mask = "*";
+                       limit.limit = 64;
+                       chanlimits.push_back(limit);
+               }
+       }
+
+       /** Populate the Implements list with the correct events for a List Mode
+        */
+       virtual void DoImplements(Module* m)
+       {
+               Implementation eventlist[] = { I_OnChannelDelete, I_OnSyncChannel, I_OnRehash };
+               ServerInstance->Modules->Attach(eventlist, m, 3);
+       }
+
+       /** Handle the list mode.
+        * See mode.h
+        */
+       virtual ModeAction OnModeChange(User* source, User*, Channel* channel, std::string &parameter, bool adding)
+       {
+               // Try and grab the list
+               modelist* el = extItem.get(channel);
+
+               if (adding)
+               {
+                       // If there was no list
+                       if (!el)
+                       {
+                               // Make one
+                               el = new modelist;
+                               extItem.set(channel, el);
+                       }
+
+                       // Clean the mask up
+                       if (this->tidy)
+                               ModeParser::CleanMask(parameter);
+
+                       // Check if the item already exists in the list
+                       for (modelist::iterator it = el->begin(); it != el->end(); it++)
+                       {
+                               if (parameter == it->mask)
+                               {
+                                       /* Give a subclass a chance to error about this */
+                                       TellAlreadyOnList(source, channel, parameter);
+
+                                       // it does, deny the change
+                                       return MODEACTION_DENY;
+                               }
+                       }
+
+                       unsigned int maxsize = 0;
+
+                       for (limitlist::iterator it = chanlimits.begin(); it != chanlimits.end(); it++)
+                       {
+                               if (InspIRCd::Match(channel->name, it->mask))
+                               {
+                                       // We have a pattern matching the channel...
+                                       maxsize = el->size();
+                                       if (IS_LOCAL(source) || (maxsize < it->limit))
+                                       {
+                                               /* Ok, it *could* be allowed, now give someone subclassing us
+                                                * a chance to validate the parameter.
+                                                * The param is passed by reference, so they can both modify it
+                                                * and tell us if we allow it or not.
+                                                *
+                                                * eg, the subclass could:
+                                                * 1) allow
+                                                * 2) 'fix' parameter and then allow
+                                                * 3) deny
+                                                */
+                                               if (ValidateParam(source, channel, parameter))
+                                               {
+                                                       // And now add the mask onto the list...
+                                                       ListItem e;
+                                                       e.mask = parameter;
+                                                       e.nick = source->nick;
+                                                       e.time = stringtime();
+
+                                                       el->push_back(e);
+                                                       return MODEACTION_ALLOW;
+                                               }
+                                               else
+                                               {
+                                                       /* If they deny it they have the job of giving an error message */
+                                                       return MODEACTION_DENY;
+                                               }
+                                       }
+                               }
+                       }
+
+                       /* List is full, give subclass a chance to send a custom message */
+                       if (!TellListTooLong(source, channel, parameter))
+                       {
+                               source->WriteNumeric(478, "%s %s %s :Channel ban/ignore list is full", source->nick.c_str(), channel->name.c_str(), parameter.c_str());
+                       }
+
+                       parameter = "";
+                       return MODEACTION_DENY;
+               }
+               else
+               {
+                       // We're taking the mode off
+                       if (el)
+                       {
+                               for (modelist::iterator it = el->begin(); it != el->end(); it++)
+                               {
+                                       if (parameter == it->mask)
+                                       {
+                                               el->erase(it);
+                                               if (el->size() == 0)
+                                               {
+                                                       extItem.unset(channel);
+                                               }
+                                               return MODEACTION_ALLOW;
+                                       }
+                               }
+                               /* Tried to remove something that wasn't set */
+                               TellNotSet(source, channel, parameter);
+                               parameter = "";
+                               return MODEACTION_DENY;
+                       }
+                       else
+                       {
+                               /* Hmm, taking an exception off a non-existant list, DIE */
+                               TellNotSet(source, channel, parameter);
+                               parameter = "";
+                               return MODEACTION_DENY;
+                       }
+               }
+               return MODEACTION_DENY;
+       }
+
+       /** Syncronize channel item list with another server.
+        * See modules.h
+        * @param chan Channel to syncronize
+        * @param proto Protocol module pointer
+        * @param opaque Opaque connection handle
+        */
+       virtual void DoSyncChannel(Channel* chan, Module* proto, void* opaque)
+       {
+               modelist* mlist = extItem.get(chan);
+               irc::modestacker modestack(true);
+               std::vector<std::string> stackresult;
+               std::vector<TranslateType> types;
+               types.push_back(TR_TEXT);
+               if (mlist)
+               {
+                       for (modelist::iterator it = mlist->begin(); it != mlist->end(); it++)
+                       {
+                               modestack.Push(std::string(1, mode)[0], it->mask);
+                       }
+               }
+               while (modestack.GetStackedLine(stackresult))
+               {
+                       types.assign(stackresult.size(), this->GetTranslateType());
+                       proto->ProtoSendMode(opaque, TYPE_CHANNEL, chan, stackresult, types);
+                       stackresult.clear();
+               }
+       }
+
+       /** Clean up module on unload
+        * @param target_type Type of target to clean
+        * @param item Item to clean
+        */
+       virtual void DoCleanup(int, void*)
+       {
+       }
+
+       /** Validate parameters.
+        * Overridden by implementing module.
+        * @param source Source user adding the parameter
+        * @param channel Channel the parameter is being added to
+        * @param parameter The actual parameter being added
+        * @return true if the parameter is valid
+        */
+       virtual bool ValidateParam(User*, Channel*, std::string&)
+       {
+               return true;
+       }
+
+       /** Tell the user the list is too long.
+        * Overridden by implementing module.
+        * @param source Source user adding the parameter
+        * @param channel Channel the parameter is being added to
+        * @param parameter The actual parameter being added
+        * @return Ignored
+        */
+       virtual bool TellListTooLong(User*, Channel*, std::string&)
+       {
+               return false;
+       }
+
+       /** Tell the user an item is already on the list.
+        * Overridden by implementing module.
+        * @param source Source user adding the parameter
+        * @param channel Channel the parameter is being added to
+        * @param parameter The actual parameter being added
+        */
+       virtual void TellAlreadyOnList(User*, Channel*, std::string&)
+       {
+       }
+
+       /** Tell the user that the parameter is not in the list.
+        * Overridden by implementing module.
+        * @param source Source user removing the parameter
+        * @param channel Channel the parameter is being removed from
+        * @param parameter The actual parameter being removed
+        */
+       virtual void TellNotSet(User*, Channel*, std::string&)
+       {
+       }
+};
+
+#endif