diff options
| author | Adam <Adam@anope.org> | 2012-12-15 01:33:31 -0500 |
|---|---|---|
| committer | Adam <Adam@anope.org> | 2012-12-15 01:33:31 -0500 |
| commit | dcd34d37287adb6aa03d32edd17fc8015b52feb8 (patch) | |
| tree | 4eb963bc60c8db543973456e7fce35aaae5a4ad4 | |
| parent | cdec0a3f96bbfa6d5300b7a6d602fd170e7ccd56 (diff) | |
Move DNS handling to a module
| -rw-r--r-- | data/example.conf | 46 | ||||
| -rw-r--r-- | data/modules.example.conf | 48 | ||||
| -rw-r--r-- | data/operserv.example.conf | 3 | ||||
| -rw-r--r-- | include/anope.h | 7 | ||||
| -rw-r--r-- | include/config.h | 14 | ||||
| -rw-r--r-- | include/defs.h | 1 | ||||
| -rw-r--r-- | include/dns.h | 278 | ||||
| -rw-r--r-- | include/module.h | 1 | ||||
| -rw-r--r-- | modules/commands/os_dns.cpp | 15 | ||||
| -rw-r--r-- | modules/extra/dns.h | 178 | ||||
| -rw-r--r-- | modules/extra/m_dns.cpp | 1025 | ||||
| -rw-r--r-- | modules/extra/m_dnsbl.cpp | 16 | ||||
| -rw-r--r-- | src/config.cpp | 44 | ||||
| -rw-r--r-- | src/dns.cpp | 963 | ||||
| -rw-r--r-- | src/misc.cpp | 31 | ||||
| -rw-r--r-- | src/module.cpp | 4 | ||||
| -rw-r--r-- | src/uplink.cpp | 8 |
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> ¶ms) @@ -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; |
