mirror of
https://github.com/anope/anope.git
synced 2026-07-08 01:23:14 +02:00
BIND's forward ability did not work as I expected
because it will not forward non recursive queries. So, added support for SOA, NS, and AXFR requests.
This commit is contained in:
+14
-1
@@ -1037,7 +1037,7 @@ mail
|
||||
* [OPTIONAL] DNS Config
|
||||
*
|
||||
* This section is used to configure DNS.
|
||||
* At this time DNS is only used by a few modules (m_dnsbl)
|
||||
* At this time DNS is only used by a few modules (m_dnsbl and os_dns)
|
||||
* and is not required by the core to function.
|
||||
*/
|
||||
dns
|
||||
@@ -1055,6 +1055,8 @@ dns
|
||||
*/
|
||||
timeout = 5
|
||||
|
||||
/* Only edit below if you are expecting to use os_dns */
|
||||
|
||||
/*
|
||||
* The IP and port services use to listen for DNS queries.
|
||||
* Note that ports less than 1024 are privileged on UNIX/Linux systems, and
|
||||
@@ -1064,6 +1066,17 @@ dns
|
||||
*/
|
||||
ip = "0.0.0.0"
|
||||
port = 53
|
||||
|
||||
/*
|
||||
* SOA record information.
|
||||
*/
|
||||
admin = "admin@example.com"
|
||||
/* This should be the name of the public facing nameserver serving the records */
|
||||
primary_nameserver = "ns1.example.com"
|
||||
/* The time slave servers are allowed to cache. This should be reasonably low
|
||||
* if you want your records to be updated without much delay.
|
||||
*/
|
||||
refresh = 3600
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
+11
-11
@@ -366,21 +366,21 @@ defcon
|
||||
* To use this module you must set a nameserver record for services
|
||||
* so that DNS queries go to services.
|
||||
*
|
||||
* We recommend you use something similar to BIND's query forwarding
|
||||
* ability to hide service's IP, provide query caching, and provide
|
||||
* better fault tolerance. To do this, configure BIND similar to:
|
||||
* Alternatively, you may use a slave DNS server to hide service's IP,
|
||||
* provide query caching, and provide better fault tolerance.
|
||||
*
|
||||
* To do this using BIND, configure similar to:
|
||||
*
|
||||
* options {
|
||||
* dnssec-enable no;
|
||||
* dnssec-validation no;
|
||||
* };
|
||||
* zone "irc.example.com" IN {
|
||||
* type forward;
|
||||
* forward first;
|
||||
* forwarders { 10.0.0.1 port 5353; }; # Where this is the IP and dns:port of services
|
||||
* type slave;
|
||||
* masters { 127.0.0.1 port 5353; };
|
||||
* };
|
||||
*
|
||||
* And then set a NS record for irc.example.com. to BIND.
|
||||
* Where 127.0.0.1:5353 is the IP and port services are listening on.
|
||||
* We recommend you externally firewall both UDP and TCP to the port
|
||||
* Anope is listening on.
|
||||
*
|
||||
* Finally set a NS record for irc.example.com. to BIND or services.
|
||||
*/
|
||||
#module { name = "os_dns" }
|
||||
#command { service = "OperServ"; name = "DNS"; command = "operserv/dns"; permission = "operserv/dns"; }
|
||||
|
||||
@@ -503,6 +503,12 @@ class CoreExport ServerConfig
|
||||
/* The IP/port DNS queries come in on */
|
||||
Anope::string DNSIP;
|
||||
int DNSPort;
|
||||
/* DNS SOA admin */
|
||||
Anope::string DNSSOAAdmin;
|
||||
/* DNS SOA primary NS */
|
||||
Anope::string DNSSOANS;
|
||||
/* SOA Refresh time */
|
||||
unsigned DNSSOARefresh;
|
||||
|
||||
/* Prefix of guest nicks when a user gets forced off of a nick */
|
||||
Anope::string NSGuestNickPrefix;
|
||||
|
||||
+72
-17
@@ -27,12 +27,18 @@ enum QueryType
|
||||
DNS_QUERY_NONE,
|
||||
/* A simple A lookup */
|
||||
DNS_QUERY_A = 1,
|
||||
/* An authoritative name server */
|
||||
DNS_QUERY_NS = 2,
|
||||
/* A CNAME lookup */
|
||||
DNS_QUERY_CNAME = 5,
|
||||
/* Start of a zone of authority */
|
||||
DNS_QUERY_SOA = 6,
|
||||
/* Reverse DNS lookup */
|
||||
DNS_QUERY_PTR = 12,
|
||||
/* IPv6 AAAA lookup */
|
||||
DNS_QUERY_AAAA = 28
|
||||
DNS_QUERY_AAAA = 28,
|
||||
/* Zone transfer */
|
||||
DNS_QUERY_AXFR = 252
|
||||
};
|
||||
|
||||
/** Flags that can be AND'd into DNSPacket::flags to receive certain values
|
||||
@@ -145,30 +151,85 @@ class DNSPacket : public DNSQuery
|
||||
/* Flags on the packet */
|
||||
unsigned short flags;
|
||||
|
||||
DNSPacket(const sockaddrs &a);
|
||||
DNSPacket(sockaddrs *a);
|
||||
void Fill(const unsigned char *input, const unsigned short len);
|
||||
unsigned short Pack(unsigned char *output, unsigned short output_size);
|
||||
};
|
||||
|
||||
/** DNS manager, manages all requests
|
||||
/** DNS manager
|
||||
*/
|
||||
class CoreExport DNSManager : public Timer, public Socket
|
||||
class CoreExport DNSManager : public Timer
|
||||
{
|
||||
class ReplySocket : public virtual Socket
|
||||
{
|
||||
public:
|
||||
virtual ~ReplySocket() { }
|
||||
virtual void Reply(DNSPacket *p) = 0;
|
||||
};
|
||||
|
||||
/* Listens for TCP requests */
|
||||
class TCPSocket : public ListenSocket
|
||||
{
|
||||
/* A TCP client */
|
||||
class Client : public ClientSocket, public Timer, public ReplySocket
|
||||
{
|
||||
TCPSocket *tcpsock;
|
||||
DNSPacket *packet;
|
||||
unsigned char packet_buffer[524];
|
||||
int length;
|
||||
|
||||
public:
|
||||
Client(TCPSocket *ls, int fd, const sockaddrs &addr);
|
||||
~Client();
|
||||
|
||||
/* Times out after a few seconds */
|
||||
void Tick(time_t) anope_override { }
|
||||
void Reply(DNSPacket *p) anope_override;
|
||||
bool ProcessRead() anope_override;
|
||||
bool ProcessWrite() anope_override;
|
||||
};
|
||||
|
||||
public:
|
||||
TCPSocket(const Anope::string &ip, int port);
|
||||
|
||||
ClientSocket *OnAccept(int fd, const sockaddrs &addr) anope_override;
|
||||
};
|
||||
|
||||
/* Listens for UDP requests */
|
||||
class UDPSocket : public ReplySocket
|
||||
{
|
||||
std::deque<DNSPacket *> packets;
|
||||
public:
|
||||
|
||||
UDPSocket(const Anope::string &ip, int port);
|
||||
~UDPSocket();
|
||||
|
||||
void Reply(DNSPacket *p) anope_override;
|
||||
|
||||
std::deque<DNSPacket *>& GetPackets() { return packets; }
|
||||
|
||||
bool ProcessRead() anope_override;
|
||||
|
||||
bool ProcessWrite() anope_override;
|
||||
};
|
||||
|
||||
typedef std::multimap<Anope::string, ResourceRecord, ci::less> cache_map;
|
||||
cache_map cache;
|
||||
|
||||
std::deque<DNSPacket *> packets;
|
||||
uint32_t serial;
|
||||
int last_year, last_day, last_num;
|
||||
|
||||
public:
|
||||
TCPSocket *tcpsock;
|
||||
UDPSocket *udpsock;
|
||||
|
||||
sockaddrs addrs;
|
||||
std::map<unsigned short, DNSRequest *> requests;
|
||||
|
||||
DNSManager(const Anope::string &nameserver, const Anope::string &ip, int port);
|
||||
|
||||
~DNSManager();
|
||||
|
||||
bool ProcessRead() anope_override;
|
||||
|
||||
bool ProcessWrite() anope_override;
|
||||
bool HandlePacket(ReplySocket *s, const unsigned char *const data, int len, sockaddrs *from);
|
||||
|
||||
/** Add a record to the dns cache
|
||||
* @param r The record
|
||||
@@ -189,14 +250,8 @@ class CoreExport DNSManager : public Timer, public Socket
|
||||
*/
|
||||
void Cleanup(Module *mod);
|
||||
|
||||
/** Get the list of packets pending to be sent
|
||||
*/
|
||||
std::deque<DNSPacket *>& GetPackets();
|
||||
|
||||
/** Queues a packet for sending
|
||||
* @param p The packet
|
||||
*/
|
||||
void SendPacket(DNSPacket *p);
|
||||
void UpdateSerial();
|
||||
uint32_t GetSerial() const;
|
||||
|
||||
/** Does a BLOCKING DNS query and returns the first IP.
|
||||
* Only use this if you know what you are doing. Unless you specifically
|
||||
|
||||
+1
-1
@@ -358,7 +358,7 @@ class CoreExport BinarySocket : public virtual Socket
|
||||
virtual bool Read(const char *buffer, size_t l);
|
||||
};
|
||||
|
||||
class CoreExport ListenSocket : public Socket
|
||||
class CoreExport ListenSocket : public virtual Socket
|
||||
{
|
||||
public:
|
||||
/** Constructor
|
||||
|
||||
+38
-23
@@ -17,10 +17,10 @@ class DNSServer : public Serializable
|
||||
Anope::string server_name;
|
||||
std::vector<Anope::string> ips;
|
||||
unsigned limit;
|
||||
bool pooled;
|
||||
|
||||
DNSServer() : Serializable("DNSServer"), limit(0), pooled(false), repool(0) { dns_servers.push_back(this); }
|
||||
public:
|
||||
bool pooled;
|
||||
time_t repool;
|
||||
|
||||
DNSServer(const Anope::string &sn) : Serializable("DNSServer"), server_name(sn), limit(0), pooled(false), repool(0)
|
||||
@@ -39,6 +39,15 @@ 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;
|
||||
if (DNSEngine)
|
||||
DNSEngine->UpdateSerial();
|
||||
}
|
||||
|
||||
|
||||
Serialize::Data serialize() const anope_override
|
||||
{
|
||||
@@ -115,7 +124,7 @@ class CommandOSDNS : public Command
|
||||
|
||||
if (!srv)
|
||||
entry["State"] = "Split";
|
||||
else if (s->pooled)
|
||||
else if (s->Pooled())
|
||||
entry["State"] = "Pooled";
|
||||
else
|
||||
entry["State"] = "Unpooled";
|
||||
@@ -210,6 +219,9 @@ class CommandOSDNS : public Command
|
||||
s->GetIPs().push_back(params[2]);
|
||||
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())
|
||||
DNSEngine->UpdateSerial();
|
||||
}
|
||||
|
||||
void DelIP(CommandSource &source, const std::vector<Anope::string> ¶ms)
|
||||
@@ -232,9 +244,12 @@ class CommandOSDNS : public Command
|
||||
if (s->GetIPs().empty())
|
||||
{
|
||||
s->repool = 0;
|
||||
s->pooled = false;
|
||||
s->Pool(false);
|
||||
}
|
||||
|
||||
if (s->Pooled())
|
||||
DNSEngine->UpdateSerial();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -285,7 +300,7 @@ class CommandOSDNS : public Command
|
||||
source.Reply(_("Server %s is not currently linked."), s->GetName().c_str());
|
||||
return;
|
||||
}
|
||||
else if (s->pooled)
|
||||
else if (s->Pooled())
|
||||
{
|
||||
source.Reply(_("Server %s is already pooled."), s->GetName().c_str());
|
||||
return;
|
||||
@@ -295,7 +310,7 @@ class CommandOSDNS : public Command
|
||||
source.Reply(_("Server %s has no configured IPs."), s->GetName().c_str());
|
||||
}
|
||||
|
||||
s->pooled = true;
|
||||
s->Pool(true);
|
||||
|
||||
source.Reply(_("Pooled %s."), s->GetName().c_str());
|
||||
Log(LOG_ADMIN, source, this) << "to pool " << s->GetName();
|
||||
@@ -311,13 +326,13 @@ class CommandOSDNS : public Command
|
||||
source.Reply(_("Server %s does not exist."), params[1].c_str());
|
||||
return;
|
||||
}
|
||||
else if (!s->pooled)
|
||||
else if (!s->Pooled())
|
||||
{
|
||||
source.Reply(_("Server %s is not pooled."), s->GetName().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
s->pooled = false;
|
||||
s->Pool(false);
|
||||
|
||||
source.Reply(_("Depooled %s."), s->GetName().c_str());
|
||||
Log(LOG_ADMIN, source, this) << "to depool " << s->GetName();
|
||||
@@ -412,9 +427,9 @@ class ModuleDNS : public Module
|
||||
if (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->GetIPs().empty() && dns->GetLimit() < s->Users)
|
||||
{
|
||||
dns->pooled = true;
|
||||
dns->Pool(true);
|
||||
Log() << "Pooling server " << s->GetName();
|
||||
}
|
||||
}
|
||||
@@ -424,9 +439,9 @@ class ModuleDNS : public Module
|
||||
void OnServerQuit(Server *s) anope_override
|
||||
{
|
||||
DNSServer *dns = DNSServer::Find(s->GetName());
|
||||
if (dns && dns->pooled)
|
||||
if (dns && dns->Pooled())
|
||||
{
|
||||
dns->pooled = false;
|
||||
dns->Pool(false);
|
||||
Log() << "Depooling delinked server " << s->GetName();
|
||||
}
|
||||
}
|
||||
@@ -437,10 +452,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->GetLimit() && s->Pooled() && u->server->Users >= s->GetLimit())
|
||||
{
|
||||
Log() << "Depooling full server " << s->GetName() << ": " << u->server->Users << " users";
|
||||
s->pooled = false;
|
||||
s->Pool(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -454,10 +469,10 @@ class ModuleDNS : public Module
|
||||
return;
|
||||
|
||||
/* Check for dropping under userlimit */
|
||||
if (s->GetLimit() && !s->pooled && s->GetLimit() > u->server->Users)
|
||||
if (s->GetLimit() && !s->Pooled() && s->GetLimit() > u->server->Users)
|
||||
{
|
||||
Log() << "Pooling server " << s->GetName();
|
||||
s->pooled = true;
|
||||
s->Pool(true);
|
||||
}
|
||||
|
||||
if (this->user_drop_mark > 0)
|
||||
@@ -467,7 +482,7 @@ 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 (s->Pooled() && times.size() == static_cast<unsigned>(this->user_drop_mark))
|
||||
{
|
||||
time_t diff = Anope::CurTime - *times.begin();
|
||||
|
||||
@@ -476,12 +491,12 @@ class ModuleDNS : public Module
|
||||
{
|
||||
Log() << "Depooling server " << s->GetName() << ": dropped " << this->user_drop_mark << " users in " << diff << " seconds";
|
||||
s->repool = Anope::CurTime + this->user_drop_readd_time;
|
||||
s->pooled = false;
|
||||
s->Pool(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->repool && s->repool <= Anope::CurTime && !s->Pooled())
|
||||
{
|
||||
s->pooled = true;
|
||||
s->Pool(true);
|
||||
s->repool = 0;
|
||||
Log() << "Pooling server " << s->GetName();
|
||||
}
|
||||
@@ -496,20 +511,20 @@ class ModuleDNS : public Module
|
||||
return;
|
||||
/* Currently we reply to any QR for A/AAAA */
|
||||
const Question& q = req.questions[0];
|
||||
if (q.type != DNS_QUERY_A && q.type != DNS_QUERY_AAAA)
|
||||
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)
|
||||
{
|
||||
DNSServer *s = dns_servers[i];
|
||||
if (!s->pooled)
|
||||
if (!s->Pooled())
|
||||
continue;
|
||||
|
||||
for (unsigned j = 0; j < s->GetIPs().size(); ++j)
|
||||
{
|
||||
QueryType q_type = s->GetIPs()[j].find(':') != Anope::string::npos ? DNS_QUERY_AAAA : DNS_QUERY_A;
|
||||
|
||||
if (q_type == q.type)
|
||||
if (q.type == DNS_QUERY_AXFR || q_type == q.type)
|
||||
{
|
||||
ResourceRecord rr(q.name, q_type);
|
||||
rr.ttl = this->ttl;
|
||||
@@ -537,7 +552,7 @@ class ModuleDNS : public Module
|
||||
{
|
||||
QueryType q_type = s->GetIPs()[j].find(':') != Anope::string::npos ? DNS_QUERY_AAAA : DNS_QUERY_A;
|
||||
|
||||
if (q_type == q.type)
|
||||
if (q.type == DNS_QUERY_AXFR || q_type == q.type)
|
||||
{
|
||||
ResourceRecord rr(q.name, q_type);
|
||||
rr.ttl = this->ttl;
|
||||
|
||||
@@ -142,7 +142,7 @@ class HTTPClient : public ClientSocket, public BufferedSocket, public BinarySock
|
||||
virtual void SendReply(HTTPReply *) = 0;
|
||||
};
|
||||
|
||||
class HTTPProvider : public Service, public ListenSocket
|
||||
class HTTPProvider : public ListenSocket, public Service
|
||||
{
|
||||
Anope::string ip;
|
||||
unsigned short port;
|
||||
@@ -150,7 +150,7 @@ class HTTPProvider : public Service, public ListenSocket
|
||||
Anope::string ext_ip;
|
||||
std::vector<Anope::string> ext_headers;
|
||||
|
||||
HTTPProvider(Module *c, const Anope::string &n, const Anope::string &i, const unsigned short p) : Service(c, "HTTPProvider", n), ListenSocket(i, p, i.find(':') != Anope::string::npos), ip(i), port(p) { }
|
||||
HTTPProvider(Module *c, const Anope::string &n, const Anope::string &i, const unsigned short p) : ListenSocket(i, p, i.find(':') != Anope::string::npos), Service(c, "HTTPProvider", n) { }
|
||||
|
||||
const Anope::string &GetIP() const
|
||||
{
|
||||
|
||||
@@ -257,7 +257,7 @@ class MyHTTPProvider : public HTTPProvider, public CallBack
|
||||
std::list<dynamic_reference<MyHTTPClient> > clients;
|
||||
|
||||
public:
|
||||
MyHTTPProvider(Module *c, const Anope::string &n, const Anope::string &i, const unsigned short p, const int t) : HTTPProvider(c, n, i, p), CallBack(c, 10, Anope::CurTime, true), timeout(t) { }
|
||||
MyHTTPProvider(Module *c, const Anope::string &n, const Anope::string &i, const unsigned short p, const int t) : Socket(-1, i.find(':') != Anope::string::npos), HTTPProvider(c, n, i, p), CallBack(c, 10, Anope::CurTime, true), timeout(t) { }
|
||||
|
||||
void Tick(time_t) anope_override
|
||||
{
|
||||
|
||||
@@ -41,7 +41,7 @@ class ProxyCallbackListener : public ListenSocket
|
||||
};
|
||||
|
||||
public:
|
||||
ProxyCallbackListener(const Anope::string &b, int p) : ListenSocket(b, p, false)
|
||||
ProxyCallbackListener(const Anope::string &b, int p) : Socket(-1, b.find(':') != Anope::string::npos), ListenSocket(b, p, false)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -88,7 +88,7 @@ class MyXMLRPCClientSocket : public XMLRPCClientSocket
|
||||
class MyXMLRPCListenSocket : public XMLRPCListenSocket
|
||||
{
|
||||
public:
|
||||
MyXMLRPCListenSocket(const Anope::string &bindip, int port, bool ipv6, const Anope::string &u, const Anope::string &p, const std::vector<Anope::string> &a) : XMLRPCListenSocket(bindip, port, ipv6, u, p, a)
|
||||
MyXMLRPCListenSocket(const Anope::string &bindip, int port, bool ipv6, const Anope::string &u, const Anope::string &p, const std::vector<Anope::string> &a) : Socket(-1, ipv6), XMLRPCListenSocket(bindip, port, ipv6, u, p, a)
|
||||
{
|
||||
listen_sockets.push_back(this);
|
||||
}
|
||||
|
||||
+4
-2
@@ -208,8 +208,7 @@ ServerConfig::ServerConfig() : config_data(), NSDefFlags(NickCoreFlagStrings), C
|
||||
this->NameServer = "127.0.0.1";
|
||||
}
|
||||
}
|
||||
if (DNSEngine)
|
||||
DNSEngine->SetFlag(SF_DEAD);
|
||||
delete DNSEngine;
|
||||
DNSEngine = new DNSManager(this->NameServer, this->DNSIP, this->DNSPort);
|
||||
|
||||
if (this->CaseMap == "ascii")
|
||||
@@ -1304,6 +1303,9 @@ ConfigItems::ConfigItems(ServerConfig *conf)
|
||||
{"dns", "timeout", "5", new ValueContainerTime(&conf->DNSTimeout), DT_TIME, NoValidation},
|
||||
{"dns", "ip", "0.0.0.0", new ValueContainerString(&conf->DNSIP), DT_STRING, NoValidation},
|
||||
{"dns", "port", "53", new ValueContainerInt(&conf->DNSPort), DT_INTEGER, NoValidation},
|
||||
{"dns", "admin", "admin@example.com", new ValueContainerString(&conf->DNSSOAAdmin), DT_STRING, NoValidation},
|
||||
{"dns", "primary_nameserver", "ns1.example.com", new ValueContainerString(&conf->DNSSOANS), DT_STRING, NoValidation},
|
||||
{"dns", "refresh", "3600", new ValueContainerUInt(&conf->DNSSOARefresh), DT_UINTEGER, NoValidation},
|
||||
{"chanserv", "name", "", new ValueContainerString(&conf->ChanServ), DT_STRING, NoValidation},
|
||||
{"chanserv", "defaults", "keeptopic secure securefounder signkick", new ValueContainerString(&CSDefaults), DT_STRING, ValidateChanServ},
|
||||
{"chanserv", "maxregistered", "0", new ValueContainerUInt(&conf->CSMaxReg), DT_UINTEGER, ValidateChanServ},
|
||||
|
||||
+279
-59
@@ -58,9 +58,9 @@ DNSQuery::DNSQuery(const Question &q)
|
||||
|
||||
DNSRequest::DNSRequest(const Anope::string &addr, QueryType qt, bool cache, Module *c) : Timer(Config->DNSTimeout), Question(addr, qt), use_cache(cache), id(0), creator(c)
|
||||
{
|
||||
if (!DNSEngine)
|
||||
if (!DNSEngine || !DNSEngine->udpsock)
|
||||
throw SocketException("No DNSEngine");
|
||||
if (DNSEngine->GetPackets().size() == 65535)
|
||||
if (DNSEngine->udpsock->GetPackets().size() == 65535)
|
||||
throw SocketException("DNS queue full");
|
||||
|
||||
do
|
||||
@@ -82,7 +82,7 @@ void DNSRequest::Process()
|
||||
{
|
||||
Log(LOG_DEBUG_2) << "Resolver: Processing request to lookup " << this->name << ", of type " << this->type;
|
||||
|
||||
if (!DNSEngine)
|
||||
if (!DNSEngine || !DNSEngine->udpsock)
|
||||
throw SocketException("DNSEngine has not been initialized");
|
||||
|
||||
if (this->use_cache && DNSEngine->CheckCache(this))
|
||||
@@ -92,12 +92,12 @@ void DNSRequest::Process()
|
||||
return;
|
||||
}
|
||||
|
||||
DNSPacket *p = new DNSPacket(DNSEngine->addrs);
|
||||
DNSPacket *p = new DNSPacket(&DNSEngine->addrs);
|
||||
p->flags = DNS_QUERYFLAGS_RD;
|
||||
|
||||
p->id = this->id;
|
||||
p->questions.push_back(*this);
|
||||
DNSEngine->SendPacket(p);
|
||||
DNSEngine->udpsock->Reply(p);
|
||||
}
|
||||
|
||||
void DNSRequest::OnError(const DNSQuery *r)
|
||||
@@ -269,8 +269,10 @@ ResourceRecord DNSPacket::UnpackResourceRecord(const unsigned char *input, unsig
|
||||
return record;
|
||||
}
|
||||
|
||||
DNSPacket::DNSPacket(const sockaddrs &a) : DNSQuery(), addr(a), id(0), flags(0)
|
||||
DNSPacket::DNSPacket(sockaddrs *a) : DNSQuery(), id(0), flags(0)
|
||||
{
|
||||
if (a)
|
||||
addr = *a;
|
||||
}
|
||||
|
||||
void DNSPacket::Fill(const unsigned char *input, const unsigned short len)
|
||||
@@ -439,6 +441,7 @@ unsigned short DNSPacket::Pack(unsigned char *output, unsigned short output_size
|
||||
pos += 16;
|
||||
break;
|
||||
}
|
||||
case DNS_QUERY_NS:
|
||||
case DNS_QUERY_CNAME:
|
||||
case DNS_QUERY_PTR:
|
||||
{
|
||||
@@ -450,8 +453,47 @@ unsigned short DNSPacket::Pack(unsigned char *output, unsigned short output_size
|
||||
|
||||
this->PackName(output, output_size, pos, rr.rdata);
|
||||
|
||||
i = htons(pos - packet_pos_save - 2);
|
||||
memcpy(&output[packet_pos_save], &i, 2);
|
||||
s = htons(pos - packet_pos_save - 2);
|
||||
memcpy(&output[packet_pos_save], &s, 2);
|
||||
break;
|
||||
}
|
||||
case DNS_QUERY_SOA:
|
||||
{
|
||||
if (pos + 2 >= output_size)
|
||||
throw SocketException("Unable to pack packet");
|
||||
|
||||
unsigned short packet_pos_save = pos;
|
||||
pos += 2;
|
||||
|
||||
this->PackName(output, output_size, pos, Config->DNSSOANS);
|
||||
this->PackName(output, output_size, pos, Config->DNSSOAAdmin.replace_all_cs('@', '.'));
|
||||
|
||||
if (pos + 20 >= output_size)
|
||||
throw SocketException("Unable to pack SOA");
|
||||
|
||||
l = htonl(DNSEngine->GetSerial());
|
||||
memcpy(&output[pos], &l, 4);
|
||||
pos += 4;
|
||||
|
||||
l = htonl(Config->DNSSOARefresh); // Refresh
|
||||
memcpy(&output[pos], &l, 4);
|
||||
pos += 4;
|
||||
|
||||
l = htonl(Config->DNSSOARefresh); // Retry
|
||||
memcpy(&output[pos], &l, 4);
|
||||
pos += 4;
|
||||
|
||||
l = htonl(604800); // Expire
|
||||
memcpy(&output[pos], &l, 4);
|
||||
pos += 4;
|
||||
|
||||
l = htonl(0); // Minimum
|
||||
memcpy(&output[pos], &l, 4);
|
||||
pos += 4;
|
||||
|
||||
s = htons(pos - packet_pos_save - 2);
|
||||
memcpy(&output[packet_pos_save], &s, 2);
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -462,25 +504,164 @@ unsigned short DNSPacket::Pack(unsigned char *output, unsigned short output_size
|
||||
return pos;
|
||||
}
|
||||
|
||||
DNSManager::DNSManager(const Anope::string &nameserver, const Anope::string &ip, int port) : Timer(300, Anope::CurTime, true), Socket(-1, nameserver.find(':') != Anope::string::npos, SOCK_DGRAM)
|
||||
DNSManager::TCPSocket::Client::Client(TCPSocket *ls, int fd, const sockaddrs &addr) : Socket(fd, ls->IsIPv6()), ClientSocket(ls, addr), Timer(5), tcpsock(ls), packet(NULL), length(0)
|
||||
{
|
||||
this->addrs.pton(this->IPv6 ? AF_INET6 : AF_INET, nameserver, port);
|
||||
Log(LOG_DEBUG_2) << "Resolver: New client from " << addr.addr();
|
||||
}
|
||||
|
||||
DNSManager::TCPSocket::Client::~Client()
|
||||
{
|
||||
Log(LOG_DEBUG_2) << "Resolver: Exiting client from " << clientaddr.addr();
|
||||
delete packet;
|
||||
}
|
||||
|
||||
void DNSManager::TCPSocket::Client::Reply(DNSPacket *p) anope_override
|
||||
{
|
||||
delete packet;
|
||||
packet = p;
|
||||
SocketEngine::MarkWritable(this);
|
||||
}
|
||||
|
||||
bool DNSManager::TCPSocket::Client::ProcessRead()
|
||||
{
|
||||
Log(LOG_DEBUG_2) << "Resolver: Reading from DNS TCP socket";
|
||||
|
||||
int i = recv(this->GetFD(), reinterpret_cast<char *>(packet_buffer) + length, sizeof(packet_buffer) - length, 0);
|
||||
if (i <= 0)
|
||||
return false;
|
||||
|
||||
length += i;
|
||||
|
||||
short want_len = packet_buffer[0] << 8 | packet_buffer[1];
|
||||
if (length >= want_len - 2)
|
||||
{
|
||||
int len = length - 2;
|
||||
length = 0;
|
||||
return DNSEngine->HandlePacket(this, packet_buffer + 2, len, NULL);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DNSManager::TCPSocket::Client::ProcessWrite()
|
||||
{
|
||||
Log(LOG_DEBUG_2) << "Resolver: Writing to DNS TCP socket";
|
||||
|
||||
if (packet != NULL)
|
||||
{
|
||||
try
|
||||
{
|
||||
unsigned char buffer[524];
|
||||
unsigned short len = packet->Pack(buffer + 2, sizeof(buffer) - 2);
|
||||
|
||||
short s = htons(len);
|
||||
memcpy(buffer, &s, 2);
|
||||
len += 2;
|
||||
|
||||
send(this->GetFD(), reinterpret_cast<char *>(buffer), len, 0);
|
||||
}
|
||||
catch (const SocketException &) { }
|
||||
|
||||
delete packet;
|
||||
packet = NULL;
|
||||
}
|
||||
|
||||
SocketEngine::ClearWritable(this);
|
||||
return true; /* Do not return false here, bind is unhappy we close the connection so soon after sending */
|
||||
}
|
||||
|
||||
DNSManager::TCPSocket::TCPSocket(const Anope::string &ip, int port) : Socket(-1, ip.find(':') != Anope::string::npos), ListenSocket(ip, port, ip.find(':') != Anope::string::npos)
|
||||
{
|
||||
}
|
||||
|
||||
ClientSocket *DNSManager::TCPSocket::OnAccept(int fd, const sockaddrs &addr) anope_override
|
||||
{
|
||||
return new Client(this, fd, addr);
|
||||
}
|
||||
|
||||
DNSManager::UDPSocket::UDPSocket(const Anope::string &ip, int port) : Socket(-1, ip.find(':') != Anope::string::npos, SOCK_DGRAM)
|
||||
{
|
||||
}
|
||||
|
||||
DNSManager::UDPSocket::~UDPSocket()
|
||||
{
|
||||
for (unsigned i = 0; i < packets.size(); ++i)
|
||||
delete packets[i];
|
||||
}
|
||||
|
||||
void DNSManager::UDPSocket::Reply(DNSPacket *p)
|
||||
{
|
||||
packets.push_back(p);
|
||||
SocketEngine::MarkWritable(this);
|
||||
}
|
||||
|
||||
bool DNSManager::UDPSocket::ProcessRead()
|
||||
{
|
||||
Log(LOG_DEBUG_2) << "Resolver: Reading from DNS UDP socket";
|
||||
|
||||
unsigned char packet_buffer[524];
|
||||
sockaddrs from_server;
|
||||
socklen_t x = sizeof(from_server);
|
||||
int length = recvfrom(this->GetFD(), reinterpret_cast<char *>(&packet_buffer), sizeof(packet_buffer), 0, &from_server.sa, &x);
|
||||
return DNSEngine->HandlePacket(this, packet_buffer, length, &from_server);
|
||||
}
|
||||
|
||||
bool DNSManager::UDPSocket::ProcessWrite()
|
||||
{
|
||||
Log(LOG_DEBUG_2) << "Resolver: Writing to DNS UDP socket";
|
||||
|
||||
DNSPacket *r = !packets.empty() ? packets.front() : NULL;
|
||||
if (r != NULL)
|
||||
{
|
||||
try
|
||||
{
|
||||
unsigned char buffer[524];
|
||||
unsigned short len = r->Pack(buffer, sizeof(buffer));
|
||||
|
||||
sendto(this->GetFD(), reinterpret_cast<char *>(buffer), len, 0, &r->addr.sa, r->addr.size());
|
||||
}
|
||||
catch (const SocketException &) { }
|
||||
|
||||
delete r;
|
||||
packets.pop_front();
|
||||
}
|
||||
|
||||
if (packets.empty())
|
||||
SocketEngine::ClearWritable(this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
DNSManager::DNSManager(const Anope::string &nameserver, const Anope::string &ip, int port) : Timer(300, Anope::CurTime, true), serial(0), last_year(0), last_day(0), last_num(0), tcpsock(NULL), udpsock(NULL)
|
||||
{
|
||||
this->addrs.pton(nameserver.find(':') != Anope::string::npos ? AF_INET6 : AF_INET, nameserver, port);
|
||||
|
||||
try
|
||||
{
|
||||
this->Bind(ip, port);
|
||||
udpsock = new UDPSocket(ip, port);
|
||||
}
|
||||
catch (const SocketException &ex)
|
||||
{
|
||||
Log() << "Unable to create socket for DNSManager: " << ex.GetReason();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
udpsock->Bind(ip, port);
|
||||
tcpsock = new TCPSocket(ip, port);
|
||||
}
|
||||
catch (const SocketException &ex)
|
||||
{
|
||||
/* This error can be from normal operation as most people don't use services to handle DNS queries, so put it in debug log */
|
||||
Log(LOG_DEBUG) << "Unable to bind DNSManager to port " << port << ": " << ex.GetReason();
|
||||
}
|
||||
|
||||
this->UpdateSerial();
|
||||
}
|
||||
|
||||
DNSManager::~DNSManager()
|
||||
{
|
||||
for (unsigned i = this->packets.size(); i > 0; --i)
|
||||
delete this->packets[i - 1];
|
||||
this->packets.clear();
|
||||
delete udpsock;
|
||||
delete tcpsock;
|
||||
|
||||
for (std::map<unsigned short, DNSRequest *>::iterator it = this->requests.begin(), it_end = this->requests.end(); it != it_end; ++it)
|
||||
{
|
||||
@@ -499,19 +680,12 @@ DNSManager::~DNSManager()
|
||||
DNSEngine = NULL;
|
||||
}
|
||||
|
||||
bool DNSManager::ProcessRead()
|
||||
bool DNSManager::HandlePacket(ReplySocket *s, const unsigned char *const packet_buffer, int length, sockaddrs *from)
|
||||
{
|
||||
Log(LOG_DEBUG_2) << "Resolver: Reading from DNS socket";
|
||||
|
||||
unsigned char packet_buffer[524];
|
||||
sockaddrs from_server;
|
||||
socklen_t x = sizeof(from_server);
|
||||
int length = recvfrom(this->GetFD(), reinterpret_cast<char *>(&packet_buffer), sizeof(packet_buffer), 0, &from_server.sa, &x);
|
||||
|
||||
if (length < DNSPacket::HEADER_LENGTH)
|
||||
return true;
|
||||
|
||||
DNSPacket recv_packet(from_server);
|
||||
DNSPacket recv_packet(from);
|
||||
|
||||
try
|
||||
{
|
||||
@@ -525,18 +699,65 @@ bool DNSManager::ProcessRead()
|
||||
|
||||
if (!(recv_packet.flags & DNS_QUERYFLAGS_QR))
|
||||
{
|
||||
if (recv_packet.questions.empty())
|
||||
{
|
||||
Log(LOG_DEBUG_2) << "Resolver: Received a question with no questions?";
|
||||
return true;
|
||||
}
|
||||
|
||||
DNSPacket *packet = new DNSPacket(recv_packet);
|
||||
packet->flags |= DNS_QUERYFLAGS_QR; /* This is a reponse */
|
||||
packet->flags |= DNS_QUERYFLAGS_AA; /* And we are authoritative */
|
||||
|
||||
packet->answers.clear();
|
||||
packet->authorities.clear();
|
||||
packet->additional.clear();
|
||||
|
||||
for (unsigned i = 0; i < recv_packet.questions.size(); ++i)
|
||||
{
|
||||
const Question& q = recv_packet.questions[i];
|
||||
|
||||
if (q.type == DNS_QUERY_AXFR || q.type == DNS_QUERY_SOA)
|
||||
{
|
||||
ResourceRecord rr(q.name, DNS_QUERY_SOA);
|
||||
packet->answers.push_back(rr);
|
||||
|
||||
if (q.type == DNS_QUERY_AXFR)
|
||||
{
|
||||
ResourceRecord rr2(q.name, DNS_QUERY_NS);
|
||||
rr2.rdata = Config->DNSSOANS;
|
||||
packet->answers.push_back(rr2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
FOREACH_MOD(I_OnDnsRequest, OnDnsRequest(recv_packet, packet));
|
||||
|
||||
DNSEngine->SendPacket(packet);
|
||||
for (unsigned i = 0; i < recv_packet.questions.size(); ++i)
|
||||
{
|
||||
const Question& q = recv_packet.questions[i];
|
||||
|
||||
if (q.type == DNS_QUERY_AXFR)
|
||||
{
|
||||
ResourceRecord rr(q.name, DNS_QUERY_SOA);
|
||||
packet->answers.push_back(rr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
s->Reply(packet);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this->addrs != from_server)
|
||||
if (from == NULL)
|
||||
{
|
||||
Log(LOG_DEBUG_2) << "Resolver: Received an answer from the wrong nameserver, Bad NAT or DNS forging attempt? '" << this->addrs.addr() << "' != '" << from_server.addr() << "'";
|
||||
Log(LOG_DEBUG_2) << "Resolver: Received an answer over TCP. This is not supported.";
|
||||
return true;
|
||||
}
|
||||
else if (this->addrs != *from)
|
||||
{
|
||||
Log(LOG_DEBUG_2) << "Resolver: Received an answer from the wrong nameserver, Bad NAT or DNS forging attempt? '" << this->addrs.addr() << "' != '" << from->addr() << "'";
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -604,32 +825,6 @@ bool DNSManager::ProcessRead()
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DNSManager::ProcessWrite()
|
||||
{
|
||||
Log(LOG_DEBUG_2) << "Resolver: Writing to DNS socket";
|
||||
|
||||
DNSPacket *r = !DNSEngine->packets.empty() ? DNSEngine->packets.front() : NULL;
|
||||
if (r != NULL)
|
||||
{
|
||||
try
|
||||
{
|
||||
unsigned char buffer[524];
|
||||
unsigned short len = r->Pack(buffer, sizeof(buffer));
|
||||
|
||||
sendto(this->GetFD(), reinterpret_cast<char *>(buffer), len, 0, &r->addr.sa, r->addr.size());
|
||||
}
|
||||
catch (const SocketException &) { }
|
||||
|
||||
delete r;
|
||||
DNSEngine->packets.pop_front();
|
||||
}
|
||||
|
||||
if (DNSEngine->packets.empty())
|
||||
SocketEngine::ClearWritable(this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DNSManager::AddCache(DNSQuery &r)
|
||||
{
|
||||
for (unsigned i = 0; i < r.answers.size(); ++i)
|
||||
@@ -700,17 +895,42 @@ void DNSManager::Cleanup(Module *mod)
|
||||
}
|
||||
}
|
||||
|
||||
std::deque<DNSPacket *>& DNSManager::GetPackets()
|
||||
void DNSManager::UpdateSerial()
|
||||
{
|
||||
return this->packets;
|
||||
char timebuf[20];
|
||||
tm *tm = localtime(&Anope::CurTime);
|
||||
|
||||
if (!tm)
|
||||
{
|
||||
Log(LOG_DEBUG) << "Resolver: Unable to update serial";
|
||||
return;
|
||||
}
|
||||
|
||||
if (tm->tm_yday != last_day || tm->tm_year != last_year)
|
||||
{
|
||||
last_day = tm->tm_yday;
|
||||
last_year = tm->tm_year;
|
||||
last_num = 0;
|
||||
}
|
||||
|
||||
++last_num;
|
||||
|
||||
int i = strftime(timebuf, sizeof(timebuf), "%Y%m%d", tm);
|
||||
snprintf(timebuf + i, sizeof(timebuf) - i, "%d", last_num);
|
||||
|
||||
try
|
||||
{
|
||||
serial = convertTo<uint32_t>(timebuf);
|
||||
}
|
||||
catch (const ConvertException &)
|
||||
{
|
||||
Log(LOG_DEBUG) << "Resolver: Unable to update serial";
|
||||
}
|
||||
}
|
||||
|
||||
void DNSManager::SendPacket(DNSPacket *p)
|
||||
uint32_t DNSManager::GetSerial() const
|
||||
{
|
||||
Log(LOG_DEBUG_2) << "Resolver: Queueing packet " << p->id;
|
||||
this->packets.push_back(p);
|
||||
|
||||
SocketEngine::MarkWritable(this);
|
||||
return serial;
|
||||
}
|
||||
|
||||
DNSQuery DNSManager::BlockingQuery(const Anope::string &mask, QueryType qt)
|
||||
|
||||
+1
-1
@@ -535,7 +535,7 @@ void Socket::ProcessError()
|
||||
* @param port The port to listen on
|
||||
* @param ipv6 true for ipv6
|
||||
*/
|
||||
ListenSocket::ListenSocket(const Anope::string &bindip, int port, bool ipv6) : Socket(-1, ipv6)
|
||||
ListenSocket::ListenSocket(const Anope::string &bindip, int port, bool ipv6)
|
||||
{
|
||||
this->SetNonBlocking();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user