diff options
Diffstat (limited to 'modules/commands/os_dns.cpp')
-rw-r--r-- | modules/commands/os_dns.cpp | 401 |
1 files changed, 338 insertions, 63 deletions
diff --git a/modules/commands/os_dns.cpp b/modules/commands/os_dns.cpp index ec72f5a5d..cc1928b64 100644 --- a/modules/commands/os_dns.cpp +++ b/modules/commands/os_dns.cpp @@ -10,23 +10,91 @@ static ServiceReference<DNS::Manager> dnsmanager("DNS::Manager", "dns/manager"); +class DNSZone; class DNSServer; + +static std::vector<DNSZone *> zones; static std::vector<DNSServer *> dns_servers; static std::map<Anope::string, std::list<time_t> > server_quit_times; +struct DNSZone : Serializable +{ + Anope::string name; + std::set<Anope::string, ci::less> servers; + + DNSZone(const Anope::string &n) : Serializable("DNSZone"), name(n) + { + zones.push_back(this); + } + + ~DNSZone() + { + std::vector<DNSZone *>::iterator it = std::find(zones.begin(), zones.end(), this); + if (it != zones.end()) + zones.erase(it); + } + + void Serialize(Serialize::Data &data) const anope_override + { + data["name"] << name; + unsigned count = 0; + for (std::set<Anope::string, ci::less>::iterator it = servers.begin(), it_end = servers.end(); it != it_end; ++it) + data["server" + stringify(count++)] << *it; + } + + static Serializable* Unserialize(Serializable *obj, Serialize::Data &data) + { + DNSZone *zone; + Anope::string zone_name; + + data["name"] >> zone_name; + + if (obj) + { + zone = anope_dynamic_static_cast<DNSZone *>(obj); + data["name"] >> zone->name; + } + else + zone = new DNSZone(zone_name); + + zone->servers.clear(); + for (unsigned count = 0; true; ++count) + { + Anope::string server_str; + data["server" + stringify(count++)] >> server_str; + if (server_str.empty()) + break; + zone->servers.insert(server_str); + } + + return zone; + } + + static DNSZone *Find(const Anope::string &name) + { + for (unsigned i = 0; i < zones.size(); ++i) + if (zones[i]->name.equals_ci(name)) + return zones[i]; + return NULL; + } +}; + class DNSServer : public Serializable { Anope::string server_name; std::vector<Anope::string> ips; unsigned limit; + /* wants to be in the pool */ bool pooled; + /* is actually in the pool */ + bool active; - DNSServer() : Serializable("DNSServer"), limit(0), pooled(false), repool(0) { dns_servers.push_back(this); } public: + std::set<Anope::string, ci::less> zones; time_t repool; - DNSServer(const Anope::string &sn) : Serializable("DNSServer"), server_name(sn), limit(0), pooled(false), repool(0) + DNSServer(const Anope::string &sn) : Serializable("DNSServer"), server_name(sn), limit(0), pooled(false), active(false), repool(0) { dns_servers.push_back(this); } @@ -42,16 +110,19 @@ class DNSServer : public Serializable std::vector<Anope::string> &GetIPs() { return ips; } unsigned GetLimit() const { return limit; } void SetLimit(unsigned l) { limit = l; } + bool Pooled() const { return pooled; } + void Pool(bool p) { pooled = p; } - void Pool(bool p) + bool Active() const { return pooled && active; } + void SetActive(bool p) { - pooled = p; + this->Pool(p); + active = p; if (dnsmanager) dnsmanager->UpdateSerial(); } - void Serialize(Serialize::Data &data) const anope_override { data["server_name"] << server_name; @@ -59,18 +130,25 @@ class DNSServer : public Serializable data["ip" + stringify(i)] << ips[i]; data["limit"] << limit; data["pooled"] << pooled; + unsigned count = 0; + for (std::set<Anope::string, ci::less>::iterator it = zones.begin(), it_end = zones.end(); it != it_end; ++it) + data["zone" + stringify(count++)] << *it; } static Serializable* Unserialize(Serializable *obj, Serialize::Data &data) { DNSServer *req; + Anope::string server_name; + + data["server_name"] >> server_name; if (obj) + { req = anope_dynamic_static_cast<DNSServer *>(obj); + req->server_name = server_name; + } else - req = new DNSServer(); - - data["server_name"] >> req->server_name; + req = new DNSServer(server_name); for (unsigned i = 0; true; ++i) { @@ -84,6 +162,16 @@ class DNSServer : public Serializable data["limit"] >> req->limit; data["pooled"] >> req->pooled; + req->zones.clear(); + for (unsigned i = 0; true; ++i) + { + Anope::string zone_str; + data["zone" + stringify(i)] >> zone_str; + if (zone_str.empty()) + break; + req->zones.insert(zone_str); + } + return req; } @@ -127,8 +215,10 @@ class CommandOSDNS : public Command if (!srv) entry["State"] = "Split"; + else if (s->Active()) + entry["State"] = "Pooled/Active"; else if (s->Pooled()) - entry["State"] = "Pooled"; + entry["State"] = "Pooled/Not Active"; else entry["State"] = "Unpooled"; @@ -138,47 +228,187 @@ class CommandOSDNS : public Command std::vector<Anope::string> replies; lf.Process(replies); + if (!zones.empty()) + { + ListFormatter lf2; + lf2.AddColumn("Zone").AddColumn("Servers"); + + for (unsigned i = 0; i < zones.size(); ++i) + { + DNSZone *z = zones[i]; + + ListFormatter::ListEntry entry; + entry["Zone"] = z->name; + + Anope::string server_str; + for (std::set<Anope::string, ci::less>::iterator it = z->servers.begin(), it_end = z->servers.end(); it != it_end; ++it) + server_str += *it + " "; + server_str.trim(); + + if (server_str.empty()) + server_str = "None"; + + entry["Servers"] = server_str; + + lf2.AddEntry(entry); + } + + lf2.Process(replies); + } + for (unsigned i = 0; i < replies.size(); ++i) source.Reply(replies[i]); } - void OnAdd(CommandSource &source, const std::vector<Anope::string> ¶ms) + void AddZone(CommandSource &source, const std::vector<Anope::string> ¶ms) + { + const Anope::string &zone = params[1]; + + if (DNSZone::Find(zone)) + { + source.Reply(_("Zone %s already exists."), zone.c_str()); + return; + } + + Log(LOG_ADMIN, source, this) << "to add zone " << zone; + + new DNSZone(zone); + source.Reply(_("Added zone %s."), zone.c_str()); + } + + void DelZone(CommandSource &source, const std::vector<Anope::string> ¶ms) + { + const Anope::string &zone = params[1]; + + DNSZone *z = DNSZone::Find(zone); + if (!z) + { + source.Reply(_("Zone %s does not exist."), zone.c_str()); + return; + } + + Log(LOG_ADMIN, source, this) << "to delete zone " << z->name; + + for (std::set<Anope::string, ci::less>::iterator it = z->servers.begin(), it_end = z->servers.end(); it != it_end; ++it) + { + DNSServer *s = DNSServer::Find(*it); + if (s) + s->zones.erase(z->name); + } + + source.Reply(_("Zone %s removed."), z->name.c_str()); + delete z; + } + + void AddServer(CommandSource &source, const std::vector<Anope::string> ¶ms) { DNSServer *s = DNSServer::Find(params[1]); + const Anope::string &zone = params.size() > 2 ? params[2] : ""; if (s) { - source.Reply(_("Server %s already exists."), params[1].c_str()); + if (zone.empty()) + { + source.Reply(_("Server %s already exists."), s->GetName().c_str()); + } + else + { + DNSZone *z = DNSZone::Find(zone); + if (!z) + { + source.Reply(_("Zone %s does not exist."), zone.c_str()); + return; + } + else if (z->servers.count(s->GetName())) + { + source.Reply(_("Server %s is already in zone %s."), s->GetName().c_str(), z->name.c_str()); + return; + } + + z->servers.insert(s->GetName()); + s->zones.insert(zone); + + Log(LOG_ADMIN, source, this) << "to add server " << s->GetName() << " to zone " << z->name; + + source.Reply(_("Server %s added to zone %s."), s->GetName().c_str(), z->name.c_str()); + } + return; } - if (!Server::Find(params[1])) + Server *serv = Server::Find(params[1]); + if (!serv || serv == Me || serv->HasFlag(SERVER_JUPED)) { source.Reply(_("Server %s is not linked to the network."), params[1].c_str()); return; } s = new DNSServer(params[1]); - source.Reply(_("Added server %s."), s->GetName().c_str()); + if (zone.empty()) + { + Log(LOG_ADMIN, source, this) << "to add server " << s->GetName(); + source.Reply(_("Added server %s."), s->GetName().c_str()); + } + else + { + DNSZone *z = DNSZone::Find(zone); + if (!z) + { + source.Reply(_("Zone %s does not exist."), zone.c_str()); + delete s; + return; + } - Log(LOG_ADMIN, source, this) << "to add server " << s->GetName(); + Log(LOG_ADMIN, source, this) << "to add server " << s->GetName() << " to zone " << zone; + + z->servers.insert(s->GetName()); + s->zones.insert(z->name); + } } - void OnDel(CommandSource &source, const std::vector<Anope::string> ¶ms) + void DelServer(CommandSource &source, const std::vector<Anope::string> ¶ms) { DNSServer *s = DNSServer::Find(params[1]); + const Anope::string &zone = params.size() > 2 ? params[2] : ""; if (!s) { source.Reply(_("Server %s does not exist."), params[1].c_str()); return; } + else if (!zone.empty()) + { + DNSZone *z = DNSZone::Find(zone); + if (!z) + { + source.Reply(_("Zone %s does not exist."), zone.c_str()); + return; + } + else if (!z->servers.count(s->GetName())) + { + source.Reply(_("Server %s is not in zone %s."), s->GetName().c_str(), z->name.c_str()); + return; + } + + Log(LOG_ADMIN, source, this) << "to remove server " << s->GetName() << " from zone " << z->name; + + z->servers.erase(s->GetName()); + source.Reply(_("Removed server %s from zone %s."), s->GetName().c_str(), z->name.c_str()); + return; + } else if (Server::Find(s->GetName())) { source.Reply(_("Server %s must be quit before it can be deleted."), s->GetName().c_str()); return; } + for (std::set<Anope::string, ci::less>::iterator it = s->zones.begin(), it_end = s->zones.end(); it != it_end; ++it) + { + DNSZone *z = DNSZone::Find(*it); + if (z) + z->servers.erase(s->GetName()); + } + Log(LOG_ADMIN, source, this) << "to delete server " << s->GetName(); source.Reply(_("Removed server %s."), s->GetName().c_str()); delete s; @@ -223,7 +453,7 @@ class CommandOSDNS : public Command source.Reply(_("Added IP %s to %s."), params[2].c_str(), s->GetName().c_str()); Log(LOG_ADMIN, source, this) << "to add IP " << params[2] << " to " << s->GetName(); - if (s->Pooled() && dnsmanager) + if (s->Active() && dnsmanager) dnsmanager->UpdateSerial(); } @@ -247,10 +477,10 @@ class CommandOSDNS : public Command if (s->GetIPs().empty()) { s->repool = 0; - s->Pool(false); + s->SetActive(false); } - if (s->Pooled() && dnsmanager) + if (s->Active() && dnsmanager) dnsmanager->UpdateSerial(); return; @@ -311,9 +541,10 @@ class CommandOSDNS : public Command else if (s->GetIPs().empty()) { source.Reply(_("Server %s has no configured IPs."), s->GetName().c_str()); + return; } - s->Pool(true); + s->SetActive(true); source.Reply(_("Pooled %s."), s->GetName().c_str()); Log(LOG_ADMIN, source, this) << "to pool " << s->GetName(); @@ -335,7 +566,7 @@ class CommandOSDNS : public Command return; } - s->Pool(false); + s->SetActive(false); source.Reply(_("Depooled %s."), s->GetName().c_str()); Log(LOG_ADMIN, source, this) << "to depool " << s->GetName(); @@ -344,9 +575,11 @@ class CommandOSDNS : public Command public: CommandOSDNS(Module *creator) : Command(creator, "operserv/dns", 0, 3) { - this->SetDesc(_("Manage the DNS zone for this network")); - this->SetSyntax(_("ADD \037server.name\037")); - this->SetSyntax(_("DEL \037server.name\037")); + this->SetDesc(_("Manage DNS zones for this network")); + this->SetSyntax(_("ADDZONE \037zone.name\037")); + this->SetSyntax(_("DELZONE \037zone.name\037")); + this->SetSyntax(_("ADDSERVER \037server.name\037 [\037zone.name\037]")); + this->SetSyntax(_("DELSERVER \037server.name\037 [\037zone.name\037]")); this->SetSyntax(_("ADDIP \037server.name\037 \037ip\037")); this->SetSyntax(_("DELIP \037server.name\037 \037ip\037")); this->SetSyntax(_("SET \037server.name\037 \37option\37 \037value\037")); @@ -358,10 +591,14 @@ class CommandOSDNS : public Command { if (params.empty()) this->DisplayPoolState(source); - else if (params[0].equals_ci("ADD") && params.size() > 1) - this->OnAdd(source, params); - else if (params[0].equals_ci("DEL") && params.size() > 1) - this->OnDel(source, params); + else if (params[0].equals_ci("ADDZONE") && params.size() > 1) + this->AddZone(source, params); + else if (params[0].equals_ci("DELZONE") && params.size() > 1) + this->DelZone(source, params); + else if (params[0].equals_ci("ADDSERVER") && params.size() > 1) + this->AddServer(source, params); + else if (params[0].equals_ci("DELSERVER") && params.size() > 1) + this->DelServer(source, params); else if (params[0].equals_ci("ADDIP") && params.size() > 2) this->AddIP(source, params); else if (params[0].equals_ci("DELIP") && params.size() > 2) @@ -380,18 +617,27 @@ class CommandOSDNS : public Command { this->SendSyntax(source); source.Reply(" "); - source.Reply(_("This command allows managing a DNS zone\n" - "used for controlling what servers users\n" - "are directed to when connecting. Omitting\n" - "all parameters prints out the status of\n" - "the DNS zone.\n")); + source.Reply(_("This command allows managing DNS zones used for controlling what servers users\n" + "are directed to when connecting. Omitting all parameters prints out the status of\n" + "the DNS zone.\n" + " \n" + "\2ADDZONE\2 adds a zone, eg us.yournetwork.tld. Servers can then be added to this\n" + "zone with the \2ADDSERVER\2 command.\n" + " \n" + "The \2ADDSERVER\2 command adds a server to the given zone. When a query is done, the\n" + "zone in question is served if it exists, else all servers in all zones are served.\n" + "A server may be in more than one zone.\n" + " \n" + "The \2ADDIP\2 command associates an IP with a server.\n" + " \n" + "The \2POOL\2 and \2DEPOOL\2 commands actually add and remove servers to their given zones.\b")); return true; } }; class ModuleDNS : public Module { - Serialize::Type dns_type; + Serialize::Type zone_type, dns_type; CommandOSDNS commandosdns; time_t ttl; @@ -403,7 +649,7 @@ class ModuleDNS : public Module public: ModuleDNS(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, SUPPORTED), - dns_type("DNSServer", DNSServer::Unserialize), commandosdns(this) + zone_type("DNSZone", DNSZone::Unserialize), dns_type("DNSServer", DNSServer::Unserialize), commandosdns(this) { this->SetAuthor("Anope"); @@ -427,24 +673,25 @@ class ModuleDNS : public Module void OnNewServer(Server *s) anope_override { - if (this->readd_connected_servers) + if (s == Me || s->HasFlag(SERVER_JUPED)) + return; + if (!Me->IsSynced() || this->readd_connected_servers) { DNSServer *dns = DNSServer::Find(s->GetName()); - if (dns && !dns->Pooled() && !dns->GetIPs().empty() && dns->GetLimit() < s->users) + if (dns && dns->Pooled() && !dns->Active() && !dns->GetIPs().empty() && dns->GetLimit() < s->users) { - dns->Pool(true); + dns->SetActive(true); Log(this) << "Pooling server " << s->GetName(); } } } - void OnServerQuit(Server *s) anope_override { DNSServer *dns = DNSServer::Find(s->GetName()); - if (dns && dns->Pooled()) + if (dns && dns->Pooled() && dns->Active()) { - dns->Pool(false); + dns->SetActive(false); Log(this) << "Depooling delinked server " << s->GetName(); } } @@ -455,10 +702,10 @@ class ModuleDNS : public Module { DNSServer *s = DNSServer::Find(u->server->GetName()); /* Check for user limit reached */ - if (s && s->GetLimit() && s->Pooled() && u->server->users >= s->GetLimit()) + if (s && s->Pooled() && s->Active() && s->GetLimit() && u->server->users >= s->GetLimit()) { Log(this) << "Depooling full server " << s->GetName() << ": " << u->server->users << " users"; - s->Pool(false); + s->SetActive(false); } } } @@ -468,14 +715,14 @@ class ModuleDNS : public Module if (u && u->server) { DNSServer *s = DNSServer::Find(u->server->GetName()); - if (!s) + if (!s || !s->Pooled()) return; /* Check for dropping under userlimit */ - if (s->GetLimit() && !s->Pooled() && s->GetLimit() > u->server->users) + if (s->GetLimit() && !s->Active() && s->GetLimit() > u->server->users) { Log(this) << "Pooling server " << s->GetName(); - s->Pool(true); + s->SetActive(true); } if (this->user_drop_mark > 0) @@ -485,21 +732,21 @@ class ModuleDNS : public Module if (times.size() > static_cast<unsigned>(this->user_drop_mark)) times.pop_front(); - if (s->Pooled() && times.size() == static_cast<unsigned>(this->user_drop_mark)) + if (times.size() == static_cast<unsigned>(this->user_drop_mark)) { time_t diff = Anope::CurTime - *times.begin(); /* Check for very fast user drops */ - if (diff <= this->user_drop_time) + if (s->Active() && diff <= this->user_drop_time) { Log(this) << "Depooling server " << s->GetName() << ": dropped " << this->user_drop_mark << " users in " << diff << " seconds"; s->repool = Anope::CurTime + this->user_drop_readd_time; - s->Pool(false); + s->SetActive(false); } /* Check for needing to re-pool a server that dropped users */ - else if (s->repool && s->repool <= Anope::CurTime && !s->Pooled()) + else if (!s->Active() && s->repool && s->repool <= Anope::CurTime) { - s->Pool(true); + s->SetActive(true); s->repool = 0; Log(this) << "Pooling server " << s->GetName(); } @@ -517,22 +764,50 @@ class ModuleDNS : public Module if (q.type != DNS::QUERY_A && q.type != DNS::QUERY_AAAA && q.type != DNS::QUERY_AXFR) return; - for (unsigned i = 0; i < dns_servers.size(); ++i) + DNSZone *zone = DNSZone::Find(q.name); + if (zone) { - DNSServer *s = dns_servers[i]; - if (!s->Pooled()) - continue; + for (std::set<Anope::string, ci::less>::iterator it = zone->servers.begin(), it_end = zone->servers.end(); it != it_end; ++it) + { + DNSServer *s = DNSServer::Find(*it); + if (!s || !s->Active()) + continue; - for (unsigned j = 0; j < s->GetIPs().size(); ++j) + for (unsigned j = 0; j < s->GetIPs().size(); ++j) + { + DNS::QueryType q_type = s->GetIPs()[j].find(':') != Anope::string::npos ? DNS::QUERY_AAAA : DNS::QUERY_A; + + if (q.type == DNS::QUERY_AXFR || q_type == q.type) + { + DNS::ResourceRecord rr(q.name, q_type); + rr.ttl = this->ttl; + rr.rdata = s->GetIPs()[j]; + packet->answers.push_back(rr); + } + } + } + } + + if (packet->answers.empty()) + { + /* Default zone */ + for (unsigned i = 0; i < dns_servers.size(); ++i) { - DNS::QueryType q_type = s->GetIPs()[j].find(':') != Anope::string::npos ? DNS::QUERY_AAAA : DNS::QUERY_A; + DNSServer *s = dns_servers[i]; + if (!s->Active()) + continue; - if (q.type == DNS::QUERY_AXFR || q_type == q.type) + for (unsigned j = 0; j < s->GetIPs().size(); ++j) { - DNS::ResourceRecord rr(q.name, q_type); - rr.ttl = this->ttl; - rr.rdata = s->GetIPs()[j]; - packet->answers.push_back(rr); + DNS::QueryType q_type = s->GetIPs()[j].find(':') != Anope::string::npos ? DNS::QUERY_AAAA : DNS::QUERY_A; + + if (q.type == DNS::QUERY_AXFR || q_type == q.type) + { + DNS::ResourceRecord rr(q.name, q_type); + rr.ttl = this->ttl; + rr.rdata = s->GetIPs()[j]; + packet->answers.push_back(rr); + } } } } @@ -543,7 +818,7 @@ class ModuleDNS : public Module if (last_warn + 60 < Anope::CurTime) { last_warn = Anope::CurTime; - Log(this) << "os_dns: Warning! There are no pooled servers!"; + Log(this) << "Warning! There are no pooled servers!"; } /* Something messed up, just return them all and hope one is available */ @@ -567,7 +842,7 @@ class ModuleDNS : public Module if (packet->answers.empty()) { - Log(this) << "os_dns: Error! There are no servers with any IPs. At all."; + Log(this) << "Error! There are no servers with any IPs. At all."; /* Send back an empty answer anyway */ } } |