m_proxyscan.cpp

Go to the documentation of this file.
00001 /*
00002  * (C) 2003-2013 Anope Team
00003  * Contact us at team@anope.org
00004  *
00005  * Please read COPYING and README for further details.
00006  */
00007 
00008 #include "module.h"
00009 
00010 struct ProxyCheck
00011 {
00012         std::set<Anope::string, ci::less> types;
00013         std::vector<unsigned short> ports;
00014         time_t duration;
00015         Anope::string reason;
00016 };
00017 
00018 static Anope::string ProxyCheckString;
00019 static Anope::string target_ip;
00020 static unsigned short target_port;
00021 static bool add_to_akill;
00022 
00023 class ProxyCallbackListener : public ListenSocket
00024 {
00025         class ProxyCallbackClient : public ClientSocket, public BufferedSocket
00026         {
00027          public:
00028                 ProxyCallbackClient(ListenSocket *l, int f, const sockaddrs &a) : Socket(f, l->IsIPv6()), ClientSocket(l, a), BufferedSocket()
00029                 {
00030                 }
00031 
00032                 void OnAccept() anope_override
00033                 {
00034                         this->Write(ProxyCheckString);
00035                 }
00036 
00037                 bool ProcessWrite() anope_override
00038                 {
00039                         return !BufferedSocket::ProcessWrite() || this->write_buffer.empty() ? false : true;
00040                 }
00041         };
00042 
00043  public:
00044         ProxyCallbackListener(const Anope::string &b, int p) : Socket(-1, b.find(':') != Anope::string::npos), ListenSocket(b, p, false)
00045         {
00046         }
00047 
00048         ClientSocket *OnAccept(int fd, const sockaddrs &addr) anope_override
00049         {
00050                 return new ProxyCallbackClient(this, fd, addr);
00051         }
00052 };
00053 
00054 class ProxyConnect : public ConnectionSocket
00055 {
00056         static ServiceReference<XLineManager> akills;
00057 
00058  public:
00059         static std::set<ProxyConnect *> proxies;
00060 
00061         ProxyCheck proxy;
00062         unsigned short port;
00063         time_t created;
00064 
00065         ProxyConnect(ProxyCheck &p, unsigned short po) : Socket(-1), ConnectionSocket(), proxy(p),
00066                 port(po), created(Anope::CurTime)
00067         {
00068                 proxies.insert(this);
00069         }
00070 
00071         ~ProxyConnect()
00072         {
00073                 proxies.erase(this);
00074         }
00075 
00076         virtual void OnConnect() anope_override = 0;
00077         virtual const Anope::string GetType() const = 0;
00078 
00079  protected:
00080         void Ban()
00081         {
00082                 Anope::string reason = this->proxy.reason;
00083 
00084                 reason = reason.replace_all_cs("%t", this->GetType());
00085                 reason = reason.replace_all_cs("%i", this->conaddr.addr());
00086                 reason = reason.replace_all_cs("%p", stringify(this->conaddr.port()));
00087 
00088                 Log(OperServ) << "PROXYSCAN: Open " << this->GetType() << " proxy found on " << this->conaddr.addr() << ":" << this->conaddr.port() << " (" << reason << ")";
00089                 XLine *x = new XLine("*@" + this->conaddr.addr(), Config->OperServ, Anope::CurTime + this->proxy.duration, reason, XLineManager::GenerateUID());
00090                 if (add_to_akill && akills)
00091                 {
00092                         akills->AddXLine(x);
00093                         akills->Send(NULL, x);
00094                 }
00095                 else
00096                 {
00097                         if (IRCD->CanSZLine)
00098                                 IRCD->SendSZLine(NULL, x);
00099                         else
00100                                 IRCD->SendAkill(NULL, x);
00101                         delete x;
00102                 }
00103         }
00104 };
00105 ServiceReference<XLineManager> ProxyConnect::akills("XLineManager", "xlinemanager/sgline");
00106 std::set<ProxyConnect *> ProxyConnect::proxies;
00107 
00108 class HTTPProxyConnect : public ProxyConnect, public BufferedSocket
00109 {
00110  public:
00111         HTTPProxyConnect(ProxyCheck &p, unsigned short po) : Socket(-1), ProxyConnect(p, po), BufferedSocket()
00112         {
00113         }
00114 
00115         void OnConnect() anope_override
00116         {
00117                 this->Write("CONNECT %s:%d HTTP/1.0", target_ip.c_str(), target_port);
00118                 this->Write("Content-length: 0");
00119                 this->Write("Connection: close");
00120                 this->Write("");
00121         }
00122 
00123         const Anope::string GetType() const anope_override
00124         {
00125                 return "HTTP";
00126         }
00127 
00128         bool ProcessRead() anope_override
00129         {
00130                 BufferedSocket::ProcessRead();
00131                 if (this->GetLine() == ProxyCheckString)
00132                 {
00133                         this->Ban();
00134                         return false;
00135                 }
00136                 return true;
00137         }
00138 };
00139 
00140 class SOCKS5ProxyConnect : public ProxyConnect, public BinarySocket
00141 {
00142  public:
00143         SOCKS5ProxyConnect(ProxyCheck &p, unsigned short po) : Socket(-1), ProxyConnect(p, po), BinarySocket()
00144         {
00145         }
00146 
00147         void OnConnect() anope_override
00148         {
00149                 sockaddrs target_addr;
00150                 char buf[4 + sizeof(target_addr.sa4.sin_addr.s_addr) + sizeof(target_addr.sa4.sin_port)];
00151                 int ptr = 0;
00152                 try
00153                 {
00154                         target_addr.pton(AF_INET, target_ip, target_port);
00155                 }
00156                 catch (const SocketException &)
00157                 {
00158                         return;
00159                 }
00160 
00161                 buf[ptr++] = 5; // Version
00162                 buf[ptr++] = 1; // # of methods
00163                 buf[ptr++] = 0; // No authentication
00164 
00165                 this->Write(buf, ptr);
00166 
00167                 ptr = 1;
00168                 buf[ptr++] = 1; // Connect
00169                 buf[ptr++] = 0; // Reserved
00170                 buf[ptr++] = 1; // IPv4
00171                 memcpy(buf + ptr, &target_addr.sa4.sin_addr.s_addr, sizeof(target_addr.sa4.sin_addr.s_addr));
00172                 ptr += sizeof(target_addr.sa4.sin_addr.s_addr);
00173                 memcpy(buf + ptr, &target_addr.sa4.sin_port, sizeof(target_addr.sa4.sin_port));
00174                 ptr += sizeof(target_addr.sa4.sin_port);
00175 
00176                 this->Write(buf, ptr);
00177         }
00178 
00179         const Anope::string GetType() const anope_override
00180         {
00181                 return "SOCKS5";
00182         }
00183 
00184         bool Read(const char *buffer, size_t l) anope_override
00185         {
00186                 if (l >= ProxyCheckString.length() && !strncmp(buffer, ProxyCheckString.c_str(), ProxyCheckString.length()))
00187                 {
00188                         this->Ban();
00189                         return false;
00190                 }
00191                 return true;
00192         }
00193 };
00194 
00195 class ModuleProxyScan : public Module
00196 {
00197         Anope::string listen_ip;
00198         unsigned short listen_port;
00199         Anope::string con_notice, con_source;
00200         std::vector<ProxyCheck> proxyscans;
00201 
00202         ProxyCallbackListener *listener;
00203 
00204         class ConnectionTimeout : public CallBack
00205         {
00206          public:
00207                 ConnectionTimeout(Module *creator, long timeout) : CallBack(creator, timeout, Anope::CurTime, true)
00208                 {
00209                 }
00210 
00211                 void Tick(time_t) anope_override
00212                 {
00213                         for (std::set<ProxyConnect *>::iterator it = ProxyConnect::proxies.begin(), it_end = ProxyConnect::proxies.end(); it != it_end; ++it)
00214                         {
00215                                 ProxyConnect *p = *it;
00216 
00217                                 if (p->created + this->GetSecs() < Anope::CurTime)
00218                                         p->flags[SF_DEAD] = true;
00219                         }
00220                 }
00221         } connectionTimeout;
00222 
00223  public:
00224         ModuleProxyScan(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, SUPPORTED),
00225                 connectionTimeout(this, 5)
00226         {
00227                 this->SetAuthor("Anope");
00228 
00229                 Implementation i[] = { I_OnReload, I_OnUserConnect };
00230                 ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation));
00231 
00232                 this->listener = NULL;
00233 
00234                 try
00235                 {
00236                         OnReload();
00237                 }
00238                 catch (const ConfigException &ex)
00239                 {
00240                         throw ModuleException(ex.GetReason());
00241                 }
00242         }
00243 
00244         ~ModuleProxyScan()
00245         {
00246                 for (std::set<ProxyConnect *>::iterator it = ProxyConnect::proxies.begin(), it_end = ProxyConnect::proxies.end(); it != it_end;)
00247                 {
00248                         ProxyConnect *p = *it;
00249                         ++it;
00250                         delete p;
00251                 }
00252 
00253                 for (std::map<int, Socket *>::const_iterator it = SocketEngine::Sockets.begin(), it_end = SocketEngine::Sockets.end(); it != it_end;)
00254                 {
00255                         Socket *s = it->second;
00256                         ++it;
00257 
00258                         ClientSocket *cs = dynamic_cast<ClientSocket *>(s);
00259                         if (cs != NULL && cs->ls == this->listener)
00260                                 delete s;
00261                 }
00262 
00263                 delete this->listener;
00264         }
00265 
00266         void OnReload() anope_override
00267         {
00268                 ConfigReader config;
00269 
00270                 Anope::string s_target_ip = config.ReadValue("m_proxyscan", "target_ip", "", 0);
00271                 if (s_target_ip.empty())
00272                         throw ConfigException("m_proxyscan:target_ip may not be empty");
00273 
00274                 int s_target_port = config.ReadInteger("m_proxyscan", "target_port", "-1", 0, true);
00275                 if (s_target_port <= 0)
00276                         throw ConfigException("m_proxyscan:target_port may not be empty and must be a positive number");
00277 
00278                 Anope::string s_listen_ip = config.ReadValue("m_proxyscan", "listen_ip", "", 0);
00279                 if (s_listen_ip.empty())
00280                         throw ConfigException("m_proxyscan:listen_ip may not be empty");
00281 
00282                 int s_listen_port = config.ReadInteger("m_proxyscan", "listen_port", "-1", 0, true);
00283                 if (s_listen_port <= 0)
00284                         throw ConfigException("m_proxyscan:listen_port may not be empty and must be a positive number");
00285 
00286                 target_ip = s_target_ip;
00287                 target_port = s_target_port;
00288                 this->listen_ip = s_listen_ip;
00289                 this->listen_port = s_listen_port;
00290                 this->con_notice = config.ReadValue("m_proxyscan", "connect_notice", "", 0);
00291                 this->con_source = config.ReadValue("m_proxyscan", "connect_source", "", 0);
00292                 add_to_akill = config.ReadFlag("m_proxyscan", "add_to_akill", "true", 0);
00293                 this->connectionTimeout.SetSecs(config.ReadInteger("m_proxyscan", "timeout", "5", 0, true));
00294 
00295                 ProxyCheckString = Config->NetworkName + " proxy check";
00296                 delete this->listener;
00297                 this->listener = NULL;
00298                 try
00299                 {
00300                         this->listener = new ProxyCallbackListener(this->listen_ip, this->listen_port);
00301                 }
00302                 catch (const SocketException &ex)
00303                 {
00304                         throw ConfigException("m_proxyscan: " + ex.GetReason());
00305                 }
00306 
00307                 this->proxyscans.clear();
00308                 for (int i = 0; i < config.Enumerate("proxyscan"); ++i)
00309                 {
00310                         ProxyCheck p;
00311                         Anope::string token;
00312 
00313                         commasepstream sep(config.ReadValue("proxyscan", "type", "", i));
00314                         while (sep.GetToken(token))
00315                         {
00316                                 if (!token.equals_ci("HTTP") && !token.equals_ci("SOCKS5"))
00317                                         continue;
00318                                 p.types.insert(token);
00319                         }
00320                         if (p.types.empty())
00321                                 continue;
00322 
00323                         commasepstream sep2(config.ReadValue("proxyscan", "port", "", i));
00324                         while (sep2.GetToken(token))
00325                         {
00326                                 try
00327                                 {
00328                                         unsigned short port = convertTo<unsigned short>(token);
00329                                         p.ports.push_back(port);
00330                                 }
00331                                 catch (const ConvertException &) { }
00332                         }
00333                         if (p.ports.empty())
00334                                 continue;
00335 
00336                         p.duration = Anope::DoTime(config.ReadValue("proxyscan", "time", "4h", i));
00337                         p.reason = config.ReadValue("proxyscan", "reason", "", i);
00338                         if (p.reason.empty())
00339                                 continue;
00340 
00341                         this->proxyscans.push_back(p);
00342                 }
00343         }
00344 
00345         void OnUserConnect(User *user, bool &exempt) anope_override
00346         {
00347                 if (exempt || user->Quitting() || !Me->IsSynced() || !user->server->IsSynced())
00348                         return;
00349 
00350                 /* At this time we only support IPv4 */
00351                 sockaddrs user_ip;
00352                 try
00353                 {
00354                         user_ip.pton(AF_INET, user->ip);
00355                 }
00356                 catch (const SocketException &)
00357                 {
00358                         /* User doesn't have a valid IPv4 IP (ipv6/spoof/etc) */
00359                         return;
00360                 }
00361 
00362                 if (!this->con_notice.empty() && !this->con_source.empty())
00363                 {
00364                         const BotInfo *bi = BotInfo::Find(this->con_source);
00365                         if (bi)
00366                                 user->SendMessage(bi, this->con_notice);
00367                 }
00368 
00369                 for (unsigned i = this->proxyscans.size(); i > 0; --i)
00370                 {
00371                         ProxyCheck &p = this->proxyscans[i - 1];
00372 
00373                         for (std::set<Anope::string, ci::less>::iterator it = p.types.begin(), it_end = p.types.end(); it != it_end; ++it)
00374                         {
00375                                 for (unsigned k = 0; k < p.ports.size(); ++k)
00376                                 {
00377                                         try
00378                                         {
00379                                                 ProxyConnect *con = NULL;
00380                                                 if (it->equals_ci("HTTP"))
00381                                                         con = new HTTPProxyConnect(p, p.ports[k]);
00382                                                 else if (it->equals_ci("SOCKS5"))
00383                                                         con = new SOCKS5ProxyConnect(p, p.ports[k]);
00384                                                 else
00385                                                         continue;
00386                                                 con->Connect(user->ip, p.ports[k]);
00387                                         }
00388                                         catch (const SocketException &ex)
00389                                         {
00390                                                 Log(LOG_DEBUG) << "m_proxyscan: " << ex.GetReason();
00391                                         }
00392                                 }
00393                         }
00394                 }
00395         }
00396 };
00397 
00398 MODULE_INIT(ModuleProxyScan)
00399