diff options
author | Adam <Adam@anope.org> | 2013-08-11 15:46:59 -0400 |
---|---|---|
committer | Adam <Adam@anope.org> | 2013-08-11 15:48:46 -0400 |
commit | 812cb04fde10414ba1b13c88bde78f86e6701a5d (patch) | |
tree | fc96432c599f118f42642126bd88eaabac41e1d0 | |
parent | 1314d5b4f159a5a4964bf192b9e062b823ccdf62 (diff) |
Add DNS Notify support
-rw-r--r-- | data/modules.example.conf | 8 | ||||
-rw-r--r-- | include/modules/dns.h | 4 | ||||
-rw-r--r-- | modules/commands/os_dns.cpp | 12 | ||||
-rw-r--r-- | modules/m_dns.cpp | 110 |
4 files changed, 121 insertions, 13 deletions
diff --git a/data/modules.example.conf b/data/modules.example.conf index aaf7048b2..030a01258 100644 --- a/data/modules.example.conf +++ b/data/modules.example.conf @@ -66,6 +66,14 @@ module { name = "help" } * if you want your records to be updated without much delay. */ refresh = 3600 + + /* A notify block. There should probably be one per nameserver listed in 'nameservers'. + */ + notify + { + ip = "192.0.2.0" + port = 53 + } } /* diff --git a/include/modules/dns.h b/include/modules/dns.h index 44b9370cb..71697cc32 100644 --- a/include/modules/dns.h +++ b/include/modules/dns.h @@ -38,13 +38,14 @@ namespace DNS /* A lookup for any record */ QUERY_ANY = 255 }; - + /** Flags that can be AND'd into DNSPacket::flags to receive certain values */ enum { QUERYFLAGS_QR = 0x8000, QUERYFLAGS_OPCODE = 0x7800, + QUERYFLAGS_OPCODE_NOTIFY = 0x2000, QUERYFLAGS_AA = 0x400, QUERYFLAGS_TC = 0x200, QUERYFLAGS_RD = 0x100, @@ -126,6 +127,7 @@ namespace DNS virtual bool HandlePacket(ReplySocket *s, const unsigned char *const data, int len, sockaddrs *from) = 0; virtual void UpdateSerial() = 0; + virtual void Notify(const Anope::string &zone) = 0; virtual uint32_t GetSerial() const = 0; }; diff --git a/modules/commands/os_dns.cpp b/modules/commands/os_dns.cpp index 1df401112..717cbf29b 100644 --- a/modules/commands/os_dns.cpp +++ b/modules/commands/os_dns.cpp @@ -131,7 +131,11 @@ class DNSServer : public Serializable active = p; if (dnsmanager) + { dnsmanager->UpdateSerial(); + for (std::set<Anope::string, ci::less>::iterator it = zones.begin(), it_end = zones.end(); it != it_end; ++it) + dnsmanager->Notify(*it); + } } void Serialize(Serialize::Data &data) const anope_override @@ -458,7 +462,11 @@ class CommandOSDNS : public Command Log(LOG_ADMIN, source, this) << "to add IP " << params[2] << " to " << s->GetName(); if (s->Active() && dnsmanager) + { dnsmanager->UpdateSerial(); + for (std::set<Anope::string, ci::less>::iterator it = s->zones.begin(), it_end = s->zones.end(); it != it_end; ++it) + dnsmanager->Notify(*it); + } } void DelIP(CommandSource &source, const std::vector<Anope::string> ¶ms) @@ -485,7 +493,11 @@ class CommandOSDNS : public Command } if (s->Active() && dnsmanager) + { dnsmanager->UpdateSerial(); + for (std::set<Anope::string, ci::less>::iterator it = s->zones.begin(), it_end = s->zones.end(); it != it_end; ++it) + dnsmanager->Notify(*it); + } return; } diff --git a/modules/m_dns.cpp b/modules/m_dns.cpp index 422d035b3..214e97cf4 100644 --- a/modules/m_dns.cpp +++ b/modules/m_dns.cpp @@ -612,6 +612,39 @@ class UDPSocket : public ReplySocket } }; +class NotifySocket : public Socket +{ + Packet *packet; + public: + NotifySocket(bool v6, Packet *p) : Socket(-1, v6, SOCK_DGRAM), packet(p) + { + SocketEngine::Change(this, false, SF_READABLE); + SocketEngine::Change(this, true, SF_WRITABLE); + } + + bool ProcessWrite() anope_override + { + if (!packet) + return false; + + Log(LOG_DEBUG_2) << "Resolver: Notifying slave " << packet->addr.addr(); + + try + { + unsigned char buffer[524]; + unsigned short len = packet->Pack(buffer, sizeof(buffer)); + + sendto(this->GetFD(), reinterpret_cast<char *>(buffer), len, 0, &packet->addr.sa, packet->addr.size()); + } + catch (const SocketException &) { } + + delete packet; + packet = NULL; + + return false; + } +}; + class MyManager : public Manager, public Timer { uint32_t serial; @@ -624,6 +657,8 @@ class MyManager : public Manager, public Timer bool listen; sockaddrs addrs; + + std::vector<std::pair<Anope::string, short> > notify; public: std::map<unsigned short, Request *> requests; @@ -652,7 +687,7 @@ class MyManager : public Manager, public Timer this->cache.clear(); } - void SetIPPort(const Anope::string &nameserver, const Anope::string &ip, unsigned short port) + void SetIPPort(const Anope::string &nameserver, const Anope::string &ip, unsigned short port, std::vector<std::pair<Anope::string, short> > n) { delete udpsock; delete tcpsock; @@ -677,8 +712,26 @@ class MyManager : public Manager, public Timer { Log() << "Unable to bind dns to " << ip << ":" << port << ": " << ex.GetReason(); } + + notify = n; + } + + private: + unsigned short GetID() + { + if (this->udpsock->GetPackets().size() == 65535) + throw SocketException("DNS queue full"); + + static unsigned short cur_id = rand(); + + do + cur_id = (cur_id + 1) & 0xFFFF; + while (!cur_id || this->requests.count(cur_id)); + + return cur_id; } + public: void Process(Request *req) anope_override { Log(LOG_DEBUG_2) << "Resolver: Processing request to lookup " << req->name << ", of type " << req->type; @@ -693,16 +746,7 @@ class MyManager : public Manager, public Timer if (!this->udpsock) throw SocketException("No dns socket"); - if (this->udpsock->GetPackets().size() == 65535) - throw SocketException("DNS queue full"); - - do - { - static unsigned short cur_id = rand(); - cur_id = req->id = (cur_id + 1) & 0xFFFF; - } - while (!req->id || this->requests.count(req->id)); - + req->id = GetID(); this->requests[req->id] = req; req->SetSecs(timeout); @@ -877,6 +921,37 @@ class MyManager : public Manager, public Timer serial = Anope::CurTime; } + void Notify(const Anope::string &zone) anope_override + { + /* notify slaves of the update */ + for (unsigned i = 0; i < notify.size(); ++i) + { + const Anope::string &ip = notify[i].first; + short port = notify[i].second; + + sockaddrs addr; + addr.pton(ip.find(':') != Anope::string::npos ? AF_INET6 : AF_INET, ip, port); + if (!addr.valid()) + return; + + Packet *packet = new Packet(this, &addr); + packet->flags = QUERYFLAGS_AA | QUERYFLAGS_OPCODE_NOTIFY; + try + { + packet->id = GetID(); + } + catch (const SocketException &) + { + delete packet; + continue; + } + + packet->questions.push_back(Question(zone, QUERY_SOA)); + + new NotifySocket(ip.find(':') != Anope::string::npos ? AF_INET6 : AF_INET, packet); + } + } + uint32_t GetSerial() const anope_override { return serial; @@ -936,6 +1011,8 @@ class ModuleDNS : public Module Anope::string ip; int port; + std::vector<std::pair<Anope::string, short> > notify; + public: ModuleDNS(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, EXTRA | VENDOR), manager(this) { @@ -954,6 +1031,15 @@ class ModuleDNS : public Module nameservers = block->Get<const Anope::string>("nameservers", "ns1.example.com"); refresh = block->Get<int>("refresh", "3600"); + for (int i = 0; i < block->CountBlock("notify"); ++i) + { + Configuration::Block *n = block->GetBlock("notify", i); + Anope::string nip = n->Get<Anope::string>("ip"); + short nport = n->Get<short>("port"); + + notify.push_back(std::make_pair(nip, nport)); + } + if (Anope::IsFile(nameserver)) { std::ifstream f(nameserver.c_str()); @@ -991,7 +1077,7 @@ class ModuleDNS : public Module try { - this->manager.SetIPPort(nameserver, ip, port); + this->manager.SetIPPort(nameserver, ip, port, notify); } catch (const SocketException &ex) { |