summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdam <Adam@anope.org>2012-12-15 01:33:31 -0500
committerAdam <Adam@anope.org>2012-12-15 01:33:31 -0500
commitdcd34d37287adb6aa03d32edd17fc8015b52feb8 (patch)
tree4eb963bc60c8db543973456e7fce35aaae5a4ad4
parentcdec0a3f96bbfa6d5300b7a6d602fd170e7ccd56 (diff)
Move DNS handling to a module
-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;