anope

- supernets anope source code & configuration
git clone git://git.acid.vegas/anope.git
Log | Files | Refs | Archive | README

m_proxyscan.cpp (9870B)

      1 /*
      2  *
      3  * (C) 2003-2022 Anope Team
      4  * Contact us at team@anope.org
      5  *
      6  * Please read COPYING and README for further details.
      7  */
      8 
      9 #include "module.h"
     10 
     11 struct ProxyCheck
     12 {
     13 	std::set<Anope::string, ci::less> types;
     14 	std::vector<unsigned short> ports;
     15 	time_t duration;
     16 	Anope::string reason;
     17 };
     18 
     19 static Anope::string ProxyCheckString;
     20 static Anope::string target_ip;
     21 static unsigned short target_port;
     22 static bool add_to_akill;
     23 
     24 class ProxyCallbackListener : public ListenSocket
     25 {
     26 	class ProxyCallbackClient : public ClientSocket, public BufferedSocket
     27 	{
     28 	 public:
     29 		ProxyCallbackClient(ListenSocket *l, int f, const sockaddrs &a) : Socket(f, l->IsIPv6()), ClientSocket(l, a), BufferedSocket()
     30 		{
     31 		}
     32 
     33 		void OnAccept() anope_override
     34 		{
     35 			this->Write(ProxyCheckString);
     36 		}
     37 
     38 		bool ProcessWrite() anope_override
     39 		{
     40 			return !BufferedSocket::ProcessWrite() || this->write_buffer.empty() ? false : true;
     41 		}
     42 	};
     43 
     44  public:
     45 	ProxyCallbackListener(const Anope::string &b, int p) : Socket(-1, b.find(':') != Anope::string::npos), ListenSocket(b, p, false)
     46 	{
     47 	}
     48 
     49 	ClientSocket *OnAccept(int fd, const sockaddrs &addr) anope_override
     50 	{
     51 		return new ProxyCallbackClient(this, fd, addr);
     52 	}
     53 };
     54 
     55 class ProxyConnect : public ConnectionSocket
     56 {
     57 	static ServiceReference<XLineManager> akills;
     58 
     59  public:
     60 	static std::set<ProxyConnect *> proxies;
     61 
     62 	ProxyCheck proxy;
     63 	unsigned short port;
     64 	time_t created;
     65 
     66 	ProxyConnect(ProxyCheck &p, unsigned short po) : Socket(-1), ConnectionSocket(), proxy(p),
     67 		port(po), created(Anope::CurTime)
     68 	{
     69 		proxies.insert(this);
     70 	}
     71 
     72 	~ProxyConnect()
     73 	{
     74 		proxies.erase(this);
     75 	}
     76 
     77 	virtual void OnConnect() anope_override = 0;
     78 	virtual const Anope::string GetType() const = 0;
     79 
     80  protected:
     81 	void Ban()
     82 	{
     83 		Anope::string reason = this->proxy.reason;
     84 
     85 		reason = reason.replace_all_cs("%t", this->GetType());
     86 		reason = reason.replace_all_cs("%i", this->conaddr.addr());
     87 		reason = reason.replace_all_cs("%p", stringify(this->conaddr.port()));
     88 
     89 		BotInfo *OperServ = Config->GetClient("OperServ");
     90 		Log(OperServ) << "PROXYSCAN: Open " << this->GetType() << " proxy found on " << this->conaddr.addr() << ":" << this->conaddr.port() << " (" << reason << ")";
     91 		XLine *x = new XLine("*@" + this->conaddr.addr(), OperServ ? OperServ->nick : "", Anope::CurTime + this->proxy.duration, reason, XLineManager::GenerateUID());
     92 		if (add_to_akill && akills)
     93 		{
     94 			akills->AddXLine(x);
     95 			akills->Send(NULL, x);
     96 		}
     97 		else
     98 		{
     99 			if (IRCD->CanSZLine)
    100 				IRCD->SendSZLine(NULL, x);
    101 			else
    102 				IRCD->SendAkill(NULL, x);
    103 			delete x;
    104 		}
    105 	}
    106 };
    107 ServiceReference<XLineManager> ProxyConnect::akills("XLineManager", "xlinemanager/sgline");
    108 std::set<ProxyConnect *> ProxyConnect::proxies;
    109 
    110 class HTTPProxyConnect : public ProxyConnect, public BufferedSocket
    111 {
    112  public:
    113 	HTTPProxyConnect(ProxyCheck &p, unsigned short po) : Socket(-1), ProxyConnect(p, po), BufferedSocket()
    114 	{
    115 	}
    116 
    117 	void OnConnect() anope_override
    118 	{
    119 		this->Write("CONNECT %s:%d HTTP/1.0", target_ip.c_str(), target_port);
    120 		this->Write("Content-Length: 0");
    121 		this->Write("Connection: close");
    122 		this->Write("");
    123 	}
    124 
    125 	const Anope::string GetType() const anope_override
    126 	{
    127 		return "HTTP";
    128 	}
    129 
    130 	bool ProcessRead() anope_override
    131 	{
    132 		bool b = BufferedSocket::ProcessRead();
    133 		if (this->GetLine() == ProxyCheckString)
    134 		{
    135 			this->Ban();
    136 			return false;
    137 		}
    138 		return b;
    139 	}
    140 };
    141 
    142 class SOCKS5ProxyConnect : public ProxyConnect, public BinarySocket
    143 {
    144  public:
    145 	SOCKS5ProxyConnect(ProxyCheck &p, unsigned short po) : Socket(-1), ProxyConnect(p, po), BinarySocket()
    146 	{
    147 	}
    148 
    149 	void OnConnect() anope_override
    150 	{
    151 		sockaddrs target_addr;
    152 		char buf[4 + sizeof(target_addr.sa4.sin_addr.s_addr) + sizeof(target_addr.sa4.sin_port)];
    153 		int ptr = 0;
    154 		target_addr.pton(AF_INET, target_ip, target_port);
    155 		if (!target_addr.valid())
    156 			return;
    157 
    158 		buf[ptr++] = 5; // Version
    159 		buf[ptr++] = 1; // # of methods
    160 		buf[ptr++] = 0; // No authentication
    161 
    162 		this->Write(buf, ptr);
    163 
    164 		ptr = 1;
    165 		buf[ptr++] = 1; // Connect
    166 		buf[ptr++] = 0; // Reserved
    167 		buf[ptr++] = 1; // IPv4
    168 		memcpy(buf + ptr, &target_addr.sa4.sin_addr.s_addr, sizeof(target_addr.sa4.sin_addr.s_addr));
    169 		ptr += sizeof(target_addr.sa4.sin_addr.s_addr);
    170 		memcpy(buf + ptr, &target_addr.sa4.sin_port, sizeof(target_addr.sa4.sin_port));
    171 		ptr += sizeof(target_addr.sa4.sin_port);
    172 
    173 		this->Write(buf, ptr);
    174 	}
    175 
    176 	const Anope::string GetType() const anope_override
    177 	{
    178 		return "SOCKS5";
    179 	}
    180 
    181 	bool Read(const char *buffer, size_t l) anope_override
    182 	{
    183 		if (l >= ProxyCheckString.length() && !strncmp(buffer, ProxyCheckString.c_str(), ProxyCheckString.length()))
    184 		{
    185 			this->Ban();
    186 			return false;
    187 		}
    188 		return true;
    189 	}
    190 };
    191 
    192 class ModuleProxyScan : public Module
    193 {
    194 	Anope::string listen_ip;
    195 	unsigned short listen_port;
    196 	Anope::string con_notice, con_source;
    197 	std::vector<ProxyCheck> proxyscans;
    198 
    199 	ProxyCallbackListener *listener;
    200 
    201 	class ConnectionTimeout : public Timer
    202 	{
    203 	 public:
    204 		ConnectionTimeout(Module *c, long timeout) : Timer(c, timeout, Anope::CurTime, true)
    205 		{
    206 		}
    207 
    208 		void Tick(time_t) anope_override
    209 		{
    210 			for (std::set<ProxyConnect *>::iterator it = ProxyConnect::proxies.begin(), it_end = ProxyConnect::proxies.end(); it != it_end;)
    211 			{
    212 				ProxyConnect *p = *it;
    213 				++it;
    214 
    215 				if (p->created + this->GetSecs() < Anope::CurTime)
    216 					delete p;
    217 			}
    218 		}
    219 	} connectionTimeout;
    220 
    221  public:
    222 	ModuleProxyScan(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, EXTRA | VENDOR),
    223 		connectionTimeout(this, 5)
    224 	{
    225 
    226 
    227 		this->listener = NULL;
    228 	}
    229 
    230 	~ModuleProxyScan()
    231 	{
    232 		for (std::set<ProxyConnect *>::iterator it = ProxyConnect::proxies.begin(), it_end = ProxyConnect::proxies.end(); it != it_end;)
    233 		{
    234 			ProxyConnect *p = *it;
    235 			++it;
    236 			delete p;
    237 		}
    238 
    239 		for (std::map<int, Socket *>::const_iterator it = SocketEngine::Sockets.begin(), it_end = SocketEngine::Sockets.end(); it != it_end;)
    240 		{
    241 			Socket *s = it->second;
    242 			++it;
    243 
    244 			ClientSocket *cs = dynamic_cast<ClientSocket *>(s);
    245 			if (cs != NULL && cs->ls == this->listener)
    246 				delete s;
    247 		}
    248 
    249 		delete this->listener;
    250 	}
    251 
    252 	void OnReload(Configuration::Conf *conf) anope_override
    253 	{
    254 		Configuration::Block *config = Config->GetModule(this);
    255 
    256 		Anope::string s_target_ip = config->Get<const Anope::string>("target_ip");
    257 		if (s_target_ip.empty())
    258 			throw ConfigException(this->name + " target_ip may not be empty");
    259 
    260 		int s_target_port = config->Get<int>("target_port", "-1");
    261 		if (s_target_port <= 0)
    262 			throw ConfigException(this->name + " target_port may not be empty and must be a positive number");
    263 
    264 		Anope::string s_listen_ip = config->Get<const Anope::string>("listen_ip");
    265 		if (s_listen_ip.empty())
    266 			throw ConfigException(this->name + " listen_ip may not be empty");
    267 
    268 		int s_listen_port = config->Get<int>("listen_port", "-1");
    269 		if (s_listen_port <= 0)
    270 			throw ConfigException(this->name + " listen_port may not be empty and must be a positive number");
    271 
    272 		target_ip = s_target_ip;
    273 		target_port = s_target_port;
    274 		this->listen_ip = s_listen_ip;
    275 		this->listen_port = s_listen_port;
    276 		this->con_notice = config->Get<const Anope::string>("connect_notice");
    277 		this->con_source = config->Get<const Anope::string>("connect_source");
    278 		add_to_akill = config->Get<bool>("add_to_akill", "true");
    279 		this->connectionTimeout.SetSecs(config->Get<time_t>("timeout", "5s"));
    280 
    281 		ProxyCheckString = Config->GetBlock("networkinfo")->Get<const Anope::string>("networkname") + " proxy check";
    282 		delete this->listener;
    283 		this->listener = NULL;
    284 		try
    285 		{
    286 			this->listener = new ProxyCallbackListener(this->listen_ip, this->listen_port);
    287 		}
    288 		catch (const SocketException &ex)
    289 		{
    290 			throw ConfigException("m_proxyscan: " + ex.GetReason());
    291 		}
    292 
    293 		this->proxyscans.clear();
    294 		for (int i = 0; i < config->CountBlock("proxyscan"); ++i)
    295 		{
    296 			Configuration::Block *block = config->GetBlock("proxyscan", i);
    297 			ProxyCheck p;
    298 			Anope::string token;
    299 
    300 			commasepstream sep(block->Get<const Anope::string>("type"));
    301 			while (sep.GetToken(token))
    302 			{
    303 				if (!token.equals_ci("HTTP") && !token.equals_ci("SOCKS5"))
    304 					continue;
    305 				p.types.insert(token);
    306 			}
    307 			if (p.types.empty())
    308 				continue;
    309 
    310 			commasepstream sep2(block->Get<const Anope::string>("port"));
    311 			while (sep2.GetToken(token))
    312 			{
    313 				try
    314 				{
    315 					unsigned short port = convertTo<unsigned short>(token);
    316 					p.ports.push_back(port);
    317 				}
    318 				catch (const ConvertException &) { }
    319 			}
    320 			if (p.ports.empty())
    321 				continue;
    322 
    323 			p.duration = block->Get<time_t>("time", "4h");
    324 			p.reason = block->Get<const Anope::string>("reason");
    325 			if (p.reason.empty())
    326 				continue;
    327 
    328 			this->proxyscans.push_back(p);
    329 		}
    330 	}
    331 
    332 	void OnUserConnect(User *user, bool &exempt) anope_override
    333 	{
    334 		if (exempt || user->Quitting() || !Me->IsSynced() || !user->server->IsSynced())
    335 			return;
    336 
    337 		/* At this time we only support IPv4 */
    338 		if (!user->ip.valid() || user->ip.sa.sa_family != AF_INET)
    339 			/* User doesn't have a valid IPv4 IP (ipv6/spoof/etc) */
    340 			return;
    341 
    342 		if (!this->con_notice.empty() && !this->con_source.empty())
    343 		{
    344 			BotInfo *bi = BotInfo::Find(this->con_source, true);
    345 			if (bi)
    346 				user->SendMessage(bi, this->con_notice);
    347 		}
    348 
    349 		for (unsigned i = this->proxyscans.size(); i > 0; --i)
    350 		{
    351 			ProxyCheck &p = this->proxyscans[i - 1];
    352 
    353 			for (std::set<Anope::string, ci::less>::iterator it = p.types.begin(), it_end = p.types.end(); it != it_end; ++it)
    354 			{
    355 				for (unsigned k = 0; k < p.ports.size(); ++k)
    356 				{
    357 					try
    358 					{
    359 						ProxyConnect *con = NULL;
    360 						if (it->equals_ci("HTTP"))
    361 							con = new HTTPProxyConnect(p, p.ports[k]);
    362 						else if (it->equals_ci("SOCKS5"))
    363 							con = new SOCKS5ProxyConnect(p, p.ports[k]);
    364 						else
    365 							continue;
    366 						con->Connect(user->ip.addr(), p.ports[k]);
    367 					}
    368 					catch (const SocketException &ex)
    369 					{
    370 						Log(LOG_DEBUG) << "m_proxyscan: " << ex.GetReason();
    371 					}
    372 				}
    373 			}
    374 		}
    375 	}
    376 };
    377 
    378 MODULE_INIT(ModuleProxyScan)