diff options
author | Adam <Adam@anope.org> | 2014-01-02 11:02:14 -0500 |
---|---|---|
committer | Adam <Adam@anope.org> | 2014-01-02 11:03:33 -0500 |
commit | 004c4cbe5f4c37cf455cb6d0caa434fbbb3406ea (patch) | |
tree | 0e6675dfe28a829f5786b0b2f993566c61662de7 /modules/m_proxyscan.cpp | |
parent | 072202c181943901c727782e64881adadf13d7dd (diff) |
Move modules out of extras that dont have external dependencies
Diffstat (limited to 'modules/m_proxyscan.cpp')
-rw-r--r-- | modules/m_proxyscan.cpp | 379 |
1 files changed, 379 insertions, 0 deletions
diff --git a/modules/m_proxyscan.cpp b/modules/m_proxyscan.cpp new file mode 100644 index 000000000..98178260c --- /dev/null +++ b/modules/m_proxyscan.cpp @@ -0,0 +1,379 @@ +/* + * (C) 2003-2014 Anope Team + * Contact us at team@anope.org + * + * Please read COPYING and README for further details. + */ + +#include "module.h" + +struct ProxyCheck +{ + std::set<Anope::string, ci::less> types; + std::vector<unsigned short> ports; + time_t duration; + Anope::string reason; +}; + +static Anope::string ProxyCheckString; +static Anope::string target_ip; +static unsigned short target_port; +static bool add_to_akill; + +class ProxyCallbackListener : public ListenSocket +{ + class ProxyCallbackClient : public ClientSocket, public BufferedSocket + { + public: + ProxyCallbackClient(ListenSocket *l, int f, const sockaddrs &a) : Socket(f, l->IsIPv6()), ClientSocket(l, a), BufferedSocket() + { + } + + void OnAccept() anope_override + { + this->Write(ProxyCheckString); + } + + bool ProcessWrite() anope_override + { + return !BufferedSocket::ProcessWrite() || this->write_buffer.empty() ? false : true; + } + }; + + public: + ProxyCallbackListener(const Anope::string &b, int p) : Socket(-1, b.find(':') != Anope::string::npos), ListenSocket(b, p, false) + { + } + + ClientSocket *OnAccept(int fd, const sockaddrs &addr) anope_override + { + return new ProxyCallbackClient(this, fd, addr); + } +}; + +class ProxyConnect : public ConnectionSocket +{ + static ServiceReference<XLineManager> akills; + + public: + static std::set<ProxyConnect *> proxies; + + ProxyCheck proxy; + unsigned short port; + time_t created; + + ProxyConnect(ProxyCheck &p, unsigned short po) : Socket(-1), ConnectionSocket(), proxy(p), + port(po), created(Anope::CurTime) + { + proxies.insert(this); + } + + ~ProxyConnect() + { + proxies.erase(this); + } + + virtual void OnConnect() anope_override = 0; + virtual const Anope::string GetType() const = 0; + + protected: + void Ban() + { + Anope::string reason = this->proxy.reason; + + reason = reason.replace_all_cs("%t", this->GetType()); + reason = reason.replace_all_cs("%i", this->conaddr.addr()); + reason = reason.replace_all_cs("%p", stringify(this->conaddr.port())); + + BotInfo *OperServ = Config->GetClient("OperServ"); + Log(OperServ) << "PROXYSCAN: Open " << this->GetType() << " proxy found on " << this->conaddr.addr() << ":" << this->conaddr.port() << " (" << reason << ")"; + XLine *x = new XLine("*@" + this->conaddr.addr(), OperServ ? OperServ->nick : "", Anope::CurTime + this->proxy.duration, reason, XLineManager::GenerateUID()); + if (add_to_akill && akills) + { + akills->AddXLine(x); + akills->Send(NULL, x); + } + else + { + if (IRCD->CanSZLine) + IRCD->SendSZLine(NULL, x); + else + IRCD->SendAkill(NULL, x); + delete x; + } + } +}; +ServiceReference<XLineManager> ProxyConnect::akills("XLineManager", "xlinemanager/sgline"); +std::set<ProxyConnect *> ProxyConnect::proxies; + +class HTTPProxyConnect : public ProxyConnect, public BufferedSocket +{ + public: + HTTPProxyConnect(ProxyCheck &p, unsigned short po) : Socket(-1), ProxyConnect(p, po), BufferedSocket() + { + } + + void OnConnect() anope_override + { + this->Write("CONNECT %s:%d HTTP/1.0", target_ip.c_str(), target_port); + this->Write("Content-length: 0"); + this->Write("Connection: close"); + this->Write(""); + } + + const Anope::string GetType() const anope_override + { + return "HTTP"; + } + + bool ProcessRead() anope_override + { + bool b = BufferedSocket::ProcessRead(); + if (this->GetLine() == ProxyCheckString) + { + this->Ban(); + return false; + } + return b; + } +}; + +class SOCKS5ProxyConnect : public ProxyConnect, public BinarySocket +{ + public: + SOCKS5ProxyConnect(ProxyCheck &p, unsigned short po) : Socket(-1), ProxyConnect(p, po), BinarySocket() + { + } + + void OnConnect() anope_override + { + sockaddrs target_addr; + char buf[4 + sizeof(target_addr.sa4.sin_addr.s_addr) + sizeof(target_addr.sa4.sin_port)]; + int ptr = 0; + target_addr.pton(AF_INET, target_ip, target_port); + if (!target_addr.valid()) + return; + + buf[ptr++] = 5; // Version + buf[ptr++] = 1; // # of methods + buf[ptr++] = 0; // No authentication + + this->Write(buf, ptr); + + ptr = 1; + buf[ptr++] = 1; // Connect + buf[ptr++] = 0; // Reserved + buf[ptr++] = 1; // IPv4 + memcpy(buf + ptr, &target_addr.sa4.sin_addr.s_addr, sizeof(target_addr.sa4.sin_addr.s_addr)); + ptr += sizeof(target_addr.sa4.sin_addr.s_addr); + memcpy(buf + ptr, &target_addr.sa4.sin_port, sizeof(target_addr.sa4.sin_port)); + ptr += sizeof(target_addr.sa4.sin_port); + + this->Write(buf, ptr); + } + + const Anope::string GetType() const anope_override + { + return "SOCKS5"; + } + + bool Read(const char *buffer, size_t l) anope_override + { + if (l >= ProxyCheckString.length() && !strncmp(buffer, ProxyCheckString.c_str(), ProxyCheckString.length())) + { + this->Ban(); + return false; + } + return true; + } +}; + +class ModuleProxyScan : public Module +{ + Anope::string listen_ip; + unsigned short listen_port; + Anope::string con_notice, con_source; + std::vector<ProxyCheck> proxyscans; + + ProxyCallbackListener *listener; + + class ConnectionTimeout : public Timer + { + public: + ConnectionTimeout(Module *c, long timeout) : Timer(c, timeout, Anope::CurTime, true) + { + } + + void Tick(time_t) anope_override + { + for (std::set<ProxyConnect *>::iterator it = ProxyConnect::proxies.begin(), it_end = ProxyConnect::proxies.end(); it != it_end; ++it) + { + ProxyConnect *p = *it; + + if (p->created + this->GetSecs() < Anope::CurTime) + p->flags[SF_DEAD] = true; + } + } + } connectionTimeout; + + public: + ModuleProxyScan(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, EXTRA | VENDOR), + connectionTimeout(this, 5) + { + + + this->listener = NULL; + } + + ~ModuleProxyScan() + { + for (std::set<ProxyConnect *>::iterator it = ProxyConnect::proxies.begin(), it_end = ProxyConnect::proxies.end(); it != it_end;) + { + ProxyConnect *p = *it; + ++it; + delete p; + } + + for (std::map<int, Socket *>::const_iterator it = SocketEngine::Sockets.begin(), it_end = SocketEngine::Sockets.end(); it != it_end;) + { + Socket *s = it->second; + ++it; + + ClientSocket *cs = dynamic_cast<ClientSocket *>(s); + if (cs != NULL && cs->ls == this->listener) + delete s; + } + + delete this->listener; + } + + void OnReload(Configuration::Conf *conf) anope_override + { + Configuration::Block *config = Config->GetModule(this); + + Anope::string s_target_ip = config->Get<const Anope::string>("target_ip"); + if (s_target_ip.empty()) + throw ConfigException(this->name + " target_ip may not be empty"); + + int s_target_port = config->Get<int>("target_port", "-1"); + if (s_target_port <= 0) + throw ConfigException(this->name + " target_port may not be empty and must be a positive number"); + + Anope::string s_listen_ip = config->Get<const Anope::string>("listen_ip"); + if (s_listen_ip.empty()) + throw ConfigException(this->name + " listen_ip may not be empty"); + + int s_listen_port = config->Get<int>("listen_port", "-1"); + if (s_listen_port <= 0) + throw ConfigException(this->name + " listen_port may not be empty and must be a positive number"); + + target_ip = s_target_ip; + target_port = s_target_port; + this->listen_ip = s_listen_ip; + this->listen_port = s_listen_port; + this->con_notice = config->Get<const Anope::string>("connect_notice"); + this->con_source = config->Get<const Anope::string>("connect_source"); + add_to_akill = config->Get<bool>("add_to_akill", "true"); + this->connectionTimeout.SetSecs(config->Get<time_t>("timeout", "5s")); + + ProxyCheckString = Config->GetBlock("networkinfo")->Get<const Anope::string>("networkname") + " proxy check"; + delete this->listener; + this->listener = NULL; + try + { + this->listener = new ProxyCallbackListener(this->listen_ip, this->listen_port); + } + catch (const SocketException &ex) + { + throw ConfigException("m_proxyscan: " + ex.GetReason()); + } + + this->proxyscans.clear(); + for (int i = 0; i < config->CountBlock("proxyscan"); ++i) + { + Configuration::Block *block = config->GetBlock("proxyscan", i); + ProxyCheck p; + Anope::string token; + + commasepstream sep(block->Get<const Anope::string>("type")); + while (sep.GetToken(token)) + { + if (!token.equals_ci("HTTP") && !token.equals_ci("SOCKS5")) + continue; + p.types.insert(token); + } + if (p.types.empty()) + continue; + + commasepstream sep2(block->Get<const Anope::string>("port")); + while (sep2.GetToken(token)) + { + try + { + unsigned short port = convertTo<unsigned short>(token); + p.ports.push_back(port); + } + catch (const ConvertException &) { } + } + if (p.ports.empty()) + continue; + + p.duration = block->Get<time_t>("time", "4h"); + p.reason = block->Get<const Anope::string>("reason"); + if (p.reason.empty()) + continue; + + this->proxyscans.push_back(p); + } + } + + void OnUserConnect(User *user, bool &exempt) anope_override + { + if (exempt || user->Quitting() || !Me->IsSynced() || !user->server->IsSynced()) + return; + + /* At this time we only support IPv4 */ + sockaddrs user_ip; + user_ip.pton(AF_INET, user->ip); + if (!user_ip.valid()) + /* User doesn't have a valid IPv4 IP (ipv6/spoof/etc) */ + return; + + if (!this->con_notice.empty() && !this->con_source.empty()) + { + BotInfo *bi = BotInfo::Find(this->con_source); + if (bi) + user->SendMessage(bi, this->con_notice); + } + + for (unsigned i = this->proxyscans.size(); i > 0; --i) + { + ProxyCheck &p = this->proxyscans[i - 1]; + + for (std::set<Anope::string, ci::less>::iterator it = p.types.begin(), it_end = p.types.end(); it != it_end; ++it) + { + for (unsigned k = 0; k < p.ports.size(); ++k) + { + try + { + ProxyConnect *con = NULL; + if (it->equals_ci("HTTP")) + con = new HTTPProxyConnect(p, p.ports[k]); + else if (it->equals_ci("SOCKS5")) + con = new SOCKS5ProxyConnect(p, p.ports[k]); + else + continue; + con->Connect(user->ip, p.ports[k]); + } + catch (const SocketException &ex) + { + Log(LOG_DEBUG) << "m_proxyscan: " << ex.GetReason(); + } + } + } + } + } +}; + +MODULE_INIT(ModuleProxyScan) + |