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)