summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data/example.conf46
-rw-r--r--data/modules.example.conf48
-rw-r--r--data/operserv.example.conf3
-rw-r--r--include/anope.h7
-rw-r--r--include/config.h14
-rw-r--r--include/defs.h1
-rw-r--r--include/dns.h278
-rw-r--r--include/module.h1
-rw-r--r--modules/commands/os_dns.cpp15
-rw-r--r--modules/extra/dns.h178
-rw-r--r--modules/extra/m_dns.cpp1025
-rw-r--r--modules/extra/m_dnsbl.cpp16
-rw-r--r--src/config.cpp44
-rw-r--r--src/dns.cpp963
-rw-r--r--src/misc.cpp31
-rw-r--r--src/module.cpp4
-rw-r--r--src/uplink.cpp8
17 files changed, 1314 insertions, 1368 deletions
diff --git a/data/example.conf b/data/example.conf
index 205a0ead9..9f4a568dd 100644
--- a/data/example.conf
+++ b/data/example.conf
@@ -1035,52 +1035,6 @@ mail
}
/*
- * [OPTIONAL] DNS Config
- *
- * This section is used to configure DNS.
- * 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
-{
- /*
- * The nameserver to use for resolving hostnames, must be an IP or a resolver configuration file.
- * The below should work fine on all unix like systems. Windows users will have to find their nameservers
- * from ipconfig /all and put the IP here
- */
- nameserver = "/etc/resolv.conf"
- #nameserver = "127.0.0.1"
-
- /*
- * How long to wait in seconds before a DNS query has timed out
- */
- 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
- * require Anope to be started as root. If you do this, it is recommended you
- * set options:user and options:group so Anope can change users after binding
- * to this port.
- */
- ip = "0.0.0.0"
- port = 53
-
- /*
- * SOA record information.
- */
- admin = "admin@example.com"
- /* This should be the names of the public facing nameserver serving the records */
- nameservers = "ns1.example.com ns2.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
-}
-
-/*
* [REQUIRED] Database configuration.
*
* This section is used to configure databases used by Anope.
diff --git a/data/modules.example.conf b/data/modules.example.conf
index a9c7d5cb9..7137975fa 100644
--- a/data/modules.example.conf
+++ b/data/modules.example.conf
@@ -17,13 +17,59 @@
module { name = "help" }
/*
+ * m_dns
+ *
+ * Adds support for the DNS protocol. By itself this module does nothing useful,
+ * but other modules such as m_dnsbl and os_dns require this.
+ */
+#module { name = "m_dns" }
+dns
+{
+ /*
+ * The nameserver to use for resolving hostnames, must be an IP or a resolver configuration file.
+ * The below should work fine on all unix like systems. Windows users will have to find their nameservers
+ * from ipconfig /all and put the IP here
+ */
+ nameserver = "/etc/resolv.conf"
+ #nameserver = "127.0.0.1"
+
+ /*
+ * How long to wait in seconds before a DNS query has timed out
+ */
+ timeout = 5
+
+ /* Only edit below if you are expecting to use os_dns or otherwise answer DNS queries. */
+
+ /*
+ * The IP and port services use to listen for DNS queries.
+ * Note that ports less than 1024 are privileged on UNIX/Linux systems, and
+ * require Anope to be started as root. If you do this, it is recommended you
+ * set options:user and options:group so Anope can change users after binding
+ * to this port.
+ */
+ ip = "0.0.0.0"
+ port = 53
+
+ /*
+ * SOA record information.
+ */
+ admin = "admin@example.com"
+ /* This should be the names of the public facing nameserver serving the records */
+ nameservers = "ns1.example.com ns2.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
+}
+
+/*
* m_dnsbl
*
* Allows configurable DNS blacklists to check connecting users against. If a user
* is found on the blacklist they will be immediately banned. This is a crucial module
* to prevent bot attacks.
*/
-module { name = "m_dnsbl" }
+#module { name = "m_dnsbl" }
m_dnsbl
{
/*
diff --git a/data/operserv.example.conf b/data/operserv.example.conf
index 8a887c5ea..cb3f19cc4 100644
--- a/data/operserv.example.conf
+++ b/data/operserv.example.conf
@@ -359,6 +359,8 @@ defcon
*
* Provides the command operserv/dns.
*
+ * This module requires that m_dns is loaded.
+ *
* This module allows controlling a DNS zone. This is useful for
* controlling what servers users are placed on for load balancing,
* and to automatically remove split servers.
@@ -371,6 +373,7 @@ defcon
*
* To do this using BIND, configure similar to:
*
+ * options { max-refresh-time 60; };
* zone "irc.example.com" IN {
* type slave;
* masters { 127.0.0.1 port 5353; };
diff --git a/include/anope.h b/include/anope.h
index c241524ab..bf82e6d78 100644
--- a/include/anope.h
+++ b/include/anope.h
@@ -506,6 +506,13 @@ namespace Anope
* @param Raw message from the uplink
*/
extern void Process(const Anope::string &);
+
+ /** Does a blocking dns query and returns the first IP.
+ * @param host host to look up
+ * @param type inet addr type
+ * @return the IP if it was found, else the host
+ */
+ extern Anope::string Resolve(const Anope::string &host, int type);
}
/** sepstream allows for splitting token seperated lists.
diff --git a/include/config.h b/include/config.h
index b53047a0c..bedbfca23 100644
--- a/include/config.h
+++ b/include/config.h
@@ -478,20 +478,6 @@ class CoreExport ServerConfig
Anope::string MailEmailchangeSubject, MailEmailchangeMessage;
Anope::string MailMemoSubject, MailMemoMessage;
- /* Nameserver to use for resolving hostnames */
- Anope::string NameServer;
- /* Time before a DNS query is considered dead */
- time_t DNSTimeout;
- /* 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;
/* Allow users to set kill immed on */
diff --git a/include/defs.h b/include/defs.h
index 437f0f08d..f592b3a4f 100644
--- a/include/defs.h
+++ b/include/defs.h
@@ -48,7 +48,6 @@ class User;
class XLine;
class XLineManager;
struct BadWord;
-namespace DNS { struct Query; }
struct Exception;
struct MemoInfo;
struct ModeLock;
diff --git a/include/dns.h b/include/dns.h
deleted file mode 100644
index fee9da8a9..000000000
--- a/include/dns.h
+++ /dev/null
@@ -1,278 +0,0 @@
-/*
- *
- * (C) 2003-2012 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- *
- */
-
-#ifndef DNS_H
-#define DNS_H
-
-#include "sockets.h"
-#include "timers.h"
-#include "config.h"
-
-namespace DNS
-{
-
- /** Valid query types
- */
- enum QueryType
- {
- /* Nothing */
- QUERY_NONE,
- /* A simple A lookup */
- QUERY_A = 1,
- /* An authoritative name server */
- QUERY_NS = 2,
- /* A CNAME lookup */
- QUERY_CNAME = 5,
- /* Start of a zone of authority */
- QUERY_SOA = 6,
- /* Reverse DNS lookup */
- QUERY_PTR = 12,
- /* IPv6 AAAA lookup */
- QUERY_AAAA = 28,
- /* Zone transfer */
- QUERY_AXFR = 252
- };
-
- /** Flags that can be AND'd into DNSPacket::flags to receive certain values
- */
- enum
- {
- QUERYFLAGS_QR = 0x8000,
- QUERYFLAGS_OPCODE = 0x7800,
- QUERYFLAGS_AA = 0x400,
- QUERYFLAGS_TC = 0x200,
- QUERYFLAGS_RD = 0x100,
- QUERYFLAGS_RA = 0x80,
- QUERYFLAGS_Z = 0x70,
- QUERYFLAGS_RCODE = 0xF
- };
-
- enum Error
- {
- ERROR_NONE,
- ERROR_UNKNOWN,
- ERROR_UNLOADED,
- ERROR_TIMEOUT,
- ERROR_NOT_AN_ANSWER,
- ERROR_NONSTANDARD_QUERY,
- ERROR_FORMAT_ERROR,
- ERROR_SERVER_FAILURE,
- ERROR_DOMAIN_NOT_FOUND,
- ERROR_NOT_IMPLEMENTED,
- ERROR_REFUSED,
- ERROR_NO_RECORDS,
- ERROR_INVALIDTYPE
- };
-
-
- struct CoreExport Question
- {
- Anope::string name;
- QueryType type;
- unsigned short qclass;
-
- Question();
- Question(const Anope::string &, QueryType, unsigned short = 1);
- };
-
- struct CoreExport ResourceRecord : public Question
- {
- unsigned int ttl;
- Anope::string rdata;
- time_t created;
-
- ResourceRecord(const Anope::string &, QueryType, unsigned short = 1);
- ResourceRecord(const Question &);
- };
-
- struct CoreExport Query
- {
- std::vector<Question> questions;
- std::vector<ResourceRecord> answers, authorities, additional;
- Error error;
-
- Query();
- Query(const Question &q);
- };
-
- /** A DNS query.
- */
- class CoreExport Request : public Timer, public Question
- {
- /* Use result cache if available */
- bool use_cache;
-
- public:
- /* Request id */
- unsigned short id;
- /* Creator of this request */
- Module *creator;
-
- Request(const Anope::string &addr, QueryType qt, bool cache = false, Module *c = NULL);
- virtual ~Request();
-
- void Process();
-
- /** Called when this request succeeds
- * @param r The query sent back from the nameserver
- */
- virtual void OnLookupComplete(const Query *r) = 0;
-
- /** Called when this request fails or times out.
- * @param r The query sent back from the nameserver, check the error code.
- */
- virtual void OnError(const Query *r);
-
- /** Used to time out the query, Calls OnError and lets the TimerManager
- * delete this request.
- */
- void Tick(time_t) anope_override;
- };
-
- /** A full packet sent or recieved to/from the nameserver
- */
- class Packet : public Query
- {
- static const int POINTER = 0xC0;
- static const int LABEL = 0x3F;
-
- void PackName(unsigned char *output, unsigned short output_size, unsigned short &pos, const Anope::string &name);
- Anope::string UnpackName(const unsigned char *input, unsigned short input_size, unsigned short &pos);
-
- Question UnpackQuestion(const unsigned char *input, unsigned short input_size, unsigned short &pos);
- ResourceRecord UnpackResourceRecord(const unsigned char *input, unsigned short input_size, unsigned short &poss);
- public:
- static const int HEADER_LENGTH = 12;
-
- /* Source or destination of the packet */
- sockaddrs addr;
- /* ID for this packet */
- unsigned short id;
- /* Flags on the packet */
- unsigned short flags;
-
- Packet(sockaddrs *a);
- void Fill(const unsigned char *input, const unsigned short len);
- unsigned short Pack(unsigned char *output, unsigned short output_size);
- };
-
- /** DNS manager
- */
- class CoreExport Manager : public Timer
- {
- class ReplySocket : public virtual Socket
- {
- public:
- virtual ~ReplySocket() { }
- virtual void Reply(Packet *p) = 0;
- };
-
- /* Listens for TCP requests */
- class TCPSocket : public ListenSocket
- {
- /* A TCP client */
- class Client : public ClientSocket, public Timer, public ReplySocket
- {
- TCPSocket *tcpsock;
- Packet *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(Packet *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<Packet *> packets;
- public:
-
- UDPSocket(const Anope::string &ip, int port);
- ~UDPSocket();
-
- void Reply(Packet *p) anope_override;
-
- std::deque<Packet *>& GetPackets() { return packets; }
-
- bool ProcessRead() anope_override;
-
- bool ProcessWrite() anope_override;
- };
-
- typedef std::multimap<Anope::string, ResourceRecord, ci::less> cache_map;
- cache_map cache;
-
- bool listen;
- uint32_t serial;
-
- public:
- TCPSocket *tcpsock;
- UDPSocket *udpsock;
-
- sockaddrs addrs;
- std::map<unsigned short, Request *> requests;
-
- Manager(const Anope::string &nameserver, const Anope::string &ip, int port);
- ~Manager();
-
- bool HandlePacket(ReplySocket *s, const unsigned char *const data, int len, sockaddrs *from);
-
- /** Add a record to the dns cache
- * @param r The record
- */
- void AddCache(Query &r);
-
- /** Check the DNS cache to see if request can be handled by a cached result
- * @return true if a cached result was found.
- */
- bool CheckCache(Request *request);
-
- /** Tick this timer, used to clear the DNS cache.
- */
- void Tick(time_t now) anope_override;
-
- /** Cleanup all pending DNS queries for a module
- * @param mod The module
- */
- void Cleanup(Module *mod);
-
- 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
- * need a blocking query use the DNSRequest system
- */
- static Query BlockingQuery(const Anope::string &mask, QueryType qt);
- };
-
- extern CoreExport Manager *Engine;
-
-} // namespace DNS
-
-#endif // DNS_H
-
-
diff --git a/include/module.h b/include/module.h
index 197899ca8..e2fc18eaf 100644
--- a/include/module.h
+++ b/include/module.h
@@ -21,7 +21,6 @@
#include "channels.h"
#include "commands.h"
#include "config.h"
-#include "dns.h"
#include "extensible.h"
#include "hashcomp.h"
#include "language.h"
diff --git a/modules/commands/os_dns.cpp b/modules/commands/os_dns.cpp
index 6e5689396..ec72f5a5d 100644
--- a/modules/commands/os_dns.cpp
+++ b/modules/commands/os_dns.cpp
@@ -6,6 +6,9 @@
*/
#include "module.h"
+#include "../extra/dns.h"
+
+static ServiceReference<DNS::Manager> dnsmanager("DNS::Manager", "dns/manager");
class DNSServer;
static std::vector<DNSServer *> dns_servers;
@@ -44,8 +47,8 @@ class DNSServer : public Serializable
void Pool(bool p)
{
pooled = p;
- if (DNS::Engine)
- DNS::Engine->UpdateSerial();
+ if (dnsmanager)
+ dnsmanager->UpdateSerial();
}
@@ -220,8 +223,8 @@ 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())
- DNS::Engine->UpdateSerial();
+ if (s->Pooled() && dnsmanager)
+ dnsmanager->UpdateSerial();
}
void DelIP(CommandSource &source, const std::vector<Anope::string> &params)
@@ -247,8 +250,8 @@ class CommandOSDNS : public Command
s->Pool(false);
}
- if (s->Pooled())
- DNS::Engine->UpdateSerial();
+ if (s->Pooled() && dnsmanager)
+ dnsmanager->UpdateSerial();
return;
}
diff --git a/modules/extra/dns.h b/modules/extra/dns.h
new file mode 100644
index 000000000..9d141249b
--- /dev/null
+++ b/modules/extra/dns.h
@@ -0,0 +1,178 @@
+/*
+ *
+ * (C) 2003-2012 Anope Team
+ * Contact us at team@anope.org
+ *
+ * Please read COPYING and README for further details.
+ *
+ * Based on the original code of Epona by Lara.
+ * Based on the original code of Services by Andy Church.
+ *
+ */
+
+#ifndef DNS_H
+#define DNS_H
+
+namespace DNS
+{
+ /** Valid query types
+ */
+ enum QueryType
+ {
+ /* Nothing */
+ QUERY_NONE,
+ /* A simple A lookup */
+ QUERY_A = 1,
+ /* An authoritative name server */
+ QUERY_NS = 2,
+ /* A CNAME lookup */
+ QUERY_CNAME = 5,
+ /* Start of a zone of authority */
+ QUERY_SOA = 6,
+ /* Reverse DNS lookup */
+ QUERY_PTR = 12,
+ /* IPv6 AAAA lookup */
+ QUERY_AAAA = 28,
+ /* Zone transfer */
+ QUERY_AXFR = 252
+ };
+
+ /** Flags that can be AND'd into DNSPacket::flags to receive certain values
+ */
+ enum
+ {
+ QUERYFLAGS_QR = 0x8000,
+ QUERYFLAGS_OPCODE = 0x7800,
+ QUERYFLAGS_AA = 0x400,
+ QUERYFLAGS_TC = 0x200,
+ QUERYFLAGS_RD = 0x100,
+ QUERYFLAGS_RA = 0x80,
+ QUERYFLAGS_Z = 0x70,
+ QUERYFLAGS_RCODE = 0xF
+ };
+
+ enum Error
+ {
+ ERROR_NONE,
+ ERROR_UNKNOWN,
+ ERROR_UNLOADED,
+ ERROR_TIMEOUT,
+ ERROR_NOT_AN_ANSWER,
+ ERROR_NONSTANDARD_QUERY,
+ ERROR_FORMAT_ERROR,
+ ERROR_SERVER_FAILURE,
+ ERROR_DOMAIN_NOT_FOUND,
+ ERROR_NOT_IMPLEMENTED,
+ ERROR_REFUSED,
+ ERROR_NO_RECORDS,
+ ERROR_INVALIDTYPE
+ };
+
+ struct Question
+ {
+ Anope::string name;
+ QueryType type;
+ unsigned short qclass;
+
+ Question() : type(QUERY_NONE), qclass(0) { }
+ Question(const Anope::string &n, QueryType t, unsigned short c = 1) : name(n), type(t), qclass(c) { }
+ };
+
+ struct ResourceRecord : public Question
+ {
+ unsigned int ttl;
+ Anope::string rdata;
+ time_t created;
+
+ ResourceRecord(const Anope::string &n, QueryType t, unsigned short c = 1) : Question(n, t, c), ttl(0), created(Anope::CurTime) { }
+ ResourceRecord(const Question &q) : Question(q), ttl(0), created(Anope::CurTime) { }
+ };
+
+ struct Query
+ {
+ std::vector<Question> questions;
+ std::vector<ResourceRecord> answers, authorities, additional;
+ Error error;
+
+ Query() : error(ERROR_NONE) { }
+ Query(const Question &q) : error(ERROR_NONE) { questions.push_back(q); }
+ };
+
+ class Packet : public Query
+ {
+ public:
+ static const int POINTER = 0xC0;
+ static const int LABEL = 0x3F;
+ static const int HEADER_LENGTH = 12;
+
+ virtual ~Packet() { }
+ };
+
+ class ReplySocket;
+ class Request;
+
+ /** DNS manager
+ */
+ class Manager : public Service
+ {
+ public:
+ Manager(Module *creator) : Service(creator, "DNS::Manager", "dns/manager") { }
+ virtual ~Manager() { }
+
+ virtual void Process(Request *req) = 0;
+ virtual void RemoveRequest(Request *req) = 0;
+
+ virtual bool HandlePacket(ReplySocket *s, const unsigned char *const data, int len, sockaddrs *from) = 0;
+
+ virtual void UpdateSerial() = 0;
+ virtual uint32_t GetSerial() const = 0;
+ };
+
+ /** A DNS query.
+ */
+ class Request : public Timer, public Question
+ {
+ Manager *manager;
+ public:
+ /* Use result cache if available */
+ bool use_cache;
+ /* Request id */
+ unsigned short id;
+ /* Creator of this request */
+ Module *creator;
+
+ Request(Manager *mgr, Module *c, const Anope::string &addr, QueryType qt, bool cache = false) : Timer(0), Question(addr, qt), manager(mgr),
+ use_cache(cache), id(0), creator(c) { }
+
+ virtual ~Request()
+ {
+ manager->RemoveRequest(this);
+ }
+
+ /** Called when this request succeeds
+ * @param r The query sent back from the nameserver
+ */
+ virtual void OnLookupComplete(const Query *r) = 0;
+
+ /** Called when this request fails or times out.
+ * @param r The query sent back from the nameserver, check the error code.
+ */
+ virtual void OnError(const Query *r) { }
+
+ /** Used to time out the query, xalls OnError and lets the TimerManager
+ * delete this request.
+ */
+ void Tick(time_t) anope_override
+ {
+ Log(LOG_DEBUG_2) << "Resolver: timeout for query " << this->name;
+ Query rr(*this);
+ rr.error = ERROR_TIMEOUT;
+ this->OnError(&rr);
+ }
+ };
+
+} // namespace DNS
+
+#endif // DNS_H
+
+
diff --git a/modules/extra/m_dns.cpp b/modules/extra/m_dns.cpp
new file mode 100644
index 000000000..867d81d84
--- /dev/null
+++ b/modules/extra/m_dns.cpp
@@ -0,0 +1,1025 @@
+/*
+ *
+ * (C) 2003-2012 Anope Team
+ * Contact us at team@anope.org
+ *
+ * Please read COPYING and README for further details.
+ *
+ * Based on the original code of Epona by Lara.
+ * Based on the original code of Services by Andy Church.
+ *
+ */
+
+#include "module.h"
+#include "dns.h"
+
+using namespace DNS;
+
+namespace
+{
+ Anope::string admin, nameservers;
+ int refresh;
+ time_t timeout;
+}
+
+/** A full packet sent or recieved to/from the nameserver
+ */
+class MyPacket : public Packet
+{
+ void PackName(unsigned char *output, unsigned short output_size, unsigned short &pos, const Anope::string &name)
+ {
+ if (name.length() + 2 > output_size)
+ throw SocketException("Unable to pack name");
+
+ Log(LOG_DEBUG_2) << "Resolver: PackName packing " << name;
+
+ sepstream sep(name, '.');
+ Anope::string token;
+
+ while (sep.GetToken(token))
+ {
+ output[pos++] = token.length();
+ memcpy(&output[pos], token.c_str(), token.length());
+ pos += token.length();
+ }
+
+ output[pos++] = 0;
+ }
+
+ Anope::string UnpackName(const unsigned char *input, unsigned short input_size, unsigned short &pos)
+ {
+ Anope::string name;
+ unsigned short pos_ptr = pos, lowest_ptr = input_size;
+ bool compressed = false;
+
+ if (pos_ptr >= input_size)
+ throw SocketException("Unable to unpack name - no input");
+
+ while (input[pos_ptr] > 0)
+ {
+ unsigned short offset = input[pos_ptr];
+
+ if (offset & POINTER)
+ {
+ if ((offset & POINTER) != POINTER)
+ throw SocketException("Unable to unpack name - bogus compression header");
+ if (pos_ptr + 1 >= input_size)
+ throw SocketException("Unable to unpack name - bogus compression header");
+
+ /* Place pos at the second byte of the first (farthest) compression pointer */
+ if (compressed == false)
+ {
+ ++pos;
+ compressed = true;
+ }
+
+ pos_ptr = (offset & LABEL) << 8 | input[pos_ptr + 1];
+
+ /* Pointers can only go back */
+ if (pos_ptr >= lowest_ptr)
+ throw SocketException("Unable to unpack name - bogus compression pointer");
+ lowest_ptr = pos_ptr;
+ }
+ else
+ {
+ if (pos_ptr + offset + 1 >= input_size)
+ throw SocketException("Unable to unpack name - offset too large");
+ if (!name.empty())
+ name += ".";
+ for (unsigned i = 1; i <= offset; ++i)
+ name += input[pos_ptr + i];
+
+ pos_ptr += offset + 1;
+ if (compressed == false)
+ /* Move up pos */
+ pos = pos_ptr;
+ }
+ }
+
+ /* +1 pos either to one byte after the compression pointer or one byte after the ending \0 */
+ ++pos;
+
+ Log(LOG_DEBUG_2) << "Resolver: UnpackName successfully unpacked " << name;
+
+ return name;
+ }
+
+ Question UnpackQuestion(const unsigned char *input, unsigned short input_size, unsigned short &pos)
+ {
+ Question question;
+
+ question.name = this->UnpackName(input, input_size, pos);
+
+ if (pos + 4 > input_size)
+ throw SocketException("Unable to unpack question");
+
+ question.type = static_cast<QueryType>(input[pos] << 8 | input[pos + 1]);
+ pos += 2;
+
+ question.qclass = input[pos] << 8 | input[pos + 1];
+ pos += 2;
+
+ return question;
+ }
+
+ ResourceRecord UnpackResourceRecord(const unsigned char *input, unsigned short input_size, unsigned short &pos)
+ {
+ ResourceRecord record = static_cast<ResourceRecord>(this->UnpackQuestion(input, input_size, pos));
+
+ if (pos + 6 > input_size)
+ throw SocketException("Unable to unpack resource record");
+
+ record.ttl = (input[pos] << 24) | (input[pos + 1] << 16) | (input[pos + 2] << 8) | input[pos + 3];
+ pos += 4;
+
+ //record.rdlength = input[pos] << 8 | input[pos + 1];
+ pos += 2;
+
+ switch (record.type)
+ {
+ case QUERY_A:
+ {
+ if (pos + 4 > input_size)
+ throw SocketException("Unable to unpack resource record");
+
+ in_addr a;
+ a.s_addr = input[pos] | (input[pos + 1] << 8) | (input[pos + 2] << 16) | (input[pos + 3] << 24);
+ pos += 4;
+
+ sockaddrs addrs;
+ addrs.ntop(AF_INET, &a);
+
+ record.rdata = addrs.addr();
+ break;
+ }
+ case QUERY_AAAA:
+ {
+ if (pos + 16 > input_size)
+ throw SocketException("Unable to unpack resource record");
+
+ in6_addr a;
+ for (int j = 0; j < 16; ++j)
+ a.s6_addr[j] = input[pos + j];
+ pos += 16;
+
+ sockaddrs addrs;
+ addrs.ntop(AF_INET6, &a);
+
+ record.rdata = addrs.addr();
+ break;
+ }
+ case QUERY_CNAME:
+ case QUERY_PTR:
+ {
+ record.rdata = this->UnpackName(input, input_size, pos);
+ break;
+ }
+ default:
+ break;
+ }
+
+ Log(LOG_DEBUG_2) << "Resolver: " << record.name << " -> " << record.rdata;
+
+ return record;
+ }
+
+ public:
+ Manager *manager;
+ /* Source or destination of the packet */
+ sockaddrs addr;
+ /* ID for this packet */
+ unsigned short id;
+ /* Flags on the packet */
+ unsigned short flags;
+
+ MyPacket(Manager *m, sockaddrs *a) : manager(m), id(0), flags(0)
+ {
+ if (a)
+ addr = *a;
+ }
+
+ void Fill(const unsigned char *input, const unsigned short len)
+ {
+ if (len < HEADER_LENGTH)
+ throw SocketException("Unable to fill packet");
+
+ unsigned short packet_pos = 0;
+
+ this->id = (input[packet_pos] << 8) | input[packet_pos + 1];
+ packet_pos += 2;
+
+ this->flags = (input[packet_pos] << 8) | input[packet_pos + 1];
+ packet_pos += 2;
+
+ unsigned short qdcount = (input[packet_pos] << 8) | input[packet_pos + 1];
+ packet_pos += 2;
+
+ unsigned short ancount = (input[packet_pos] << 8) | input[packet_pos + 1];
+ packet_pos += 2;
+
+ unsigned short nscount = (input[packet_pos] << 8) | input[packet_pos + 1];
+ packet_pos += 2;
+
+ unsigned short arcount = (input[packet_pos] << 8) | input[packet_pos + 1];
+ packet_pos += 2;
+
+ Log(LOG_DEBUG_2) << "Resolver: qdcount: " << qdcount << " ancount: " << ancount << " nscount: " << nscount << " arcount: " << arcount;
+
+ for (unsigned i = 0; i < qdcount; ++i)
+ this->questions.push_back(this->UnpackQuestion(input, len, packet_pos));
+
+ for (unsigned i = 0; i < ancount; ++i)
+ this->answers.push_back(this->UnpackResourceRecord(input, len, packet_pos));
+
+ for (unsigned i = 0; i < nscount; ++i)
+ this->authorities.push_back(this->UnpackResourceRecord(input, len, packet_pos));
+
+ for (unsigned i = 0; i < arcount; ++i)
+ this->additional.push_back(this->UnpackResourceRecord(input, len, packet_pos));
+ }
+
+ unsigned short Pack(unsigned char *output, unsigned short output_size)
+ {
+ if (output_size < HEADER_LENGTH)
+ throw SocketException("Unable to pack packet");
+
+ unsigned short pos = 0;
+
+ output[pos++] = this->id >> 8;
+ output[pos++] = this->id & 0xFF;
+ output[pos++] = this->flags >> 8;
+ output[pos++] = this->flags & 0xFF;
+ output[pos++] = this->questions.size() >> 8;
+ output[pos++] = this->questions.size() & 0xFF;
+ output[pos++] = this->answers.size() >> 8;
+ output[pos++] = this->answers.size() & 0xFF;
+ output[pos++] = this->authorities.size() >> 8;
+ output[pos++] = this->authorities.size() & 0xFF;
+ output[pos++] = this->additional.size() >> 8;
+ output[pos++] = this->additional.size() & 0xFF;
+
+ for (unsigned i = 0; i < this->questions.size(); ++i)
+ {
+ Question &q = this->questions[i];
+
+ if (q.type == QUERY_PTR)
+ {
+ sockaddrs ip(q.name);
+
+ if (q.name.find(':') != Anope::string::npos)
+ {
+ static const char *const hex = "0123456789abcdef";
+ char reverse_ip[128];
+ unsigned reverse_ip_count = 0;
+ for (int j = 15; j >= 0; --j)
+ {
+ reverse_ip[reverse_ip_count++] = hex[ip.sa6.sin6_addr.s6_addr[j] & 0xF];
+ reverse_ip[reverse_ip_count++] = '.';
+ reverse_ip[reverse_ip_count++] = hex[ip.sa6.sin6_addr.s6_addr[j] >> 4];
+ reverse_ip[reverse_ip_count++] = '.';
+ }
+ reverse_ip[reverse_ip_count++] = 0;
+
+ q.name = reverse_ip;
+ q.name += "ip6.arpa";
+ }
+ else
+ {
+ unsigned long forward = ip.sa4.sin_addr.s_addr;
+ in_addr reverse;
+ reverse.s_addr = forward << 24 | (forward & 0xFF00) << 8 | (forward & 0xFF0000) >> 8 | forward >> 24;
+
+ ip.ntop(AF_INET, &reverse);
+
+ q.name = ip.addr() + ".in-addr.arpa";
+ }
+ }
+
+ this->PackName(output, output_size, pos, q.name);
+
+ if (pos + 4 >= output_size)
+ throw SocketException("Unable to pack packet");
+
+ short s = htons(q.type);
+ memcpy(&output[pos], &s, 2);
+ pos += 2;
+
+ s = htons(q.qclass);
+ memcpy(&output[pos], &s, 2);
+ pos += 2;
+ }
+
+ std::vector<ResourceRecord> types[] = { this->answers, this->authorities, this->additional };
+ for (int i = 0; i < 3; ++i)
+ for (unsigned j = 0; j < types[i].size(); ++j)
+ {
+ ResourceRecord &rr = types[i][j];
+
+ this->PackName(output, output_size, pos, rr.name);
+
+ if (pos + 8 >= output_size)
+ throw SocketException("Unable to pack packet");
+
+ short s = htons(rr.type);
+ memcpy(&output[pos], &s, 2);
+ pos += 2;
+
+ s = htons(rr.qclass);
+ memcpy(&output[pos], &s, 2);
+ pos += 2;
+
+ long l = htonl(rr.ttl);
+ memcpy(&output[pos], &l, 4);
+ pos += 4;
+
+ switch (rr.type)
+ {
+ case QUERY_A:
+ {
+ if (pos + 6 > output_size)
+ throw SocketException("Unable to pack packet");
+
+ sockaddrs a(rr.rdata);
+
+ s = htons(4);
+ memcpy(&output[pos], &s, 2);
+ pos += 2;
+
+ memcpy(&output[pos], &a.sa4.sin_addr, 4);
+ pos += 4;
+ break;
+ }
+ case QUERY_AAAA:
+ {
+ if (pos + 18 > output_size)
+ throw SocketException("Unable to pack packet");
+
+ sockaddrs a(rr.rdata);
+
+ s = htons(16);
+ memcpy(&output[pos], &s, 2);
+ pos += 2;
+
+ memcpy(&output[pos], &a.sa6.sin6_addr, 16);
+ pos += 16;
+ break;
+ }
+ case QUERY_NS:
+ case QUERY_CNAME:
+ case QUERY_PTR:
+ {
+ 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, rr.rdata);
+
+ s = htons(pos - packet_pos_save - 2);
+ memcpy(&output[packet_pos_save], &s, 2);
+ break;
+ }
+ case QUERY_SOA:
+ {
+ if (pos + 2 >= output_size)
+ throw SocketException("Unable to pack packet");
+
+ unsigned short packet_pos_save = pos;
+ pos += 2;
+
+ std::vector<Anope::string> ns;
+ spacesepstream(nameservers).GetTokens(ns);
+ this->PackName(output, output_size, pos, !ns.empty() ? ns[0] : "");
+ this->PackName(output, output_size, pos, admin.replace_all_cs('@', '.'));
+
+ if (pos + 20 >= output_size)
+ throw SocketException("Unable to pack SOA");
+
+ l = htonl(manager->GetSerial());
+ memcpy(&output[pos], &l, 4);
+ pos += 4;
+
+ l = htonl(refresh); // Refresh
+ memcpy(&output[pos], &l, 4);
+ pos += 4;
+
+ l = htonl(refresh); // 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:
+ break;
+ }
+ }
+
+ return pos;
+ }
+};
+
+namespace DNS
+{
+ class ReplySocket : public virtual Socket
+ {
+ public:
+ virtual ~ReplySocket() { }
+ virtual void Reply(MyPacket *p) = 0;
+ };
+}
+
+/* Listens for TCP requests */
+class TCPSocket : public ListenSocket
+{
+ /* A TCP client */
+ class Client : public ClientSocket, public Timer, public ReplySocket
+ {
+ Manager *manager;
+ TCPSocket *tcpsock;
+ MyPacket *packet;
+ unsigned char packet_buffer[524];
+ int length;
+
+ public:
+ Client(Manager *m, TCPSocket *l, int fd, const sockaddrs &addr) : Socket(fd, l->IsIPv6()), ClientSocket(l, addr), Timer(5),
+ manager(m), tcpsock(l), packet(NULL), length(0)
+ {
+ Log(LOG_DEBUG_2) << "Resolver: New client from " << addr.addr();
+ }
+
+ ~Client()
+ {
+ Log(LOG_DEBUG_2) << "Resolver: Exiting client from " << clientaddr.addr();
+ delete packet;
+ }
+
+ /* Times out after a few seconds */
+ void Tick(time_t) anope_override { }
+
+ void Reply(MyPacket *p) anope_override
+ {
+ delete packet;
+ packet = p;
+ SocketEngine::Change(this, true, SF_WRITABLE);
+ }
+
+ bool ProcessRead() anope_override
+ {
+ 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;
+
+ unsigned short want_len = packet_buffer[0] << 8 | packet_buffer[1];
+ if (length >= want_len + 2)
+ {
+ int len = length - 2;
+ length -= want_len + 2;
+ return this->manager->HandlePacket(this, packet_buffer + 2, len, NULL);
+ }
+ return true;
+ }
+
+ bool ProcessWrite() anope_override
+ {
+ Log(LOG_DEBUG_2) << "Resolver: Writing to DNS TCP socket";
+
+ if (packet != NULL)
+ {
+ try
+ {
+ unsigned char buffer[65535];
+ 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::Change(this, false, SF_WRITABLE);
+ return true; /* Do not return false here, bind is unhappy we close the connection so soon after sending */
+ }
+ };
+
+ Manager *manager;
+
+ public:
+ TCPSocket(Manager *m, const Anope::string &ip, int port) : Socket(-1, ip.find(':') != Anope::string::npos), ListenSocket(ip, port, ip.find(':') != Anope::string::npos), manager(m) { }
+
+ ClientSocket *OnAccept(int fd, const sockaddrs &addr) anope_override
+ {
+ return new Client(this->manager, this, fd, addr);
+ }
+};
+
+/* Listens for UDP requests */
+class UDPSocket : public ReplySocket
+{
+ Manager *manager;
+ std::deque<MyPacket *> packets;
+
+ public:
+ UDPSocket(Manager *m, const Anope::string &ip, int port) : Socket(-1, ip.find(':') != Anope::string::npos, SOCK_DGRAM), manager(m) { }
+
+ ~UDPSocket()
+ {
+ for (unsigned i = 0; i < packets.size(); ++i)
+ delete packets[i];
+ }
+
+ void Reply(MyPacket *p) anope_override
+ {
+ packets.push_back(p);
+ SocketEngine::Change(this, true, SF_WRITABLE);
+ }
+
+ std::deque<MyPacket *>& GetPackets() { return packets; }
+
+ bool ProcessRead() anope_override
+ {
+ 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 this->manager->HandlePacket(this, packet_buffer, length, &from_server);
+ }
+
+ bool ProcessWrite() anope_override
+ {
+ Log(LOG_DEBUG_2) << "Resolver: Writing to DNS UDP socket";
+
+ MyPacket *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::Change(this, false, SF_WRITABLE);
+
+ return true;
+ }
+};
+
+class MyManager : public Manager, public Timer
+{
+ uint32_t serial;
+
+ typedef std::multimap<Anope::string, ResourceRecord, ci::less> cache_map;
+ cache_map cache;
+
+ TCPSocket *tcpsock;
+ UDPSocket *udpsock;
+
+ bool listen;
+ sockaddrs addrs;
+ public:
+ std::map<unsigned short, Request *> requests;
+
+ MyManager(Module *creator) : Manager(creator), Timer(300, Anope::CurTime, true), serial(Anope::CurTime), tcpsock(NULL), udpsock(NULL), listen(false)
+ {
+ }
+
+ ~MyManager()
+ {
+ delete udpsock;
+ delete tcpsock;
+
+ for (std::map<unsigned short, Request *>::iterator it = this->requests.begin(), it_end = this->requests.end(); it != it_end;)
+ {
+ Request *request = it->second;
+ ++it;
+
+ Query rr(*request);
+ rr.error = ERROR_UNKNOWN;
+ request->OnError(&rr);
+
+ delete request;
+ }
+ this->requests.clear();
+
+ this->cache.clear();
+ }
+
+ void SetIPPort(const Anope::string &nameserver, const Anope::string &ip, unsigned short port)
+ {
+ delete udpsock;
+ delete tcpsock;
+
+ udpsock = NULL;
+ tcpsock = NULL;
+
+ try
+ {
+ this->addrs.pton(nameserver.find(':') != Anope::string::npos ? AF_INET6 : AF_INET, nameserver, 53);
+
+ udpsock = new UDPSocket(this, ip, port);
+
+ if (!ip.empty())
+ {
+ udpsock->Bind(ip, port);
+ tcpsock = new TCPSocket(this, ip, port);
+ listen = true;
+ }
+ }
+ catch (const SocketException &ex)
+ {
+ Log() << "Unable to bind dns to " << ip << ":" << port << ": " << ex.GetReason();
+ }
+ }
+
+ void Process(Request *req) anope_override
+ {
+ Log(LOG_DEBUG_2) << "Resolver: Processing request to lookup " << req->name << ", of type " << req->type;
+
+ if (req->use_cache && this->CheckCache(req))
+ {
+ Log(LOG_DEBUG_2) << "Resolver: Using cached result";
+ delete req;
+ return;
+ }
+
+ 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));
+
+ this->requests[req->id] = req;
+
+ req->SetSecs(timeout);
+
+ MyPacket *p = new MyPacket(this, &this->addrs);
+ p->flags = QUERYFLAGS_RD;
+
+ p->id = req->id;
+ p->questions.push_back(*req);
+ this->udpsock->Reply(p);
+ }
+
+ void RemoveRequest(Request *req) anope_override
+ {
+ this->requests.erase(req->id);
+ }
+
+ bool HandlePacket(ReplySocket *s, const unsigned char *const packet_buffer, int length, sockaddrs *from) anope_override
+ {
+ if (length < Packet::HEADER_LENGTH)
+ return true;
+
+ MyPacket recv_packet(this, from);
+
+ try
+ {
+ recv_packet.Fill(packet_buffer, length);
+ }
+ catch (const SocketException &ex)
+ {
+ Log(LOG_DEBUG_2) << ex.GetReason();
+ return true;
+ }
+
+ if (!(recv_packet.flags & QUERYFLAGS_QR))
+ {
+ if (!listen)
+ return true;
+ else if (recv_packet.questions.empty())
+ {
+ Log(LOG_DEBUG_2) << "Resolver: Received a question with no questions?";
+ return true;
+ }
+
+ MyPacket *packet = new MyPacket(recv_packet);
+ packet->flags |= QUERYFLAGS_QR; /* This is a reponse */
+ packet->flags |= 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 == QUERY_AXFR || q.type == QUERY_SOA)
+ {
+ ResourceRecord rr(q.name, QUERY_SOA);
+ packet->answers.push_back(rr);
+
+ if (q.type == QUERY_AXFR)
+ {
+ Anope::string token;
+ spacesepstream sep(nameservers);
+ while (sep.GetToken(token))
+ {
+ ResourceRecord rr2(q.name, QUERY_NS);
+ rr2.rdata = token;
+ packet->answers.push_back(rr2);
+ }
+ }
+ break;
+ }
+ }
+
+ FOREACH_MOD(I_OnDnsRequest, OnDnsRequest(recv_packet, packet));
+
+ for (unsigned i = 0; i < recv_packet.questions.size(); ++i)
+ {
+ const Question& q = recv_packet.questions[i];
+
+ if (q.type == QUERY_AXFR)
+ {
+ ResourceRecord rr(q.name, QUERY_SOA);
+ packet->answers.push_back(rr);
+ break;
+ }
+ }
+
+ s->Reply(packet);
+ return true;
+ }
+
+ if (from == NULL)
+ {
+ 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;
+ }
+
+ std::map<unsigned short, Request *>::iterator it = this->requests.find(recv_packet.id);
+ if (it == this->requests.end())
+ {
+ Log(LOG_DEBUG_2) << "Resolver: Received an answer for something we didn't request";
+ return true;
+ }
+ Request *request = it->second;
+
+ if (recv_packet.flags & QUERYFLAGS_OPCODE)
+ {
+ Log(LOG_DEBUG_2) << "Resolver: Received a nonstandard query";
+ recv_packet.error = ERROR_NONSTANDARD_QUERY;
+ request->OnError(&recv_packet);
+ }
+ else if (recv_packet.flags & QUERYFLAGS_RCODE)
+ {
+ Error error = ERROR_UNKNOWN;
+
+ switch (recv_packet.flags & QUERYFLAGS_RCODE)
+ {
+ case 1:
+ Log(LOG_DEBUG_2) << "Resolver: format error";
+ error = ERROR_FORMAT_ERROR;
+ break;
+ case 2:
+ Log(LOG_DEBUG_2) << "Resolver: server error";
+ error = ERROR_SERVER_FAILURE;
+ break;
+ case 3:
+ Log(LOG_DEBUG_2) << "Resolver: domain not found";
+ error = ERROR_DOMAIN_NOT_FOUND;
+ break;
+ case 4:
+ Log(LOG_DEBUG_2) << "Resolver: not implemented";
+ error = ERROR_NOT_IMPLEMENTED;
+ break;
+ case 5:
+ Log(LOG_DEBUG_2) << "Resolver: refused";
+ error = ERROR_REFUSED;
+ break;
+ default:
+ break;
+ }
+
+ recv_packet.error = error;
+ request->OnError(&recv_packet);
+ }
+ else if (recv_packet.answers.empty())
+ {
+ Log(LOG_DEBUG_2) << "Resolver: No resource records returned";
+ recv_packet.error = ERROR_NO_RECORDS;
+ request->OnError(&recv_packet);
+ }
+ else
+ {
+ Log(LOG_DEBUG_2) << "Resolver: Lookup complete for " << request->name;
+ request->OnLookupComplete(&recv_packet);
+ this->AddCache(recv_packet);
+ }
+
+ delete request;
+ return true;
+ }
+
+ void UpdateSerial() anope_override
+ {
+ serial = Anope::CurTime;
+ }
+
+ uint32_t GetSerial() const anope_override
+ {
+ return serial;
+ }
+
+ /** Add a record to the dns cache
+ * @param r The record
+ */
+ void AddCache(Query &r)
+ {
+ for (unsigned i = 0; i < r.answers.size(); ++i)
+ {
+ ResourceRecord &rr = r.answers[i];
+ Log(LOG_DEBUG_3) << "Resolver cache: added cache for " << rr.name << " -> " << rr.rdata << ", ttl: " << rr.ttl;
+ this->cache.insert(std::make_pair(rr.name, rr));
+ }
+ }
+
+ void Tick(time_t now) anope_override
+ {
+ Log(LOG_DEBUG_2) << "Resolver: Purging DNS cache";
+
+ for (cache_map::iterator it = this->cache.begin(), it_next; it != this->cache.end(); it = it_next)
+ {
+ ResourceRecord &req = it->second;
+ it_next = it;
+ ++it_next;
+
+ if (req.created + static_cast<time_t>(req.ttl) < now)
+ this->cache.erase(it);
+ }
+ }
+
+ private:
+ /** Check the DNS cache to see if request can be handled by a cached result
+ * @return true if a cached result was found.
+ */
+ bool CheckCache(Request *request)
+ {
+ cache_map::iterator it = this->cache.find(request->name);
+ if (it != this->cache.end())
+ {
+ Query record(*request);
+
+ for (cache_map::iterator it_end = this->cache.upper_bound(request->name); it != it_end; ++it)
+ {
+ ResourceRecord &rec = it->second;
+ if (rec.created + static_cast<time_t>(rec.ttl) >= Anope::CurTime)
+ record.answers.push_back(rec);
+ }
+
+ if (!record.answers.empty())
+ {
+ Log(LOG_DEBUG_3) << "Resolver: Using cached result for " << request->name;
+ request->OnLookupComplete(&record);
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+};
+
+class ModuleDNS : public Module
+{
+ MyManager manager;
+
+ Anope::string nameserver;
+ Anope::string ip;
+ int port;
+
+ public:
+ ModuleDNS(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, SUPPORTED), manager(this)
+ {
+ this->SetAuthor("Anope");
+
+ Implementation i[] = { I_OnReload, I_OnModuleUnload };
+ ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation));
+
+ this->OnReload();
+ }
+
+ void OnReload() anope_override
+ {
+ ConfigReader config;
+
+ nameserver = config.ReadValue("dns", "nameserver", "127.0.0.1", 0);
+ timeout = Anope::DoTime(config.ReadValue("dns", "timeout", "5", 0));
+ ip = config.ReadValue("dns", "ip", "0.0.0.0", 0);
+ port = config.ReadInteger("dns", "port", "53", 0, false);
+ admin = config.ReadValue("dns", "admin", "admin@example.com", 0);
+ nameservers = config.ReadValue("dns", "nameservers", "ns1.example.com", 0);
+ refresh = config.ReadInteger("dns", "refresh", "3600", 0, false);
+
+ if (Anope::IsFile(nameserver))
+ {
+ std::ifstream f(nameserver.c_str());
+ bool success = false;
+
+ if (f.is_open())
+ {
+ for (Anope::string server; std::getline(f, server.str());)
+ {
+ if (server.find("nameserver") == 0)
+ {
+ size_t i = server.find_first_of("123456789");
+ if (i != Anope::string::npos)
+ {
+ if (server.substr(i).is_pos_number_only())
+ {
+ nameserver = server.substr(i);
+ Log(LOG_DEBUG) << "Nameserver set to " << nameserver;
+ success = true;
+ break;
+ }
+ }
+ }
+ }
+
+ f.close();
+ }
+
+ if (!success)
+ {
+ Log() << "Unable to find nameserver, defaulting to 127.0.0.1";
+ nameserver = "127.0.0.1";
+ }
+ }
+
+ try
+ {
+ this->manager.SetIPPort(nameserver, ip, port);
+ }
+ catch (const SocketException &ex)
+ {
+ throw ModuleException(ex.GetReason());
+ }
+ }
+
+ void OnModuleUnload(User *u, Module *m) anope_override
+ {
+ for (std::map<unsigned short, Request *>::iterator it = this->manager.requests.begin(), it_end = this->manager.requests.end(); it != it_end;)
+ {
+ unsigned short id = it->first;
+ Request *req = it->second;
+ ++it;
+
+ if (req->creator == m)
+ {
+ Query rr(*req);
+ rr.error = ERROR_UNLOADED;
+ req->OnError(&rr);
+
+ delete req;
+ this->manager.requests.erase(id);
+ }
+ }
+ }
+};
+
+MODULE_INIT(ModuleDNS)
+
diff --git a/modules/extra/m_dnsbl.cpp b/modules/extra/m_dnsbl.cpp
index 54c802cd4..1d5cdcb2e 100644
--- a/modules/extra/m_dnsbl.cpp
+++ b/modules/extra/m_dnsbl.cpp
@@ -6,8 +6,12 @@
*/
#include "module.h"
+#include "dns.h"
+
+using namespace DNS;
static ServiceReference<XLineManager> akills("XLineManager", "xlinemanager/sgline");
+static ServiceReference<Manager> dnsmanager("DNS::Manager", "dns/manager");
struct Blacklist
{
@@ -19,21 +23,21 @@ struct Blacklist
Blacklist(const Anope::string &n, time_t b, const Anope::string &r, const std::map<int, Anope::string> &re) : name(n), bantime(b), reason(r), replies(re) { }
};
-class DNSBLResolver : public DNS::Request
+class DNSBLResolver : public Request
{
Reference<User> user;
Blacklist blacklist;
bool add_to_akill;
public:
- DNSBLResolver(Module *c, User *u, const Blacklist &b, const Anope::string &host, bool add_akill) : DNS::Request(host, DNS::QUERY_A, true, c), user(u), blacklist(b), add_to_akill(add_akill) { }
+ DNSBLResolver(Module *c, User *u, const Blacklist &b, const Anope::string &host, bool add_akill) : Request(dnsmanager, c, host, QUERY_A, true), user(u), blacklist(b), add_to_akill(add_akill) { }
- void OnLookupComplete(const DNS::Query *record) anope_override
+ void OnLookupComplete(const Query *record) anope_override
{
if (!user || user->HasExt("m_dnsbl_akilled"))
return;
- const DNS::ResourceRecord &ans_record = record->answers[0];
+ const ResourceRecord &ans_record = record->answers[0];
// Replies should be in 127.0.0.0/24
if (ans_record.rdata.find("127.0.0.") != 0)
return;
@@ -127,7 +131,7 @@ class ModuleDNSBL : public Module
void OnUserConnect(Reference<User> &user, bool &exempt) anope_override
{
- if (exempt || !user || (!this->check_on_connect && !Me->IsSynced()))
+ if (exempt || !user || (!this->check_on_connect && !Me->IsSynced()) || !dnsmanager)
return;
if (!this->check_on_netburst && !user->server->IsSynced())
@@ -158,7 +162,7 @@ class ModuleDNSBL : public Module
{
Anope::string dnsbl_host = user_ip.addr() + "." + b.name;
DNSBLResolver *res = new DNSBLResolver(this, user, b, dnsbl_host, this->add_to_akill);
- res->Process();
+ dnsmanager->Process(res);
}
catch (const SocketException &ex)
{
diff --git a/src/config.cpp b/src/config.cpp
index 8969569fd..79ebce9f3 100644
--- a/src/config.cpp
+++ b/src/config.cpp
@@ -17,7 +17,6 @@
#include "opertype.h"
#include "channels.h"
#include "hashcomp.h"
-#include "dns.h"
#ifndef _WIN32
#include <errno.h>
@@ -175,42 +174,6 @@ ServerConfig::ServerConfig()
ModeManager::UpdateDefaultMLock(this);
- if (IsFile(this->NameServer))
- {
- std::ifstream f(this->NameServer.c_str());
- Anope::string server;
- bool success = false;
-
- while (f.is_open() && getline(f, server.str()))
- {
- if (server.find("nameserver") == 0)
- {
- size_t ip = server.find_first_of("123456789");
- if (ip != Anope::string::npos)
- {
- if (server.substr(ip).is_pos_number_only())
- {
- this->NameServer = server.substr(ip);
- Log(LOG_DEBUG) << "Nameserver set to " << this->NameServer;
- success = true;
- break;
- }
- }
- }
- }
-
- if (f.is_open())
- f.close();
-
- if (!success)
- {
- Log() << "Unable to find nameserver, defaulting to 127.0.0.1";
- this->NameServer = "127.0.0.1";
- }
- }
- delete DNS::Engine;
- DNS::Engine = new DNS::Manager(this->NameServer, this->DNSIP, this->DNSPort);
-
if (this->CaseMap == "ascii")
Anope::casemap = std::locale(std::locale(), new Anope::ascii_ctype<char>());
else if (this->CaseMap == "rfc1459")
@@ -1262,13 +1225,6 @@ ConfigItems::ConfigItems(ServerConfig *conf)
{"mail", "emailchange_message", "", new ValueContainerString(&conf->MailEmailchangeMessage), DT_STRING | DT_ALLOW_NEWLINE, ValidateMail},
{"mail", "memo_subject", "", new ValueContainerString(&conf->MailMemoSubject), DT_STRING, ValidateMail},
{"mail", "memo_message", "", new ValueContainerString(&conf->MailMemoMessage), DT_STRING | DT_ALLOW_NEWLINE, ValidateMail},
- {"dns", "nameserver", "127.0.0.1", new ValueContainerString(&conf->NameServer), DT_STRING, NoValidation},
- {"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", "nameservers", "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},
diff --git a/src/dns.cpp b/src/dns.cpp
deleted file mode 100644
index ccb919a31..000000000
--- a/src/dns.cpp
+++ /dev/null
@@ -1,963 +0,0 @@
-/*
- *
- * (C) 2003-2012 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- *
- */
-
-#include "services.h"
-#include "anope.h"
-#include "dns.h"
-#include "sockets.h"
-#include "socketengine.h"
-
-#ifndef _WIN32
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netdb.h>
-#endif
-
-using namespace DNS;
-
-Manager *DNS::Engine = NULL;
-
-Question::Question()
-{
- this->type = QUERY_NONE;
- this->qclass = 0;
-}
-
-Question::Question(const Anope::string &n, QueryType t, unsigned short q) : name(n), type(t), qclass(q)
-{
-}
-
-ResourceRecord::ResourceRecord(const Anope::string &n, QueryType t, unsigned short q) : Question(n, t, q)
-{
- this->ttl = 0;
- this->created = Anope::CurTime;
-}
-
-ResourceRecord::ResourceRecord(const Question &q) : Question(q)
-{
- this->ttl = 0;
- this->created = Anope::CurTime;
-}
-
-Query::Query()
-{
- this->error = ERROR_NONE;
-}
-
-Query::Query(const Question &q)
-{
- this->questions.push_back(q);
- this->error = ERROR_NONE;
-}
-
-Request::Request(const Anope::string &addr, QueryType qt, bool cache, Module *c) : Timer(Config->DNSTimeout), Question(addr, qt), use_cache(cache), id(0), creator(c)
-{
- if (!DNS::Engine || !DNS::Engine->udpsock)
- throw SocketException("No DNS::Engine");
- if (DNS::Engine->udpsock->GetPackets().size() == 65535)
- throw SocketException("DNS queue full");
-
- do
- {
- static unsigned short cur_id = rand();
- this->id = cur_id++;
- }
- while (DNS::Engine->requests.count(this->id));
-
- DNS::Engine->requests[this->id] = this;
-}
-
-Request::~Request()
-{
- DNS::Engine->requests.erase(this->id);
-}
-
-void Request::Process()
-{
- Log(LOG_DEBUG_2) << "Resolver: Processing request to lookup " << this->name << ", of type " << this->type;
-
- if (!DNS::Engine || !DNS::Engine->udpsock)
- throw SocketException("DNS::Engine has not been initialized");
-
- if (this->use_cache && DNS::Engine->CheckCache(this))
- {
- Log(LOG_DEBUG_2) << "Resolver: Using cached result";
- delete this;
- return;
- }
-
- Packet *p = new Packet(&DNS::Engine->addrs);
- p->flags = QUERYFLAGS_RD;
-
- p->id = this->id;
- p->questions.push_back(*this);
- DNS::Engine->udpsock->Reply(p);
-}
-
-void Request::OnError(const Query *r)
-{
-}
-
-void Request::Tick(time_t)
-{
- Log(LOG_DEBUG_2) << "Resolver: timeout for query " << this->name;
- Query rr(*this);
- rr.error = ERROR_TIMEOUT;
- this->OnError(&rr);
-}
-
-void Packet::PackName(unsigned char *output, unsigned short output_size, unsigned short &pos, const Anope::string &name)
-{
- if (name.length() + 2 > output_size)
- throw SocketException("Unable to pack name");
-
- Log(LOG_DEBUG_2) << "Resolver: PackName packing " << name;
-
- sepstream sep(name, '.');
- Anope::string token;
-
- while (sep.GetToken(token))
- {
- output[pos++] = token.length();
- memcpy(&output[pos], token.c_str(), token.length());
- pos += token.length();
- }
-
- output[pos++] = 0;
-}
-
-Anope::string Packet::UnpackName(const unsigned char *input, unsigned short input_size, unsigned short &pos)
-{
- Anope::string name;
- unsigned short pos_ptr = pos, lowest_ptr = input_size;
- bool compressed = false;
-
- if (pos_ptr >= input_size)
- throw SocketException("Unable to unpack name - no input");
-
- while (input[pos_ptr] > 0)
- {
- unsigned short offset = input[pos_ptr];
-
- if (offset & POINTER)
- {
- if ((offset & POINTER) != POINTER)
- throw SocketException("Unable to unpack name - bogus compression header");
- if (pos_ptr + 1 >= input_size)
- throw SocketException("Unable to unpack name - bogus compression header");
-
- /* Place pos at the second byte of the first (farthest) compression pointer */
- if (compressed == false)
- {
- ++pos;
- compressed = true;
- }
-
- pos_ptr = (offset & LABEL) << 8 | input[pos_ptr + 1];
-
- /* Pointers can only go back */
- if (pos_ptr >= lowest_ptr)
- throw SocketException("Unable to unpack name - bogus compression pointer");
- lowest_ptr = pos_ptr;
- }
- else
- {
- if (pos_ptr + offset + 1 >= input_size)
- throw SocketException("Unable to unpack name - offset too large");
- if (!name.empty())
- name += ".";
- for (unsigned i = 1; i <= offset; ++i)
- name += input[pos_ptr + i];
-
- pos_ptr += offset + 1;
- if (compressed == false)
- /* Move up pos */
- pos = pos_ptr;
- }
- }
-
- /* +1 pos either to one byte after the compression pointer or one byte after the ending \0 */
- ++pos;
-
- Log(LOG_DEBUG_2) << "Resolver: UnpackName successfully unpacked " << name;
-
- return name;
-}
-
-Question Packet::UnpackQuestion(const unsigned char *input, unsigned short input_size, unsigned short &pos)
-{
- Question question;
-
- question.name = this->UnpackName(input, input_size, pos);
-
- if (pos + 4 > input_size)
- throw SocketException("Unable to unpack question");
-
- question.type = static_cast<QueryType>(input[pos] << 8 | input[pos + 1]);
- pos += 2;
-
- question.qclass = input[pos] << 8 | input[pos + 1];
- pos += 2;
-
- return question;
-}
-
-ResourceRecord Packet::UnpackResourceRecord(const unsigned char *input, unsigned short input_size, unsigned short &pos)
-{
- ResourceRecord record = static_cast<ResourceRecord>(this->UnpackQuestion(input, input_size, pos));
-
- if (pos + 6 > input_size)
- throw SocketException("Unable to unpack resource record");
-
- record.ttl = (input[pos] << 24) | (input[pos + 1] << 16) | (input[pos + 2] << 8) | input[pos + 3];
- pos += 4;
-
- //record.rdlength = input[pos] << 8 | input[pos + 1];
- pos += 2;
-
- switch (record.type)
- {
- case QUERY_A:
- {
- if (pos + 4 > input_size)
- throw SocketException("Unable to unpack resource record");
-
- in_addr a;
- a.s_addr = input[pos] | (input[pos + 1] << 8) | (input[pos + 2] << 16) | (input[pos + 3] << 24);
- pos += 4;
-
- sockaddrs addrs;
- addrs.ntop(AF_INET, &a);
-
- record.rdata = addrs.addr();
- break;
- }
- case QUERY_AAAA:
- {
- if (pos + 16 > input_size)
- throw SocketException("Unable to unpack resource record");
-
- in6_addr a;
- for (int j = 0; j < 16; ++j)
- a.s6_addr[j] = input[pos + j];
- pos += 16;
-
- sockaddrs addrs;
- addrs.ntop(AF_INET6, &a);
-
- record.rdata = addrs.addr();
- break;
- }
- case QUERY_CNAME:
- case QUERY_PTR:
- {
- record.rdata = this->UnpackName(input, input_size, pos);
- break;
- }
- default:
- break;
- }
-
- Log(LOG_DEBUG_2) << "Resolver: " << record.name << " -> " << record.rdata;
-
- return record;
-}
-
-Packet::Packet(sockaddrs *a) : Query(), id(0), flags(0)
-{
- if (a)
- addr = *a;
-}
-
-void Packet::Fill(const unsigned char *input, const unsigned short len)
-{
- if (len < Packet::HEADER_LENGTH)
- throw SocketException("Unable to fill packet");
-
- unsigned short packet_pos = 0;
-
- this->id = (input[packet_pos] << 8) | input[packet_pos + 1];
- packet_pos += 2;
-
- this->flags = (input[packet_pos] << 8) | input[packet_pos + 1];
- packet_pos += 2;
-
- unsigned short qdcount = (input[packet_pos] << 8) | input[packet_pos + 1];
- packet_pos += 2;
-
- unsigned short ancount = (input[packet_pos] << 8) | input[packet_pos + 1];
- packet_pos += 2;
-
- unsigned short nscount = (input[packet_pos] << 8) | input[packet_pos + 1];
- packet_pos += 2;
-
- unsigned short arcount = (input[packet_pos] << 8) | input[packet_pos + 1];
- packet_pos += 2;
-
- Log(LOG_DEBUG_2) << "Resolver: qdcount: " << qdcount << " ancount: " << ancount << " nscount: " << nscount << " arcount: " << arcount;
-
- for (unsigned i = 0; i < qdcount; ++i)
- this->questions.push_back(this->UnpackQuestion(input, len, packet_pos));
-
- for (unsigned i = 0; i < ancount; ++i)
- this->answers.push_back(this->UnpackResourceRecord(input, len, packet_pos));
-
- for (unsigned i = 0; i < nscount; ++i)
- this->authorities.push_back(this->UnpackResourceRecord(input, len, packet_pos));
-
- for (unsigned i = 0; i < arcount; ++i)
- this->additional.push_back(this->UnpackResourceRecord(input, len, packet_pos));
-}
-
-unsigned short Packet::Pack(unsigned char *output, unsigned short output_size)
-{
- if (output_size < Packet::HEADER_LENGTH)
- throw SocketException("Unable to pack packet");
-
- unsigned short pos = 0;
-
- output[pos++] = this->id >> 8;
- output[pos++] = this->id & 0xFF;
- output[pos++] = this->flags >> 8;
- output[pos++] = this->flags & 0xFF;
- output[pos++] = this->questions.size() >> 8;
- output[pos++] = this->questions.size() & 0xFF;
- output[pos++] = this->answers.size() >> 8;
- output[pos++] = this->answers.size() & 0xFF;
- output[pos++] = this->authorities.size() >> 8;
- output[pos++] = this->authorities.size() & 0xFF;
- output[pos++] = this->additional.size() >> 8;
- output[pos++] = this->additional.size() & 0xFF;
-
- for (unsigned i = 0; i < this->questions.size(); ++i)
- {
- Question &q = this->questions[i];
-
- if (q.type == QUERY_PTR)
- {
- sockaddrs ip(q.name);
-
- if (q.name.find(':') != Anope::string::npos)
- {
- static const char *const hex = "0123456789abcdef";
- char reverse_ip[128];
- unsigned reverse_ip_count = 0;
- for (int j = 15; j >= 0; --j)
- {
- reverse_ip[reverse_ip_count++] = hex[ip.sa6.sin6_addr.s6_addr[j] & 0xF];
- reverse_ip[reverse_ip_count++] = '.';
- reverse_ip[reverse_ip_count++] = hex[ip.sa6.sin6_addr.s6_addr[j] >> 4];
- reverse_ip[reverse_ip_count++] = '.';
- }
- reverse_ip[reverse_ip_count++] = 0;
-
- q.name = reverse_ip;
- q.name += "ip6.arpa";
- }
- else
- {
- unsigned long forward = ip.sa4.sin_addr.s_addr;
- in_addr reverse;
- reverse.s_addr = forward << 24 | (forward & 0xFF00) << 8 | (forward & 0xFF0000) >> 8 | forward >> 24;
-
- ip.ntop(AF_INET, &reverse);
-
- q.name = ip.addr() + ".in-addr.arpa";
- }
- }
-
- this->PackName(output, output_size, pos, q.name);
-
- if (pos + 4 >= output_size)
- throw SocketException("Unable to pack packet");
-
- short s = htons(q.type);
- memcpy(&output[pos], &s, 2);
- pos += 2;
-
- s = htons(q.qclass);
- memcpy(&output[pos], &s, 2);
- pos += 2;
- }
-
- std::vector<ResourceRecord> types[] = { this->answers, this->authorities, this->additional };
- for (int i = 0; i < 3; ++i)
- for (unsigned j = 0; j < types[i].size(); ++j)
- {
- ResourceRecord &rr = types[i][j];
-
- this->PackName(output, output_size, pos, rr.name);
-
- if (pos + 8 >= output_size)
- throw SocketException("Unable to pack packet");
-
- short s = htons(rr.type);
- memcpy(&output[pos], &s, 2);
- pos += 2;
-
- s = htons(rr.qclass);
- memcpy(&output[pos], &s, 2);
- pos += 2;
-
- long l = htonl(rr.ttl);
- memcpy(&output[pos], &l, 4);
- pos += 4;
-
- switch (rr.type)
- {
- case QUERY_A:
- {
- if (pos + 6 > output_size)
- throw SocketException("Unable to pack packet");
-
- sockaddrs a(rr.rdata);
-
- s = htons(4);
- memcpy(&output[pos], &s, 2);
- pos += 2;
-
- memcpy(&output[pos], &a.sa4.sin_addr, 4);
- pos += 4;
- break;
- }
- case QUERY_AAAA:
- {
- if (pos + 18 > output_size)
- throw SocketException("Unable to pack packet");
-
- sockaddrs a(rr.rdata);
-
- s = htons(16);
- memcpy(&output[pos], &s, 2);
- pos += 2;
-
- memcpy(&output[pos], &a.sa6.sin6_addr, 16);
- pos += 16;
- break;
- }
- case QUERY_NS:
- case QUERY_CNAME:
- case QUERY_PTR:
- {
- 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, rr.rdata);
-
- s = htons(pos - packet_pos_save - 2);
- memcpy(&output[packet_pos_save], &s, 2);
- break;
- }
- case QUERY_SOA:
- {
- if (pos + 2 >= output_size)
- throw SocketException("Unable to pack packet");
-
- unsigned short packet_pos_save = pos;
- pos += 2;
-
- std::vector<Anope::string> nameservers;
- spacesepstream(Config->DNSSOANS).GetTokens(nameservers);
- this->PackName(output, output_size, pos, !nameservers.empty() ? nameservers[0] : "");
- this->PackName(output, output_size, pos, Config->DNSSOAAdmin.replace_all_cs('@', '.'));
-
- if (pos + 20 >= output_size)
- throw SocketException("Unable to pack SOA");
-
- l = htonl(DNS::Engine->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:
- break;
- }
- }
-
- return pos;
-}
-
-Manager::TCPSocket::Client::Client(TCPSocket *l, int fd, const sockaddrs &addr) : Socket(fd, l->IsIPv6()), ClientSocket(l, addr), Timer(5), tcpsock(l), packet(NULL), length(0)
-{
- Log(LOG_DEBUG_2) << "Resolver: New client from " << addr.addr();
-}
-
-Manager::TCPSocket::Client::~Client()
-{
- Log(LOG_DEBUG_2) << "Resolver: Exiting client from " << clientaddr.addr();
- delete packet;
-}
-
-void Manager::TCPSocket::Client::Reply(Packet *p)
-{
- delete packet;
- packet = p;
- SocketEngine::Change(this, true, SF_WRITABLE);
-}
-
-bool Manager::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;
-
- unsigned short want_len = packet_buffer[0] << 8 | packet_buffer[1];
- if (length >= want_len + 2)
- {
- int len = length - 2;
- length -= want_len + 2;
- return DNS::Engine->HandlePacket(this, packet_buffer + 2, len, NULL);
- }
- return true;
-}
-
-bool Manager::TCPSocket::Client::ProcessWrite()
-{
- Log(LOG_DEBUG_2) << "Resolver: Writing to DNS TCP socket";
-
- if (packet != NULL)
- {
- try
- {
- unsigned char buffer[65535];
- 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::Change(this, false, SF_WRITABLE);
- return true; /* Do not return false here, bind is unhappy we close the connection so soon after sending */
-}
-
-Manager::TCPSocket::TCPSocket(const Anope::string &ip, int port) : Socket(-1, ip.find(':') != Anope::string::npos), ListenSocket(ip, port, ip.find(':') != Anope::string::npos)
-{
-}
-
-ClientSocket *Manager::TCPSocket::OnAccept(int fd, const sockaddrs &addr)
-{
- return new Client(this, fd, addr);
-}
-
-Manager::UDPSocket::UDPSocket(const Anope::string &ip, int port) : Socket(-1, ip.find(':') != Anope::string::npos, SOCK_DGRAM)
-{
-}
-
-Manager::UDPSocket::~UDPSocket()
-{
- for (unsigned i = 0; i < packets.size(); ++i)
- delete packets[i];
-}
-
-void Manager::UDPSocket::Reply(Packet *p)
-{
- packets.push_back(p);
- SocketEngine::Change(this, true, SF_WRITABLE);
-}
-
-bool Manager::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 DNS::Engine->HandlePacket(this, packet_buffer, length, &from_server);
-}
-
-bool Manager::UDPSocket::ProcessWrite()
-{
- Log(LOG_DEBUG_2) << "Resolver: Writing to DNS UDP socket";
-
- Packet *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::Change(this, false, SF_WRITABLE);
-
- return true;
-}
-
-Manager::Manager(const Anope::string &nameserver, const Anope::string &ip, int port) : Timer(300, Anope::CurTime, true), listen(false), serial(0), tcpsock(NULL), udpsock(NULL)
-{
- this->addrs.pton(nameserver.find(':') != Anope::string::npos ? AF_INET6 : AF_INET, nameserver, port);
-
- try
- {
- udpsock = new UDPSocket(ip, port);
- }
- catch (const SocketException &ex)
- {
- Log() << "Unable to create socket for Manager: " << ex.GetReason();
- }
-
- try
- {
- udpsock->Bind(ip, port);
- tcpsock = new TCPSocket(ip, port);
- listen = true;
- }
- 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 Manager to port " << port << ": " << ex.GetReason();
- }
-
- this->UpdateSerial();
-}
-
-Manager::~Manager()
-{
- delete udpsock;
- delete tcpsock;
-
- for (std::map<unsigned short, Request *>::iterator it = this->requests.begin(), it_end = this->requests.end(); it != it_end; ++it)
- {
- Request *request = it->second;
-
- Query rr(*request);
- rr.error = ERROR_UNKNOWN;
- request->OnError(&rr);
-
- delete request;
- }
- this->requests.clear();
-
- this->cache.clear();
-
- DNS::Engine = NULL;
-}
-
-bool Manager::HandlePacket(ReplySocket *s, const unsigned char *const packet_buffer, int length, sockaddrs *from)
-{
- if (length < Packet::HEADER_LENGTH)
- return true;
-
- Packet recv_packet(from);
-
- try
- {
- recv_packet.Fill(packet_buffer, length);
- }
- catch (const SocketException &ex)
- {
- Log(LOG_DEBUG_2) << ex.GetReason();
- return true;
- }
-
- if (!(recv_packet.flags & QUERYFLAGS_QR))
- {
- if (!listen)
- return true;
- else if (recv_packet.questions.empty())
- {
- Log(LOG_DEBUG_2) << "Resolver: Received a question with no questions?";
- return true;
- }
-
- Packet *packet = new Packet(recv_packet);
- packet->flags |= QUERYFLAGS_QR; /* This is a reponse */
- packet->flags |= 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 == QUERY_AXFR || q.type == QUERY_SOA)
- {
- ResourceRecord rr(q.name, QUERY_SOA);
- packet->answers.push_back(rr);
-
- if (q.type == QUERY_AXFR)
- {
- Anope::string token;
- spacesepstream sep(Config->DNSSOANS);
- while (sep.GetToken(token))
- {
- ResourceRecord rr2(q.name, QUERY_NS);
- rr2.rdata = token;
- packet->answers.push_back(rr2);
- }
- }
- break;
- }
- }
-
- FOREACH_MOD(I_OnDnsRequest, OnDnsRequest(recv_packet, packet));
-
- for (unsigned i = 0; i < recv_packet.questions.size(); ++i)
- {
- const Question& q = recv_packet.questions[i];
-
- if (q.type == QUERY_AXFR)
- {
- ResourceRecord rr(q.name, QUERY_SOA);
- packet->answers.push_back(rr);
- break;
- }
- }
-
- s->Reply(packet);
- return true;
- }
-
- if (from == NULL)
- {
- 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;
- }
-
- std::map<unsigned short, Request *>::iterator it = DNS::Engine->requests.find(recv_packet.id);
- if (it == DNS::Engine->requests.end())
- {
- Log(LOG_DEBUG_2) << "Resolver: Received an answer for something we didn't request";
- return true;
- }
- Request *request = it->second;
-
- if (recv_packet.flags & QUERYFLAGS_OPCODE)
- {
- Log(LOG_DEBUG_2) << "Resolver: Received a nonstandard query";
- recv_packet.error = ERROR_NONSTANDARD_QUERY;
- request->OnError(&recv_packet);
- }
- else if (recv_packet.flags & QUERYFLAGS_RCODE)
- {
- Error error = ERROR_UNKNOWN;
-
- switch (recv_packet.flags & QUERYFLAGS_RCODE)
- {
- case 1:
- Log(LOG_DEBUG_2) << "Resolver: format error";
- error = ERROR_FORMAT_ERROR;
- break;
- case 2:
- Log(LOG_DEBUG_2) << "Resolver: server error";
- error = ERROR_SERVER_FAILURE;
- break;
- case 3:
- Log(LOG_DEBUG_2) << "Resolver: domain not found";
- error = ERROR_DOMAIN_NOT_FOUND;
- break;
- case 4:
- Log(LOG_DEBUG_2) << "Resolver: not implemented";
- error = ERROR_NOT_IMPLEMENTED;
- break;
- case 5:
- Log(LOG_DEBUG_2) << "Resolver: refused";
- error = ERROR_REFUSED;
- break;
- default:
- break;
- }
-
- recv_packet.error = error;
- request->OnError(&recv_packet);
- }
- else if (recv_packet.answers.empty())
- {
- Log(LOG_DEBUG_2) << "Resolver: No resource records returned";
- recv_packet.error = ERROR_NO_RECORDS;
- request->OnError(&recv_packet);
- }
- else
- {
- Log(LOG_DEBUG_2) << "Resolver: Lookup complete for " << request->name;
- request->OnLookupComplete(&recv_packet);
- DNS::Engine->AddCache(recv_packet);
- }
-
- delete request;
- return true;
-}
-
-void Manager::AddCache(Query &r)
-{
- for (unsigned i = 0; i < r.answers.size(); ++i)
- {
- ResourceRecord &rr = r.answers[i];
- Log(LOG_DEBUG_3) << "Resolver cache: added cache for " << rr.name << " -> " << rr.rdata << ", ttl: " << rr.ttl;
- this->cache.insert(std::make_pair(rr.name, rr));
- }
-}
-
-bool Manager::CheckCache(Request *request)
-{
- cache_map::iterator it = this->cache.find(request->name);
- if (it != this->cache.end())
- {
- Query record(*request);
-
- for (cache_map::iterator it_end = this->cache.upper_bound(request->name); it != it_end; ++it)
- {
- ResourceRecord &rec = it->second;
- if (rec.created + static_cast<time_t>(rec.ttl) >= Anope::CurTime)
- record.answers.push_back(rec);
- }
-
- if (!record.answers.empty())
- {
- Log(LOG_DEBUG_3) << "Resolver: Using cached result for " << request->name;
- request->OnLookupComplete(&record);
- return true;
- }
- }
-
- return false;
-}
-
-void Manager::Tick(time_t now)
-{
- Log(LOG_DEBUG_2) << "Resolver: Purging DNS cache";
-
- for (cache_map::iterator it = this->cache.begin(), it_next; it != this->cache.end(); it = it_next)
- {
- ResourceRecord &req = it->second;
- it_next = it;
- ++it_next;
-
- if (req.created + static_cast<time_t>(req.ttl) < now)
- this->cache.erase(it);
- }
-}
-
-void Manager::Cleanup(Module *mod)
-{
- for (std::map<unsigned short, Request *>::iterator it = this->requests.begin(), it_end = this->requests.end(); it != it_end;)
- {
- unsigned short id = it->first;
- Request *req = it->second;
- ++it;
-
- if (req->creator && req->creator == mod)
- {
- Query rr(*req);
- rr.error = ERROR_UNLOADED;
- req->OnError(&rr);
-
- delete req;
- this->requests.erase(id);
- }
- }
-}
-
-void Manager::UpdateSerial()
-{
- serial = Anope::CurTime;
-}
-
-uint32_t Manager::GetSerial() const
-{
- return serial;
-}
-
-Query Manager::BlockingQuery(const Anope::string &mask, QueryType qt)
-{
- Question question(mask, qt);
- Query result(question);
-
- int type = AF_UNSPEC;
- if (qt == QUERY_A)
- type = AF_INET;
- else if (qt == QUERY_AAAA)
- type = AF_INET6;
-
- addrinfo hints;
- memset(&hints, 0, sizeof(hints));
- hints.ai_family = type;
-
- Log(LOG_DEBUG_2) << "Resolver: BlockingQuery: Looking up " << mask;
-
- addrinfo *addrresult;
- if (getaddrinfo(mask.c_str(), NULL, &hints, &addrresult) == 0)
- {
- for (addrinfo *cur = addrresult; cur; cur = cur->ai_next)
- {
- sockaddrs addr;
- memcpy(&addr, addrresult->ai_addr, addrresult->ai_addrlen);
- try
- {
- ResourceRecord rr(mask, qt);
- rr.rdata = addr.addr();
- result.answers.push_back(rr);
-
- Log(LOG_DEBUG_2) << "Resolver: BlockingQuery: " << mask << " -> " << rr.rdata;
- }
- catch (const SocketException &) { }
- }
-
- freeaddrinfo(addrresult);
- }
-
- return result;
-}
-
-
-
diff --git a/src/misc.cpp b/src/misc.cpp
index 417849df4..3f74888ee 100644
--- a/src/misc.cpp
+++ b/src/misc.cpp
@@ -18,10 +18,13 @@
#include "bots.h"
#include "language.h"
#include "regexpr.h"
+#include "sockets.h"
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netdb.h>
NumberList::NumberList(const Anope::string &list, bool descending) : is_valid(true), desc(descending)
{
@@ -652,3 +655,31 @@ Anope::string Anope::NormalizeBuffer(const Anope::string &buf)
return newbuf;
}
+Anope::string Anope::Resolve(const Anope::string &host, int type)
+{
+ Anope::string result = host;
+
+ addrinfo hints;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = type;
+
+ Log(LOG_DEBUG_2) << "Resolver: BlockingQuery: Looking up " << host;
+
+ addrinfo *addrresult = NULL;
+ if (getaddrinfo(host.c_str(), NULL, &hints, &addrresult) == 0)
+ {
+ sockaddrs addr;
+ memcpy(&addr, addrresult->ai_addr, addrresult->ai_addrlen);
+ try
+ {
+ result = addr.addr();
+ Log(LOG_DEBUG_2) << "Resolver: " << host << " -> " << result;
+ }
+ catch (const SocketException &) { }
+
+ freeaddrinfo(addrresult);
+ }
+
+ return result;
+}
+
diff --git a/src/module.cpp b/src/module.cpp
index e9c130076..396aa74e2 100644
--- a/src/module.cpp
+++ b/src/module.cpp
@@ -9,8 +9,8 @@
#include "services.h"
#include "modules.h"
-#include "dns.h"
#include "language.h"
+#include "account.h"
#ifdef GETTEXT_FOUND
# include <libintl.h>
@@ -46,8 +46,6 @@ Module::Module(const Anope::string &modname, const Anope::string &, ModType modt
Module::~Module()
{
- if (DNS::Engine)
- DNS::Engine->Cleanup(this);
/* Detach all event hooks for this module */
ModuleManager::DetachAll(this);
/* Clear any active callbacks this module has */
diff --git a/src/uplink.cpp b/src/uplink.cpp
index ac77672b2..9490329b3 100644
--- a/src/uplink.cpp
+++ b/src/uplink.cpp
@@ -15,7 +15,6 @@
#include "config.h"
#include "protocol.h"
#include "servers.h"
-#include "dns.h"
UplinkSocket *UplinkSock = NULL;
@@ -48,10 +47,9 @@ void Uplink::Connect()
if (!Config->LocalHost.empty())
UplinkSock->Bind(Config->LocalHost);
FOREACH_MOD(I_OnPreServerConnect, OnPreServerConnect());
- DNS::Query rep = DNS::Manager::BlockingQuery(u->host, u->ipv6 ? DNS::QUERY_AAAA : DNS::QUERY_A);
- Anope::string reply_ip = !rep.answers.empty() ? rep.answers.front().rdata : u->host;
- Log(LOG_TERMINAL) << "Attempting to connect to uplink #" << (Anope::CurrentUplink + 1) << " " << u->host << " (" << reply_ip << "), port " << u->port;
- UplinkSock->Connect(reply_ip, u->port);
+ Anope::string ip = Anope::Resolve(u->host, u->ipv6 ? AF_INET6 : AF_INET);
+ Log(LOG_TERMINAL) << "Attempting to connect to uplink #" << (Anope::CurrentUplink + 1) << " " << u->host << " (" << ip << "), port " << u->port;
+ UplinkSock->Connect(ip, u->port);
}