anope

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

os_dns.cpp (25198B)

      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 #include "modules/dns.h"
     11 
     12 static ServiceReference<DNS::Manager> dnsmanager("DNS::Manager", "dns/manager");
     13 
     14 struct DNSZone;
     15 class DNSServer;
     16 
     17 static Serialize::Checker<std::vector<DNSZone *> > zones("DNSZone");
     18 static Serialize::Checker<std::vector<DNSServer *> > dns_servers("DNSServer");
     19 
     20 static std::map<Anope::string, std::list<time_t> > server_quit_times;
     21 
     22 struct DNSZone : Serializable
     23 {
     24 	Anope::string name;
     25 	std::set<Anope::string, ci::less> servers;
     26 
     27 	DNSZone(const Anope::string &n) : Serializable("DNSZone"), name(n)
     28 	{
     29 		zones->push_back(this);
     30 	}
     31 
     32 	~DNSZone()
     33 	{
     34 		std::vector<DNSZone *>::iterator it = std::find(zones->begin(), zones->end(), this);
     35 		if (it != zones->end())
     36 			zones->erase(it);
     37 	}
     38 
     39 	void Serialize(Serialize::Data &data) const anope_override
     40 	{
     41 		data["name"] << name;
     42 		unsigned count = 0;
     43 		for (std::set<Anope::string, ci::less>::iterator it = servers.begin(), it_end = servers.end(); it != it_end; ++it)
     44 			data["server" + stringify(count++)] << *it;
     45 	}
     46 
     47 	static Serializable* Unserialize(Serializable *obj, Serialize::Data &data)
     48 	{
     49 		DNSZone *zone;
     50 		Anope::string zone_name;
     51 
     52 		data["name"] >> zone_name;
     53 
     54 		if (obj)
     55 		{
     56 			zone = anope_dynamic_static_cast<DNSZone *>(obj);
     57 			data["name"] >> zone->name;
     58 		}
     59 		else
     60 			zone = new DNSZone(zone_name);
     61 
     62 		zone->servers.clear();
     63 		for (unsigned count = 0; true; ++count)
     64 		{
     65 			Anope::string server_str;
     66 			data["server" + stringify(count)] >> server_str;
     67 			if (server_str.empty())
     68 				break;
     69 			zone->servers.insert(server_str);
     70 		}
     71 
     72 		return zone;
     73 	}
     74 
     75 	static DNSZone *Find(const Anope::string &name)
     76 	{
     77 		for (unsigned i = 0; i < zones->size(); ++i)
     78 			if (zones->at(i)->name.equals_ci(name))
     79 			{
     80 				DNSZone *z = zones->at(i);
     81 				z->QueueUpdate();
     82 				return z;
     83 			}
     84 		return NULL;
     85 	}
     86 };
     87 
     88 class DNSServer : public Serializable
     89 {
     90 	Anope::string server_name;
     91 	std::vector<Anope::string> ips;
     92 	unsigned limit;
     93 	/* wants to be in the pool */
     94 	bool pooled;
     95 	/* is actually in the pool */
     96 	bool active;
     97 
     98  public:
     99 	std::set<Anope::string, ci::less> zones;
    100 	time_t repool;
    101 
    102 	DNSServer(const Anope::string &sn) : Serializable("DNSServer"), server_name(sn), limit(0), pooled(false), active(false), repool(0)
    103 	{
    104 		dns_servers->push_back(this);
    105 	}
    106 
    107 	~DNSServer()
    108 	{
    109 		std::vector<DNSServer *>::iterator it = std::find(dns_servers->begin(), dns_servers->end(), this);
    110 		if (it != dns_servers->end())
    111 			dns_servers->erase(it);
    112 	}
    113 
    114 	const Anope::string &GetName() const { return server_name; }
    115 	std::vector<Anope::string> &GetIPs() { return ips; }
    116 	unsigned GetLimit() const { return limit; }
    117 	void SetLimit(unsigned l) { limit = l; }
    118 
    119 	bool Pooled() const { return pooled; }
    120 	void Pool(bool p)
    121 	{
    122 		if (!p)
    123 			this->SetActive(p);
    124 		pooled = p;
    125 	}
    126 
    127 	bool Active() const { return pooled && active; }
    128 	void SetActive(bool p)
    129 	{
    130 		if (p)
    131 			this->Pool(p);
    132 		active = p;
    133 
    134 		if (dnsmanager)
    135 		{
    136 			dnsmanager->UpdateSerial();
    137 			for (std::set<Anope::string, ci::less>::iterator it = zones.begin(), it_end = zones.end(); it != it_end; ++it)
    138 				dnsmanager->Notify(*it);
    139 		}
    140 	}
    141 
    142 	void Serialize(Serialize::Data &data) const anope_override
    143 	{
    144 		data["server_name"] << server_name;
    145 		for (unsigned i = 0; i < ips.size(); ++i)
    146 			data["ip" + stringify(i)] << ips[i];
    147 		data["limit"] << limit;
    148 		data["pooled"] << pooled;
    149 		unsigned count = 0;
    150 		for (std::set<Anope::string, ci::less>::iterator it = zones.begin(), it_end = zones.end(); it != it_end; ++it)
    151 			data["zone" + stringify(count++)] << *it;
    152 	}
    153 
    154 	static Serializable* Unserialize(Serializable *obj, Serialize::Data &data)
    155 	{
    156 		DNSServer *req;
    157 		Anope::string server_name;
    158 
    159 		data["server_name"] >> server_name;
    160 
    161 		if (obj)
    162 		{
    163 			req = anope_dynamic_static_cast<DNSServer *>(obj);
    164 			req->server_name = server_name;
    165 		}
    166 		else
    167 			req = new DNSServer(server_name);
    168 
    169 		for (unsigned i = 0; true; ++i)
    170 		{
    171 			Anope::string ip_str;
    172 			data["ip" + stringify(i)] >> ip_str;
    173 			if (ip_str.empty())
    174 				break;
    175 			req->ips.push_back(ip_str);
    176 		}
    177 
    178 		data["limit"] >> req->limit;
    179 		data["pooled"] >> req->pooled;
    180 
    181 		req->zones.clear();
    182 		for (unsigned i = 0; true; ++i)
    183 		{
    184 			Anope::string zone_str;
    185 			data["zone" + stringify(i)] >> zone_str;
    186 			if (zone_str.empty())
    187 				break;
    188 			req->zones.insert(zone_str);
    189 		}
    190 
    191 		return req;
    192 	}
    193 
    194 	static DNSServer *Find(const Anope::string &s)
    195 	{
    196 		for (unsigned i = 0; i < dns_servers->size(); ++i)
    197 			if (dns_servers->at(i)->GetName().equals_ci(s))
    198 			{
    199 				DNSServer *serv = dns_servers->at(i);
    200 				serv->QueueUpdate();
    201 				return serv;
    202 			}
    203 		return NULL;
    204 	}
    205 };
    206 
    207 class CommandOSDNS : public Command
    208 {
    209 	void DisplayPoolState(CommandSource &source)
    210 	{
    211 		if (dns_servers->empty())
    212 		{
    213 			source.Reply(_("There are no configured servers."));
    214 			return;
    215 		}
    216 
    217 		ListFormatter lf(source.GetAccount());
    218 		lf.AddColumn(_("Server")).AddColumn(_("IP")).AddColumn(_("Limit")).AddColumn(_("State"));
    219 		for (unsigned i = 0; i < dns_servers->size(); ++i)
    220 		{
    221 			DNSServer *s = dns_servers->at(i);
    222 			Server *srv = Server::Find(s->GetName(), true);
    223 
    224 			ListFormatter::ListEntry entry;
    225 			entry["Server"] = s->GetName();
    226 			entry["Limit"] = s->GetLimit() ? stringify(s->GetLimit()) : Language::Translate(source.GetAccount(), _("None"));
    227 
    228 			Anope::string ip_str;
    229 			for (unsigned j = 0; j < s->GetIPs().size(); ++j)
    230 				ip_str += s->GetIPs()[j] + " ";
    231 			ip_str.trim();
    232 			if (ip_str.empty())
    233 				ip_str = "None";
    234 			entry["IP"] = ip_str;
    235 
    236 			if (s->Active())
    237 				entry["State"] = Language::Translate(source.GetAccount(), _("Pooled/Active"));
    238 			else if (s->Pooled())
    239 				entry["State"] = Language::Translate(source.GetAccount(), _("Pooled/Not Active"));
    240 			else
    241 				entry["State"] = Language::Translate(source.GetAccount(), _("Unpooled"));
    242 
    243 			if (!srv)
    244 				entry["State"] += Anope::string(" ") + Language::Translate(source.GetAccount(), _("(Split)"));
    245 
    246 			lf.AddEntry(entry);
    247 		}
    248 
    249 		std::vector<Anope::string> replies;
    250 		lf.Process(replies);
    251 
    252 		if (!zones->empty())
    253 		{
    254 			ListFormatter lf2(source.GetAccount());
    255 			lf2.AddColumn(_("Zone")).AddColumn(_("Servers"));
    256 
    257 			for (unsigned i = 0; i < zones->size(); ++i)
    258 			{
    259 				const DNSZone *z = zones->at(i);
    260 
    261 				ListFormatter::ListEntry entry;
    262 				entry["Zone"] = z->name;
    263 
    264 				Anope::string server_str;
    265 				for (std::set<Anope::string, ci::less>::iterator it = z->servers.begin(), it_end = z->servers.end(); it != it_end; ++it)
    266 					server_str += *it + " ";
    267 				server_str.trim();
    268 
    269 				if (server_str.empty())
    270 					server_str = "None";
    271 
    272 				entry["Servers"] = server_str;
    273 
    274 				lf2.AddEntry(entry);
    275 			}
    276 
    277 			lf2.Process(replies);
    278 		}
    279 
    280 		for (unsigned i = 0; i < replies.size(); ++i)
    281 			source.Reply(replies[i]);
    282 	}
    283 
    284 	void AddZone(CommandSource &source, const std::vector<Anope::string> &params)
    285 	{
    286 		const Anope::string &zone = params[1];
    287 
    288 		if (DNSZone::Find(zone))
    289 		{
    290 			source.Reply(_("Zone %s already exists."), zone.c_str());
    291 			return;
    292 		}
    293 
    294 		if (Anope::ReadOnly)
    295 			source.Reply(READ_ONLY_MODE);
    296 
    297 		Log(LOG_ADMIN, source, this) << "to add zone " << zone;
    298 
    299 		new DNSZone(zone);
    300 		source.Reply(_("Added zone %s."), zone.c_str());
    301 	}
    302 
    303 	void DelZone(CommandSource &source, const std::vector<Anope::string> &params)
    304 	{
    305 		const Anope::string &zone = params[1];
    306 
    307 		DNSZone *z = DNSZone::Find(zone);
    308 		if (!z)
    309 		{
    310 			source.Reply(_("Zone %s does not exist."), zone.c_str());
    311 			return;
    312 		}
    313 
    314 		if (Anope::ReadOnly)
    315 			source.Reply(READ_ONLY_MODE);
    316 
    317 		Log(LOG_ADMIN, source, this) << "to delete zone " << z->name;
    318 
    319 		for (std::set<Anope::string, ci::less>::iterator it = z->servers.begin(), it_end = z->servers.end(); it != it_end; ++it)
    320 		{
    321 			DNSServer *s = DNSServer::Find(*it);
    322 			if (s)
    323 				s->zones.erase(z->name);
    324 		}
    325 
    326 		if (dnsmanager)
    327 		{
    328 			dnsmanager->UpdateSerial();
    329 			dnsmanager->Notify(z->name);
    330 		}
    331 
    332 		source.Reply(_("Zone %s removed."), z->name.c_str());
    333 		delete z;
    334 	}
    335 
    336 	void AddServer(CommandSource &source, const std::vector<Anope::string> &params)
    337 	{
    338 		DNSServer *s = DNSServer::Find(params[1]);
    339 		const Anope::string &zone = params.size() > 2 ? params[2] : "";
    340 
    341 		if (s)
    342 		{
    343 			if (zone.empty())
    344 			{
    345 				source.Reply(_("Server %s already exists."), s->GetName().c_str());
    346 			}
    347 			else
    348 			{
    349 				DNSZone *z = DNSZone::Find(zone);
    350 				if (!z)
    351 				{
    352 					source.Reply(_("Zone %s does not exist."), zone.c_str());
    353 					return;
    354 				}
    355 				else if (z->servers.count(s->GetName()))
    356 				{
    357 					source.Reply(_("Server %s is already in zone %s."), s->GetName().c_str(), z->name.c_str());
    358 					return;
    359 				}
    360 
    361 				if (Anope::ReadOnly)
    362 					source.Reply(READ_ONLY_MODE);
    363 
    364 				z->servers.insert(s->GetName());
    365 				s->zones.insert(zone);
    366 
    367 				if (dnsmanager)
    368 				{
    369 					dnsmanager->UpdateSerial();
    370 					dnsmanager->Notify(zone);
    371 				}
    372 
    373 				Log(LOG_ADMIN, source, this) << "to add server " << s->GetName() << " to zone " << z->name;
    374 
    375 				source.Reply(_("Server %s added to zone %s."), s->GetName().c_str(), z->name.c_str());
    376 			}
    377 
    378 			return;
    379 		}
    380 
    381 		Server *serv = Server::Find(params[1], true);
    382 		if (!serv || serv == Me || serv->IsJuped())
    383 		{
    384 			source.Reply(_("Server %s is not linked to the network."), params[1].c_str());
    385 			return;
    386 		}
    387 
    388 		s = new DNSServer(params[1]);
    389 		if (zone.empty())
    390 		{
    391 			if (Anope::ReadOnly)
    392 				source.Reply(READ_ONLY_MODE);
    393 
    394 			Log(LOG_ADMIN, source, this) << "to add server " << s->GetName();
    395 			source.Reply(_("Added server %s."), s->GetName().c_str());
    396 		}
    397 		else
    398 		{
    399 			DNSZone *z = DNSZone::Find(zone);
    400 			if (!z)
    401 			{
    402 				source.Reply(_("Zone %s does not exist."), zone.c_str());
    403 				delete s;
    404 				return;
    405 			}
    406 
    407 			if (Anope::ReadOnly)
    408 				source.Reply(READ_ONLY_MODE);
    409 
    410 			Log(LOG_ADMIN, source, this) << "to add server " << s->GetName() << " to zone " << zone;
    411 
    412 			z->servers.insert(s->GetName());
    413 			s->zones.insert(z->name);
    414 
    415 			if (dnsmanager)
    416 			{
    417 				dnsmanager->UpdateSerial();
    418 				dnsmanager->Notify(z->name);
    419 			}
    420 		}
    421 	}
    422 
    423 	void DelServer(CommandSource &source, const std::vector<Anope::string> &params)
    424 	{
    425 		DNSServer *s = DNSServer::Find(params[1]);
    426 		const Anope::string &zone = params.size() > 2 ? params[2] : "";
    427 
    428 		if (!s)
    429 		{
    430 			source.Reply(_("Server %s does not exist."), params[1].c_str());
    431 			return;
    432 		}
    433 		else if (!zone.empty())
    434 		{
    435 			DNSZone *z = DNSZone::Find(zone);
    436 			if (!z)
    437 			{
    438 				source.Reply(_("Zone %s does not exist."), zone.c_str());
    439 				return;
    440 			}
    441 			else if (!z->servers.count(s->GetName()))
    442 			{
    443 				source.Reply(_("Server %s is not in zone %s."), s->GetName().c_str(), z->name.c_str());
    444 				return;
    445 			}
    446 
    447 			if (Anope::ReadOnly)
    448 				source.Reply(READ_ONLY_MODE);
    449 
    450 			Log(LOG_ADMIN, source, this) << "to remove server " << s->GetName() << " from zone " << z->name;
    451 
    452 			if (dnsmanager)
    453 			{
    454 				dnsmanager->UpdateSerial();
    455 				dnsmanager->Notify(z->name);
    456 			}
    457 
    458 			z->servers.erase(s->GetName());
    459 			s->zones.erase(z->name);
    460 			source.Reply(_("Removed server %s from zone %s."), s->GetName().c_str(), z->name.c_str());
    461 			return;
    462 		}
    463 		else if (Server::Find(s->GetName(), true))
    464 		{
    465 			source.Reply(_("Server %s must be quit before it can be deleted."), s->GetName().c_str());
    466 			return;
    467 		}
    468 
    469 		for (std::set<Anope::string, ci::less>::iterator it = s->zones.begin(), it_end = s->zones.end(); it != it_end; ++it)
    470 		{
    471 			DNSZone *z = DNSZone::Find(*it);
    472 			if (z)
    473 				z->servers.erase(s->GetName());
    474 		}
    475 
    476 		if (Anope::ReadOnly)
    477 			source.Reply(READ_ONLY_MODE);
    478 
    479 		if (dnsmanager)
    480 			dnsmanager->UpdateSerial();
    481 
    482 		Log(LOG_ADMIN, source, this) << "to delete server " << s->GetName();
    483 		source.Reply(_("Removed server %s."), s->GetName().c_str());
    484 		delete s;
    485 	}
    486 
    487 	void AddIP(CommandSource &source, const std::vector<Anope::string> &params)
    488 	{
    489 		DNSServer *s = DNSServer::Find(params[1]);
    490 
    491 		if (!s)
    492 		{
    493 			source.Reply(_("Server %s does not exist."), params[1].c_str());
    494 			return;
    495 		}
    496 
    497 		for (unsigned i = 0; i < s->GetIPs().size(); ++i)
    498 			if (params[2].equals_ci(s->GetIPs()[i]))
    499 			{
    500 				source.Reply(_("IP %s already exists for %s."), s->GetIPs()[i].c_str(), s->GetName().c_str());
    501 				return;
    502 			}
    503 
    504 		sockaddrs addr(params[2]);
    505 		if (!addr.valid())
    506 		{
    507 			source.Reply(_("%s is not a valid IP address."), params[2].c_str());
    508 			return;
    509 		}
    510 
    511 		if (Anope::ReadOnly)
    512 			source.Reply(READ_ONLY_MODE);
    513 
    514 		s->GetIPs().push_back(params[2]);
    515 		source.Reply(_("Added IP %s to %s."), params[2].c_str(), s->GetName().c_str());
    516 		Log(LOG_ADMIN, source, this) << "to add IP " << params[2] << " to " << s->GetName();
    517 
    518 		if (s->Active() && dnsmanager)
    519 		{
    520 			dnsmanager->UpdateSerial();
    521 			for (std::set<Anope::string, ci::less>::iterator it = s->zones.begin(), it_end = s->zones.end(); it != it_end; ++it)
    522 				dnsmanager->Notify(*it);
    523 		}
    524 	}
    525 
    526 	void DelIP(CommandSource &source, const std::vector<Anope::string> &params)
    527 	{
    528 		DNSServer *s = DNSServer::Find(params[1]);
    529 
    530 		if (!s)
    531 		{
    532 			source.Reply(_("Server %s does not exist."), params[1].c_str());
    533 			return;
    534 		}
    535 
    536 		if (Anope::ReadOnly)
    537 			source.Reply(READ_ONLY_MODE);
    538 
    539 		for (unsigned i = 0; i < s->GetIPs().size(); ++i)
    540 			if (params[2].equals_ci(s->GetIPs()[i]))
    541 			{
    542 				s->GetIPs().erase(s->GetIPs().begin() + i);
    543 				source.Reply(_("Removed IP %s from %s."), params[2].c_str(), s->GetName().c_str());
    544 				Log(LOG_ADMIN, source, this) << "to remove IP " << params[2] << " from " << s->GetName();
    545 
    546 				if (s->GetIPs().empty())
    547 				{
    548 					s->repool = 0;
    549 					s->Pool(false);
    550 				}
    551 
    552 				if (s->Active() && dnsmanager)
    553 				{
    554 					dnsmanager->UpdateSerial();
    555 					for (std::set<Anope::string, ci::less>::iterator it = s->zones.begin(), it_end = s->zones.end(); it != it_end; ++it)
    556 						dnsmanager->Notify(*it);
    557 				}
    558 
    559 				return;
    560 			}
    561 
    562 		source.Reply(_("IP %s does not exist for %s."), params[2].c_str(), s->GetName().c_str());
    563 	}
    564 
    565 	void OnSet(CommandSource &source, const std::vector<Anope::string> &params)
    566 	{
    567 		DNSServer *s = DNSServer::Find(params[1]);
    568 
    569 		if (!s)
    570 		{
    571 			source.Reply(_("Server %s does not exist."), params[1].c_str());
    572 			return;
    573 		}
    574 
    575 		if (Anope::ReadOnly)
    576 			source.Reply(READ_ONLY_MODE);
    577 
    578 		if (params[2].equals_ci("LIMIT"))
    579 		{
    580 			try
    581 			{
    582 				unsigned l = convertTo<unsigned>(params[3]);
    583 				s->SetLimit(l);
    584 				if (l)
    585 					source.Reply(_("User limit for %s set to %d."), s->GetName().c_str(), l);
    586 				else
    587 					source.Reply(_("User limit for %s removed."), s->GetName().c_str());
    588 			}
    589 			catch (const ConvertException &ex)
    590 			{
    591 				source.Reply(_("Invalid value for LIMIT. Must be numerical."));
    592 			}
    593 		}
    594 		else
    595 			source.Reply(_("Unknown SET option."));
    596 	}
    597 
    598 	void OnPool(CommandSource &source, const std::vector<Anope::string> &params)
    599 	{
    600 		DNSServer *s = DNSServer::Find(params[1]);
    601 
    602 		if (!s)
    603 		{
    604 			source.Reply(_("Server %s does not exist."), params[1].c_str());
    605 			return;
    606 		}
    607 		else if (!Server::Find(s->GetName(), true))
    608 		{
    609 			source.Reply(_("Server %s is not currently linked."), s->GetName().c_str());
    610 			return;
    611 		}
    612 		else if (s->Pooled())
    613 		{
    614 			source.Reply(_("Server %s is already pooled."), s->GetName().c_str());
    615 			return;
    616 		}
    617 		else if (s->GetIPs().empty())
    618 		{
    619 			source.Reply(_("Server %s has no configured IPs."), s->GetName().c_str());
    620 			return;
    621 		}
    622 
    623 		if (Anope::ReadOnly)
    624 			source.Reply(READ_ONLY_MODE);
    625 
    626 		s->SetActive(true);
    627 
    628 		source.Reply(_("Pooled %s."), s->GetName().c_str());
    629 		Log(LOG_ADMIN, source, this) << "to pool " << s->GetName();
    630 	}
    631 
    632 
    633 	void OnDepool(CommandSource &source, const std::vector<Anope::string> &params)
    634 	{
    635 		DNSServer *s = DNSServer::Find(params[1]);
    636 
    637 		if (!s)
    638 		{
    639 			source.Reply(_("Server %s does not exist."), params[1].c_str());
    640 			return;
    641 		}
    642 		else if (!s->Pooled())
    643 		{
    644 			source.Reply(_("Server %s is not pooled."), s->GetName().c_str());
    645 			return;
    646 		}
    647 
    648 		if (Anope::ReadOnly)
    649 			source.Reply(READ_ONLY_MODE);
    650 
    651 		s->Pool(false);
    652 
    653 		source.Reply(_("Depooled %s."), s->GetName().c_str());
    654 		Log(LOG_ADMIN, source, this) << "to depool " << s->GetName();
    655 	}
    656 
    657  public:
    658 	CommandOSDNS(Module *creator) : Command(creator, "operserv/dns", 0, 4)
    659 	{
    660 		this->SetDesc(_("Manage DNS zones for this network"));
    661 		this->SetSyntax(_("ADDZONE \037zone.name\037"));
    662 		this->SetSyntax(_("DELZONE \037zone.name\037"));
    663 		this->SetSyntax(_("ADDSERVER \037server.name\037 [\037zone.name\037]"));
    664 		this->SetSyntax(_("DELSERVER \037server.name\037 [\037zone.name\037]"));
    665 		this->SetSyntax(_("ADDIP \037server.name\037 \037ip\037"));
    666 		this->SetSyntax(_("DELIP \037server.name\037 \037ip\037"));
    667 		this->SetSyntax(_("SET \037server.name\037 \037option\037 \037value\037"));
    668 		this->SetSyntax(_("POOL \037server.name\037"));
    669 		this->SetSyntax(_("DEPOOL \037server.name\037"));
    670 	}
    671 
    672 	void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
    673 	{
    674 		if (params.empty())
    675 			this->DisplayPoolState(source);
    676 		else if (params[0].equals_ci("ADDZONE") && params.size() > 1)
    677 			this->AddZone(source, params);
    678 		else if (params[0].equals_ci("DELZONE") && params.size() > 1)
    679 			this->DelZone(source, params);
    680 		else if (params[0].equals_ci("ADDSERVER") && params.size() > 1)
    681 			this->AddServer(source, params);
    682 		else if (params[0].equals_ci("DELSERVER") && params.size() > 1)
    683 			this->DelServer(source, params);
    684 		else if (params[0].equals_ci("ADDIP") && params.size() > 2)
    685 			this->AddIP(source, params);
    686 		else if (params[0].equals_ci("DELIP") && params.size() > 2)
    687 			this->DelIP(source, params);
    688 		else if (params[0].equals_ci("SET") && params.size() > 3)
    689 			this->OnSet(source, params);
    690 		else if (params[0].equals_ci("POOL") && params.size() > 1)
    691 			this->OnPool(source, params);
    692 		else if (params[0].equals_ci("DEPOOL") && params.size() > 1)
    693 			this->OnDepool(source, params);
    694 		else
    695 			this->OnSyntaxError(source, "");
    696 	}
    697 
    698 	bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
    699 	{
    700 		this->SendSyntax(source);
    701 		source.Reply(" ");
    702 		source.Reply(_("This command allows managing DNS zones used for controlling what servers users\n"
    703 				"are directed to when connecting. Omitting all parameters prints out the status of\n"
    704 				"the DNS zone.\n"
    705 				" \n"
    706 				"\002ADDZONE\002 adds a zone, eg us.yournetwork.tld. Servers can then be added to this\n"
    707 				"zone with the \002ADDSERVER\002 command.\n"
    708 				" \n"
    709 				"The \002ADDSERVER\002 command adds a server to the given zone. When a query is done, the\n"
    710 				"zone in question is served if it exists, else all servers in all zones are served.\n"
    711 				"A server may be in more than one zone.\n"
    712 				" \n"
    713 				"The \002ADDIP\002 command associates an IP with a server.\n"
    714 				" \n"
    715 				"The \002POOL\002 and \002DEPOOL\002 commands actually add and remove servers to their given zones."));
    716 		return true;
    717 	}
    718 };
    719 
    720 class ModuleDNS : public Module
    721 {
    722 	Serialize::Type zone_type, dns_type;
    723 	CommandOSDNS commandosdns;
    724 
    725 	time_t ttl;
    726 	int user_drop_mark;
    727 	time_t user_drop_time;
    728 	time_t user_drop_readd_time;
    729 	bool remove_split_servers;
    730 	bool readd_connected_servers;
    731 
    732 	time_t last_warn;
    733 
    734  public:
    735 	ModuleDNS(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, EXTRA | VENDOR),
    736 		zone_type("DNSZone", DNSZone::Unserialize), dns_type("DNSServer", DNSServer::Unserialize), commandosdns(this),
    737 		last_warn(0)
    738 	{
    739 
    740 
    741 		for (unsigned j = 0; j < dns_servers->size(); ++j)
    742 		{
    743 			DNSServer *s = dns_servers->at(j);
    744 			if (s->Pooled() && Server::Find(s->GetName(), true))
    745 				s->SetActive(true);
    746 		}
    747 	}
    748 
    749 	~ModuleDNS()
    750 	{
    751 		for (unsigned i = zones->size(); i > 0; --i)
    752 			delete zones->at(i - 1);
    753 		for (unsigned i = dns_servers->size(); i > 0; --i)
    754 			delete dns_servers->at(i - 1);
    755 	}
    756 
    757 	void OnReload(Configuration::Conf *conf) anope_override
    758 	{
    759 		Configuration::Block *block = conf->GetModule(this);
    760 		this->ttl = block->Get<time_t>("ttl");
    761 		this->user_drop_mark =  block->Get<int>("user_drop_mark");
    762 		this->user_drop_time = block->Get<time_t>("user_drop_time");
    763 		this->user_drop_readd_time = block->Get<time_t>("user_drop_readd_time");
    764 		this->remove_split_servers = block->Get<bool>("remove_split_servers");
    765 		this->readd_connected_servers = block->Get<bool>("readd_connected_servers");
    766 	}
    767 
    768 	void OnNewServer(Server *s) anope_override
    769 	{
    770 		if (s == Me || s->IsJuped())
    771 			return;
    772 		if (!Me->IsSynced() || this->readd_connected_servers)
    773 		{
    774 			DNSServer *dns = DNSServer::Find(s->GetName());
    775 			if (dns && dns->Pooled() && !dns->Active() && !dns->GetIPs().empty())
    776 			{
    777 				dns->SetActive(true);
    778 				Log(this) << "Pooling server " << s->GetName();
    779 			}
    780 		}
    781 	}
    782 
    783 	void OnServerQuit(Server *s) anope_override
    784 	{
    785 		DNSServer *dns = DNSServer::Find(s->GetName());
    786 		if (remove_split_servers && dns && dns->Pooled() && dns->Active())
    787 		{
    788 			if (readd_connected_servers)
    789 				dns->SetActive(false); // Will be reactivated when it comes back
    790 			else
    791 				dns->Pool(false); // Otherwise permanently pull this
    792 			Log(this) << "Depooling delinked server " << s->GetName();
    793 		}
    794 	}
    795 
    796 	void OnUserConnect(User *u, bool &exempt) anope_override
    797 	{
    798 		if (!u->Quitting() && u->server)
    799 		{
    800 			DNSServer *s = DNSServer::Find(u->server->GetName());
    801 			/* Check for user limit reached */
    802 			if (s && s->Pooled() && s->Active() && s->GetLimit() && u->server->users >= s->GetLimit())
    803 			{
    804 				Log(this) << "Depooling full server " << s->GetName() << ": " << u->server->users << " users";
    805 				s->SetActive(false);
    806 			}
    807 		}
    808 	}
    809 
    810 	void OnPreUserLogoff(User *u) anope_override
    811 	{
    812 		if (u && u->server)
    813 		{
    814 			DNSServer *s = DNSServer::Find(u->server->GetName());
    815 			if (!s || !s->Pooled())
    816 				return;
    817 
    818 			/* Check for dropping under userlimit */
    819 			if (s->GetLimit() && !s->Active() && s->GetLimit() > u->server->users)
    820 			{
    821 				Log(this) << "Pooling server " << s->GetName();
    822 				s->SetActive(true);
    823 			}
    824 
    825 			if (this->user_drop_mark > 0)
    826 			{
    827 				std::list<time_t>& times = server_quit_times[u->server->GetName()];
    828 				times.push_back(Anope::CurTime);
    829 				if (times.size() > static_cast<unsigned>(this->user_drop_mark))
    830 					times.pop_front();
    831 
    832 				if (times.size() == static_cast<unsigned>(this->user_drop_mark))
    833 				{
    834 					time_t diff = Anope::CurTime - *times.begin();
    835 
    836 					/* Check for very fast user drops */
    837 					if (s->Active() && diff <= this->user_drop_time)
    838 					{
    839 						Log(this) << "Depooling server " << s->GetName() << ": dropped " << this->user_drop_mark << " users in " << diff << " seconds";
    840 						s->repool = Anope::CurTime + this->user_drop_readd_time;
    841 						s->SetActive(false);
    842 					}
    843 					/* Check for needing to re-pool a server that dropped users */
    844 					else if (!s->Active() && s->repool && s->repool <= Anope::CurTime)
    845 					{
    846 						s->SetActive(true);
    847 						s->repool = 0;
    848 						Log(this) << "Pooling server " << s->GetName();
    849 					}
    850 				}
    851 			}
    852 		}
    853 	}
    854 
    855 	void OnDnsRequest(DNS::Query &req, DNS::Query *packet) anope_override
    856 	{
    857 		if (req.questions.empty())
    858 			return;
    859 		/* Currently we reply to any QR for A/AAAA */
    860 		const DNS::Question& q = req.questions[0];
    861 		if (q.type != DNS::QUERY_A && q.type != DNS::QUERY_AAAA && q.type != DNS::QUERY_AXFR && q.type != DNS::QUERY_ANY)
    862 			return;
    863 
    864 		DNSZone *zone = DNSZone::Find(q.name);
    865 		size_t answer_size = packet->answers.size();
    866 		if (zone)
    867 		{
    868 			for (std::set<Anope::string, ci::less>::iterator it = zone->servers.begin(), it_end = zone->servers.end(); it != it_end; ++it)
    869 			{
    870 				DNSServer *s = DNSServer::Find(*it);
    871 				if (!s || !s->Active())
    872 					continue;
    873 
    874 				for (unsigned j = 0; j < s->GetIPs().size(); ++j)
    875 				{
    876 					DNS::QueryType q_type = s->GetIPs()[j].find(':') != Anope::string::npos ? DNS::QUERY_AAAA : DNS::QUERY_A;
    877 
    878 					if (q.type == DNS::QUERY_AXFR || q.type == DNS::QUERY_ANY || q_type == q.type)
    879 					{
    880 						DNS::ResourceRecord rr(q.name, q_type);
    881 						rr.ttl = this->ttl;
    882 						rr.rdata = s->GetIPs()[j];
    883 						packet->answers.push_back(rr);
    884 					}
    885 				}
    886 			}
    887 		}
    888 
    889 		if (packet->answers.size() == answer_size)
    890 		{
    891 			/* Default zone */
    892 			for (unsigned i = 0; i < dns_servers->size(); ++i)
    893 			{
    894 				DNSServer *s = dns_servers->at(i);
    895 				if (!s->Active())
    896 					continue;
    897 
    898 				for (unsigned j = 0; j < s->GetIPs().size(); ++j)
    899 				{
    900 					DNS::QueryType q_type = s->GetIPs()[j].find(':') != Anope::string::npos ? DNS::QUERY_AAAA : DNS::QUERY_A;
    901 
    902 					if (q.type == DNS::QUERY_AXFR || q.type == DNS::QUERY_ANY || q_type == q.type)
    903 					{
    904 						DNS::ResourceRecord rr(q.name, q_type);
    905 						rr.ttl = this->ttl;
    906 						rr.rdata = s->GetIPs()[j];
    907 						packet->answers.push_back(rr);
    908 					}
    909 				}
    910 			}
    911 		}
    912 
    913 		if (packet->answers.size() == answer_size)
    914 		{
    915 			if (last_warn + 60 < Anope::CurTime)
    916 			{
    917 				last_warn = Anope::CurTime;
    918 				Log(this) << "Warning! There are no pooled servers!";
    919 			}
    920 
    921 			/* Something messed up, just return them all and hope one is available */
    922 			for (unsigned i = 0; i < dns_servers->size(); ++i)
    923 			{
    924 				DNSServer *s = dns_servers->at(i);
    925 
    926 				for (unsigned j = 0; j < s->GetIPs().size(); ++j)
    927 				{
    928 					DNS::QueryType q_type = s->GetIPs()[j].find(':') != Anope::string::npos ? DNS::QUERY_AAAA : DNS::QUERY_A;
    929 
    930 					if (q.type == DNS::QUERY_AXFR || q.type == DNS::QUERY_ANY || q_type == q.type)
    931 					{
    932 						DNS::ResourceRecord rr(q.name, q_type);
    933 						rr.ttl = this->ttl;
    934 						rr.rdata = s->GetIPs()[j];
    935 						packet->answers.push_back(rr);
    936 					}
    937 				}
    938 			}
    939 
    940 			if (packet->answers.size() == answer_size)
    941 			{
    942 				Log(this) << "Error! There are no servers with any IPs of type " << q.type;
    943 			}
    944 		}
    945 	}
    946 };
    947 
    948 MODULE_INIT(ModuleDNS)