summaryrefslogtreecommitdiff
path: root/modules/commands/os_dns.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'modules/commands/os_dns.cpp')
-rw-r--r--modules/commands/os_dns.cpp401
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> &params)
+ void AddZone(CommandSource &source, const std::vector<Anope::string> &params)
+ {
+ 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> &params)
+ {
+ 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> &params)
{
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> &params)
+ void DelServer(CommandSource &source, const std::vector<Anope::string> &params)
{
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 */
}
}