summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/dns.cpp806
-rw-r--r--src/main.cpp4
2 files changed, 448 insertions, 362 deletions
diff --git a/src/dns.cpp b/src/dns.cpp
index 7c5889477..02df2aea4 100644
--- a/src/dns.cpp
+++ b/src/dns.cpp
@@ -2,36 +2,70 @@
DNSManager *DNSEngine = NULL;
-static inline unsigned short GetRandomID()
+Question::Question()
{
- return rand();
+ this->type = DNS_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;
+}
+
+DNSQuery::DNSQuery()
+{
+ this->error = DNS_ERROR_NONE;
+}
+
+DNSQuery::DNSQuery(const Question &q)
+{
+ this->questions.push_back(q);
+ this->error = DNS_ERROR_NONE;
}
-DNSRequest::DNSRequest(const Anope::string &addr, QueryType qt, bool cache, Module *c) : timeout(NULL), use_cache(cache), id(0), creator(c), address(addr), QT(qt)
+DNSQuery::DNSQuery(const DNSPacket &p)
+{
+ this->questions = p.questions;
+ this->answers = p.answers;
+ this->authorities = p.authorities;
+ this->additional = p.additional;
+ this->error = DNS_ERROR_NONE;
+}
+
+DNSRequest::DNSRequest(const Anope::string &addr, QueryType qt, bool cache, Module *c) : Timer(Config->DNSTimeout), Question(addr, qt), use_cache(cache), id(0), creator(c)
{
if (!DNSEngine)
DNSEngine = new DNSManager(Config->NameServer, DNSManager::DNSPort);
if (DNSEngine->packets.size() == 65535)
throw SocketException("DNS queue full");
+
+ do
+ this->id = getrandom16();
+ while (!this->id || DNSEngine->requests.count(this->id));
+
+ DNSEngine->requests[this->id] = this;
}
DNSRequest::~DNSRequest()
{
- /* We never got a chance to fire off a query or create a timer */
- if (!this->timeout)
- return;
- /* DNSRequest came back, delete the timeout */
- else if (!this->timeout->done)
- delete this->timeout;
- /* Timeout timed us out, delete us from the requests map */
- else
- /* We can leave the packet, if it comes back later we will drop it */
- DNSEngine->requests.erase(this->id);
+ DNSEngine->requests.erase(this->id);
}
void DNSRequest::Process()
{
- Log(LOG_DEBUG_2) << "Resolver: Processing request to lookup " << this->address << ", of type " << this->QT;
+ Log(LOG_DEBUG_2) << "Resolver: Processing request to lookup " << this->name << ", of type " << this->type;
if (!DNSEngine)
throw SocketException("DNSEngine has not been initialized");
@@ -46,181 +80,373 @@ void DNSRequest::Process()
DNSPacket *p = new DNSPacket();
p->flags = DNS_QUERYFLAGS_RD;
- if (!p->AddQuestion(this->address, this->QT))
+ p->id = this->id;
+ p->questions.push_back(*this);
+ DNSEngine->packets.push_back(p);
+
+ SocketEngine::MarkWritable(DNSEngine);
+}
+
+void DNSRequest::OnError(const DNSQuery *r)
+{
+}
+
+void DNSRequest::Tick(time_t)
+{
+ Log(LOG_DEBUG_2) << "Resolver: timeout for query " << this->name;
+ DNSQuery rr(*this);
+ rr.error = DNS_ERROR_TIMEOUT;
+ this->OnError(&rr);
+}
+
+void DNSPacket::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))
{
- Log() << "Resolver: Unable to lookup host " << this->address << " of type " << this->QT << " - internal error";
- delete p;
- delete this;
- throw SocketException("Unable to lookup host " + this->address + " of type " + stringify(this->QT) + " - internal error");
+ output[pos++] = token.length();
+ memcpy(&output[pos], token.c_str(), token.length());
+ pos += token.length();
}
- unsigned short packet_id;
+ output[pos++] = 0;
+}
+
+Anope::string DNSPacket::UnpackName(const unsigned char *input, unsigned short input_size, unsigned short &pos)
+{
+ Anope::string name;
+ unsigned short pos_ptr = pos;
+
+ if (pos_ptr >= input_size)
+ throw SocketException("Unable to unpack name");
+
+ unsigned short offset;
do
- packet_id = GetRandomID();
- while (DNSEngine->requests.count(packet_id));
+ {
+ offset = input[pos_ptr];
- p->id = this->id = packet_id;
- DNSEngine->requests[this->id] = this;
- DNSEngine->packets.push_back(p);
+ if (offset & DNS_POINTER)
+ {
+ if (pos_ptr + 1 >= input_size)
+ throw SocketException("Unable to unpack name");
+ offset = (offset & DNS_LABEL) << 8 | input[++pos_ptr];
+ pos_ptr = offset;
+ if (pos_ptr >= input_size)
+ throw SocketException("Unable to unpack name");
+ offset = input[pos_ptr];
+ ++pos;
+ }
- SocketEngine::MarkWritable(DNSEngine);
+ if (pos_ptr + offset >= input_size)
+ throw SocketException("Unable to unpack name");
+ if (!name.empty())
+ name += ".";
+ for (unsigned i = 1; i <= offset; ++i)
+ name += input[pos_ptr + i];
+
+ pos_ptr += offset + 1;
+ if (pos_ptr >= input_size)
+ throw SocketException("Unable to unpack name");
+ offset = input[pos_ptr];
+ if (pos_ptr > pos)
+ pos = pos_ptr;
+ }
+ while (offset);
+
+ ++pos;
- this->timeout = new DNSRequestTimeout(this, Config->DNSTimeout);
+ Log(LOG_DEBUG_2) << "Resolver: UnpackName successfully unpacked " << name;
+
+ return name;
}
-void DNSRequest::OnError(const DNSRecord *r)
+Question DNSPacket::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;
}
-inline DNSPacket::DNSPacket()
+ResourceRecord DNSPacket::UnpackResourceRecord(const unsigned char *input, unsigned short input_size, unsigned short &pos)
{
- this->id = this->flags = this->qdcount = this->ancount = this->nscount = this->arcount = this->payload_count = 0;
- memset(&this->payload, 0, sizeof(this->payload));
+ 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 DNS_QUERY_A:
+ {
+ if (pos + 4 > input_size)
+ throw SocketException("Unable to unpack resource record");
+
+ in_addr addr;
+ addr.s_addr = input[pos] | (input[pos + 1] << 8) | (input[pos + 2] << 16) | (input[pos + 3] << 24);
+ pos += 4;
+
+ sockaddrs addrs;
+ addrs.ntop(AF_INET, &addr);
+
+ record.rdata = addrs.addr();
+ break;
+ }
+ case DNS_QUERY_AAAA:
+ {
+ if (pos + 16 > input_size)
+ throw SocketException("Unable to unpack resource record");
+
+ in6_addr addr;
+ for (int j = 0; j < 16; ++j)
+ addr.s6_addr[j] = input[pos + j];
+ pos += 16;
+
+ sockaddrs addrs;
+ addrs.ntop(AF_INET6, &addr);
+
+ record.rdata = addrs.addr();
+ break;
+ }
+ case DNS_QUERY_CNAME:
+ case DNS_QUERY_PTR:
+ {
+ record.rdata = this->UnpackName(input, input_size, pos);
+ break;
+ }
+ default:
+ break;
+ }
+
+ Log(LOG_DEBUG_2) << "Resolver: " << record.name << " -> " << record.rdata;
+
+ return record;
+}
+
+DNSPacket::DNSPacket() : DNSQuery()
+{
+ this->id = this->flags = 0;
}
-bool DNSPacket::AddQuestion(const Anope::string &name, const QueryType qt)
+void DNSPacket::Fill(const unsigned char *input, const unsigned short len)
{
- unsigned char temp_buffer[512] = "";
- unsigned short buffer_pos = 0;
+ if (len < DNSPacket::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;
- Anope::string working_name = name;
+ unsigned short qdcount = (input[packet_pos] << 8) | input[packet_pos + 1];
+ packet_pos += 2;
- switch (qt)
+ 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 DNSPacket::Pack(unsigned char *output, unsigned short output_size)
+{
+ if (output_size < DNSPacket::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)
{
- case DNS_QUERY_PTR:
+ Question &q = this->questions[i];
+
+ if (q.type == DNS_QUERY_PTR)
{
- if (working_name.find(':') != Anope::string::npos)
+ if (q.name.find(':') != Anope::string::npos)
{
- in6_addr ip;
- if (!inet_pton(AF_INET6, working_name.c_str(), &ip))
- {
- Log(LOG_DEBUG_2) << "Resolver: Received an invalid IP for PTR lookup (" << working_name << "): " << Anope::LastError();
- return false;
- }
+ sockaddrs ip;
+ ip.pton(AF_INET6, q.name);
static const char *const hex = "0123456789abcdef";
char reverse_ip[128];
unsigned reverse_ip_count = 0;
- for (int i = 15; i >= 0; --i)
+ for (int j = 15; j >= 0; --j)
{
- reverse_ip[reverse_ip_count++] = hex[ip.s6_addr[i] & 0xF];
+ 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.s6_addr[i] >> 4];
+ 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;
- working_name = reverse_ip;
- working_name += "ip6.arpa";
- Log(LOG_DEBUG_2) << "IP changed to in6.arpa format: " << working_name;
+ q.name = reverse_ip;
+ q.name += "ip6.arpa";
}
else
{
- in_addr ip;
- if (!inet_pton(AF_INET, working_name.c_str(), &ip))
- {
- Log(LOG_DEBUG_2) << "Resolver: Received an invalid IP for PTR lookup (" << working_name << "): " << Anope::LastError();
- return false;
- }
- unsigned long reverse_ip = ((ip.s_addr & 0xFF) << 24) | ((ip.s_addr & 0xFF00) << 8) | ((ip.s_addr & 0xFF0000) >> 8) | ((ip.s_addr & 0xFF000000) >> 24);
- char ipbuf[INET_ADDRSTRLEN];
- if (!inet_ntop(AF_INET, &reverse_ip, ipbuf, sizeof(ipbuf)))
- {
- Log(LOG_DEBUG_2) << "Resolver: Reformatted IP " << working_name << " back into an invalid IP: " << Anope::LastError();
- return false;
- }
+ sockaddrs ip;
+ ip.pton(AF_INET, q.name);
- working_name = ipbuf;
- working_name += ".in-addr.arpa";
- Log(LOG_DEBUG_2) << "IP changed to in-addr.arpa format: " << working_name;
- }
- }
- case DNS_QUERY_CNAME:
- case DNS_QUERY_A:
- case DNS_QUERY_AAAA:
- {
- sepstream sep(working_name, '.');
- Anope::string token;
- while (sep.GetToken(token))
- {
- temp_buffer[buffer_pos++] = token.length();
- memcpy(&temp_buffer[buffer_pos], token.c_str(), token.length());
- buffer_pos += token.length();
+ 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";
}
- break;
}
- default:
- Log(LOG_DEBUG_2) << "Resolver: Received an unknown query type format " << qt;
- return false;
+
+ 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;
}
- temp_buffer[buffer_pos++] = 0;
+ 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];
- short i = htons(qt);
- memcpy(&temp_buffer[buffer_pos], &i, 2);
- buffer_pos += 2;
+ this->PackName(output, output_size, pos, rr.name);
- i = htons(1);
- memcpy(&temp_buffer[buffer_pos], &i, 2);
- buffer_pos += 2;
+ if (pos + 8 >= output_size)
+ throw SocketException("Unable to pack packet");
- Log(LOG_DEBUG_3) << "Resolver: Packet payload to: " << temp_buffer;
- Log(LOG_DEBUG_3) << "Resolver: Bytes used: " << buffer_pos << ", payload count " << this->payload_count;
+ short s = htons(rr.type);
+ memcpy(&output[pos], &s, 2);
+ pos += 2;
- if (buffer_pos > (sizeof(this->payload) - this->payload_count))
- return false;
-
- memmove(this->payload + this->payload_count, temp_buffer, buffer_pos);
- this->payload_count += buffer_pos;
+ s = htons(rr.qclass);
+ memcpy(&output[pos], &s, 2);
+ pos += 2;
- this->qdcount++;
+ long l = htonl(rr.ttl);
+ memcpy(&output[pos], &l, 4);
+ pos += 4;
- return true;
-}
+ switch (rr.type)
+ {
+ case DNS_QUERY_A:
+ {
+ if (pos + 6 > output_size)
+ throw SocketException("Unable to pack packet");
-inline void DNSPacket::FillPacket(const unsigned char *input, const size_t length)
-{
- this->id = (input[0] << 8) | input[1];
- this->flags = (input[2] << 8) | input[3];
- this->qdcount = (input[4] << 8) | input[5];
- this->ancount = (input[6] << 8) | input[7];
- this->nscount = (input[8] << 8) | input[9];
- this->arcount = (input[10] << 8) | input[11];
- memcpy(this->payload, &input[12], length);
- this->payload_count = length;
-}
+ sockaddrs addr;
+ addr.pton(AF_INET, rr.rdata);
-inline void DNSPacket::FillBuffer(unsigned char *buffer)
-{
- buffer[0] = this->id >> 8;
- buffer[1] = this->id & 0xFF;
- buffer[2] = this->flags >> 8;
- buffer[3] = this->flags & 0xFF;
- buffer[4] = this->qdcount >> 8;
- buffer[5] = this->qdcount & 0xFF;
- buffer[6] = this->ancount >> 8;
- buffer[7] = this->ancount & 0xFF;
- buffer[8] = this->nscount >> 8;
- buffer[9] = this->nscount & 0xFF;
- buffer[10] = this->arcount >> 8;
- buffer[11] = this->arcount & 0xFF;
- memcpy(&buffer[12], this->payload, this->payload_count);
-}
+ s = htons(4);
+ memcpy(&output[pos], &s, 2);
+ pos += 2;
-inline DNSRecord::DNSRecord(const Anope::string &n) : name(n)
-{
- this->type = DNS_QUERY_NONE;
- this->error = DNS_ERROR_NONE;
- this->record_class = this->ttl = this->rdlength = 0;
- this->created = Anope::CurTime;
-}
+ memcpy(&output[pos], &addr.sa4.sin_addr, 4);
+ pos += 4;
+ break;
+ }
+ case DNS_QUERY_AAAA:
+ {
+ if (pos + 18 > output_size)
+ throw SocketException("Unable to pack packet");
-DNSRecord::operator bool() const
-{
- return !this->result.empty();
+ sockaddrs addr;
+ addr.pton(AF_INET6, rr.rdata);
+
+ s = htons(16);
+ memcpy(&output[pos], &s, 2);
+ pos += 2;
+
+ memcpy(&output[pos], &addr.sa6.sin6_addr, 16);
+ pos += 16;
+ break;
+ }
+ case DNS_QUERY_CNAME:
+ case DNS_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);
+
+ i = htons(pos - packet_pos_save - 2);
+ memcpy(&output[packet_pos_save], &i, 2);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ return pos;
}
-DNSManager::DNSManager(const Anope::string &nameserver, int port) : Timer(300, Anope::CurTime, true), Socket(0, nameserver.find(':') != Anope::string::npos, SOCK_DGRAM)
+DNSManager::DNSManager(const Anope::string &nameserver, int port) : Timer(300, Anope::CurTime, true), Socket(-1, nameserver.find(':') != Anope::string::npos, SOCK_DGRAM)
{
this->addrs.pton(this->IPv6 ? AF_INET6 : AF_INET, nameserver, port);
}
@@ -234,15 +460,15 @@ DNSManager::~DNSManager()
for (std::map<short, DNSRequest *>::iterator it = this->requests.begin(), it_end = this->requests.end(); it != it_end; ++it)
{
DNSRequest *request = it->second;
- DNSRecord rr(request->address);
+
+ DNSQuery rr(*request);
rr.error = DNS_ERROR_UNKNOWN;
request->OnError(&rr);
+
delete request;
}
this->requests.clear();
- for (std::multimap<Anope::string, DNSRecord *>::iterator it = this->cache.begin(), it_end = this->cache.end(); it != it_end; ++it)
- delete it->second;
this->cache.clear();
DNSEngine = NULL;
@@ -257,10 +483,8 @@ bool DNSManager::ProcessRead()
socklen_t x = sizeof(from_server);
int length = recvfrom(this->GetFD(), reinterpret_cast<char *>(&packet_buffer), sizeof(packet_buffer), 0, &from_server.sa, &x);
- if (length < 12)
+ if (length < DNSPacket::HEADER_LENGTH)
return true;
- /* Remove header length */
- length -= 12;
if (this->addrs != from_server)
{
@@ -269,7 +493,16 @@ bool DNSManager::ProcessRead()
}
DNSPacket recv_packet;
- recv_packet.FillPacket(packet_buffer, length);
+
+ try
+ {
+ recv_packet.Fill(packet_buffer, length);
+ }
+ catch (const SocketException &ex)
+ {
+ Log(LOG_DEBUG_2) << ex.GetReason();
+ return true;
+ }
std::map<short, DNSRequest *>::iterator it = DNSEngine->requests.find(recv_packet.id);
if (it == DNSEngine->requests.end())
@@ -278,21 +511,18 @@ bool DNSManager::ProcessRead()
return true;
}
DNSRequest *request = it->second;
- DNSEngine->requests.erase(it);
if (!(recv_packet.flags & DNS_QUERYFLAGS_QR))
{
Log(LOG_DEBUG_2) << "Resolver: Received a non-answer";
- DNSRecord rr(request->address);
- rr.error = DNS_ERROR_NOT_AN_ANSWER;
- request->OnError(&rr);
+ recv_packet.error = DNS_ERROR_NOT_AN_ANSWER;
+ request->OnError(&recv_packet);
}
else if (recv_packet.flags & DNS_QUERYFLAGS_OPCODE)
{
Log(LOG_DEBUG_2) << "Resolver: Received a nonstandard query";
- DNSRecord rr(request->address);
- rr.error = DNS_ERROR_NONSTANDARD_QUERY;
- request->OnError(&rr);
+ recv_packet.error = DNS_ERROR_NONSTANDARD_QUERY;
+ request->OnError(&recv_packet);
}
else if (recv_packet.flags & DNS_QUERYFLAGS_RCODE)
{
@@ -324,163 +554,20 @@ bool DNSManager::ProcessRead()
break;
}
- DNSRecord *rr = new DNSRecord(request->address);
- rr->ttl = 300;
- rr->error = error;
- request->OnError(rr);
- DNSEngine->AddCache(rr);
+ recv_packet.error = error;
+ request->OnError(&recv_packet);
}
- else if (recv_packet.ancount < 1)
+ else if (recv_packet.answers.empty())
{
Log(LOG_DEBUG_2) << "Resolver: No resource records returned";
- DNSRecord rr(request->address);
- rr.error = DNS_ERROR_NO_RECORDS;
- request->OnError(&rr);
+ recv_packet.error = DNS_ERROR_NO_RECORDS;
+ request->OnError(&recv_packet);
}
else
{
- unsigned packet_pos = 0;
-
- /* Skip over the questions in this reponse */
- for (unsigned qdcount = 0; qdcount < recv_packet.qdcount; ++qdcount)
- {
- for (unsigned offset = recv_packet.payload[packet_pos]; offset; packet_pos += offset + 1, offset = recv_packet.payload[packet_pos])
- {
- if (offset & 0xC0)
- {
- packet_pos += 2;
- offset = 0;
- }
- }
-
- packet_pos += 5;
- }
-
- for (unsigned ancount = 0; ancount < recv_packet.ancount; ++ancount)
- {
- Anope::string name;
- unsigned packet_pos_ptr = packet_pos;
- for (unsigned offset = recv_packet.payload[packet_pos_ptr]; offset; packet_pos_ptr += offset + 1, offset = recv_packet.payload[packet_pos_ptr])
- {
- if (offset & 0xC0)
- {
- offset = (offset & 0x3F) << 8 | recv_packet.payload[++packet_pos_ptr];
- packet_pos_ptr = offset - 12;
- offset = recv_packet.payload[packet_pos_ptr];
- }
- for (unsigned i = 1; i <= offset; ++i)
- name += recv_packet.payload[packet_pos_ptr + i];
- name += ".";
- }
- name.erase(name.begin() + name.length() - 1);
-
- if (packet_pos_ptr < packet_pos)
- packet_pos += 2;
- else
- packet_pos = packet_pos_ptr + 1;
-
- DNSRecord *rr = new DNSRecord(name);
-
- Log(LOG_DEBUG_2) << "Resolver: Received answer for " << name;
-
- rr->type = static_cast<QueryType>(recv_packet.payload[packet_pos] << 8 | (recv_packet.payload[packet_pos + 1] & 0xFF));
- packet_pos += 2;
-
- rr->record_class = recv_packet.payload[packet_pos] << 8 | (recv_packet.payload[packet_pos + 1] & 0xFF);
- packet_pos += 2;
-
- rr->ttl = (recv_packet.payload[packet_pos] << 24) | (recv_packet.payload[packet_pos + 1] << 16) | (recv_packet.payload[packet_pos + 2] << 8) | recv_packet.payload[packet_pos + 3];
- packet_pos += 4;
-
- rr->rdlength = (recv_packet.payload[packet_pos] << 8 | recv_packet.payload[packet_pos + 1]);
- packet_pos += 2;
-
- switch (rr->type)
- {
- case DNS_QUERY_A:
- {
- unsigned long ip = (recv_packet.payload[packet_pos]) | (recv_packet.payload[packet_pos + 1]) << 8 | (recv_packet.payload[packet_pos + 2] << 16) | (recv_packet.payload[packet_pos + 3] << 24);
- packet_pos += 4;
-
- char ipbuf[INET_ADDRSTRLEN];
- if (!inet_ntop(AF_INET, &ip, ipbuf, sizeof(ipbuf)))
- {
- Log(LOG_DEBUG_2) << "Resolver: Received an invalid IP for DNS_QUERY_A: " << Anope::LastError();
- rr->error = DNS_ERROR_FORMAT_ERROR;
- request->OnError(rr);
- delete rr;
- rr = NULL;
- }
- else
- rr->result = ipbuf;
- break;
- }
- case DNS_QUERY_AAAA:
- {
- unsigned char ip[16];
- for (int i = 0; i < 16; ++i)
- ip[i] = recv_packet.payload[packet_pos + i];
- packet_pos += 16;
-
- char ipbuf[INET6_ADDRSTRLEN];
- if (!inet_ntop(AF_INET6, &ip, ipbuf, sizeof(ipbuf)))
- {
- Log(LOG_DEBUG_2) << "Resolver: Received an invalid IP for DNS_QUERY_A: " << Anope::LastError();
- rr->error = DNS_ERROR_FORMAT_ERROR;
- request->OnError(rr);
- delete rr;
- rr = NULL;
- }
- else
- rr->result = ipbuf;
- break;
- }
- case DNS_QUERY_CNAME:
- case DNS_QUERY_PTR:
- {
- name.clear();
- packet_pos_ptr = packet_pos;
- for (unsigned offset = recv_packet.payload[packet_pos_ptr]; offset; packet_pos_ptr += offset + 1, offset = recv_packet.payload[packet_pos_ptr])
- {
- if (offset & 0xC0)
- {
- offset = (offset & 0x3F) << 8 | recv_packet.payload[++packet_pos_ptr];
- packet_pos_ptr = offset - 12;
- offset = recv_packet.payload[packet_pos_ptr];
- }
- for (unsigned i = 1; i <= offset; ++i)
- name += recv_packet.payload[packet_pos_ptr + i];
- name += ".";
- }
- name.erase(name.begin() + name.length() - 1);
-
- if (packet_pos_ptr < packet_pos)
- packet_pos += 2;
- else
- packet_pos = packet_pos_ptr + 1;
-
- rr->result = name;
- break;
- }
- default:
- rr->error = DNS_ERROR_INVALIDTYPE;
- request->OnError(rr);
- delete rr;
- rr = NULL;
- }
-
- if (rr && request->QT != rr->type)
- {
- delete rr;
- rr = NULL;
- }
-
- if (rr)
- {
- request->OnLookupComplete(rr);
- DNSEngine->AddCache(rr);
- }
- }
+ Log(LOG_DEBUG_2) << "Resolver: Lookup complete for " << request->name;
+ request->OnLookupComplete(&recv_packet);
+ DNSEngine->AddCache(recv_packet);
}
delete request;
@@ -491,50 +578,58 @@ bool DNSManager::ProcessWrite()
{
Log(LOG_DEBUG_2) << "Resolver: Writing to DNS socket";
- DNSPacket *r = DNSEngine->packets.size() ? DNSEngine->packets[0] : NULL;
+ DNSPacket *r = !DNSEngine->packets.empty() ? DNSEngine->packets.front() : NULL;
if (r != NULL)
{
- unsigned char buffer[524];
- r->FillBuffer(buffer);
+ try
+ {
+ unsigned char buffer[524];
+ unsigned short len = r->Pack(buffer, sizeof(buffer));
- sendto(this->GetFD(), reinterpret_cast<char *>(buffer), r->payload_count + 12, 0, &this->addrs.sa, this->addrs.size());
+ sendto(this->GetFD(), reinterpret_cast<char *>(buffer), len, 0, &this->addrs.sa, this->addrs.size());
+ }
+ catch (const SocketException &) { }
delete r;
- DNSEngine->packets.erase(DNSEngine->packets.begin());
+ DNSEngine->packets.pop_front();
}
if (DNSEngine->packets.empty())
SocketEngine::ClearWritable(this);
+
return true;
}
-void DNSManager::AddCache(DNSRecord *rr)
+void DNSManager::AddCache(DNSQuery &r)
{
- this->cache.insert(std::make_pair(rr->name, rr));
+ 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 DNSManager::CheckCache(DNSRequest *request)
{
- std::multimap<Anope::string, DNSRecord *>::iterator it = this->cache.find(request->address);
+ cache_map::iterator it = this->cache.find(request->name);
if (it != this->cache.end())
{
- std::multimap<Anope::string, DNSRecord *>::iterator it_end = this->cache.upper_bound(request->address);
- bool ret = false;
+ DNSQuery record(*request);
- for (; it != it_end; ++it)
+ for (cache_map::iterator it_end = this->cache.upper_bound(request->name); it != it_end; ++it)
{
- DNSRecord *rec = it->second;
- if (rec->created + rec->ttl >= Anope::CurTime)
- {
- if (rec->error == DNS_ERROR_NONE)
- request->OnLookupComplete(rec);
- else
- request->OnError(rec);
- ret = true;
- }
+ ResourceRecord &rec = it->second;
+ if (rec.created + 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 ret;
}
return false;
@@ -544,17 +639,14 @@ void DNSManager::Tick(time_t now)
{
Log(LOG_DEBUG_2) << "Resolver: Purging DNS cache";
- for (std::multimap<Anope::string, DNSRecord *>::iterator it = this->cache.begin(), it_next; it != this->cache.end(); it = it_next)
+ for (cache_map::iterator it = this->cache.begin(), it_next; it != this->cache.end(); it = it_next)
{
- DNSRecord *req = it->second;
+ ResourceRecord &req = it->second;
it_next = it;
++it_next;
- if (req->created + req->ttl < now)
- {
+ if (req.created + req.ttl < now)
this->cache.erase(it);
- delete req;
- }
}
}
@@ -568,39 +660,20 @@ void DNSManager::Cleanup(Module *mod)
if (req->creator && req->creator == mod)
{
- DNSRecord rr(req->address);
+ DNSQuery rr(*req);
rr.error = DNS_ERROR_UNLOADED;
req->OnError(&rr);
+
delete req;
this->requests.erase(id);
}
}
}
-DNSRequestTimeout::DNSRequestTimeout(DNSRequest *r, time_t timeout) : Timer(timeout), request(r)
+DNSQuery DNSManager::BlockingQuery(const Anope::string &mask, QueryType qt)
{
- this->done = false;
-}
-
-DNSRequestTimeout::~DNSRequestTimeout()
-{
-}
-
-void DNSRequestTimeout::Tick(time_t)
-{
- this->done = true;
- DNSRecord rr(this->request->address);
- rr.error = DNS_ERROR_TIMEOUT;
- this->request->OnError(&rr);
- delete this->request;
-}
-
-DNSRecord DNSManager::BlockingQuery(const Anope::string &mask, QueryType qt)
-{
- DNSRecord result(mask);
- addrinfo *addrresult, hints;
-
- result.type = qt;
+ Question question(mask, qt);
+ DNSQuery result(question);
int type = AF_UNSPEC;
if (qt == DNS_QUERY_A)
@@ -608,18 +681,29 @@ DNSRecord DNSManager::BlockingQuery(const Anope::string &mask, QueryType qt)
else if (qt == DNS_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)
{
- sockaddrs addr;
- memcpy(&addr, addrresult->ai_addr, addrresult->ai_addrlen);
- try
+ for (addrinfo *cur = addrresult; cur; cur = cur->ai_next)
{
- result.result = addr.addr();
+ 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 &) { }
}
- catch (const SocketException &) { }
freeaddrinfo(addrresult);
}
@@ -627,3 +711,5 @@ DNSRecord DNSManager::BlockingQuery(const Anope::string &mask, QueryType qt)
return result;
}
+
+
diff --git a/src/main.cpp b/src/main.cpp
index b00a6ee6e..69a5fe38f 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -183,8 +183,8 @@ static void Connect()
if (!Config->LocalHost.empty())
UplinkSock->Bind(Config->LocalHost);
FOREACH_MOD(I_OnPreServerConnect, OnPreServerConnect());
- DNSRecord req = DNSManager::BlockingQuery(u->host, u->ipv6 ? DNS_QUERY_AAAA : DNS_QUERY_A);
- UplinkSock->Connect(req.result, u->port);
+ DNSQuery rep = DNSManager::BlockingQuery(u->host, u->ipv6 ? DNS_QUERY_AAAA : DNS_QUERY_A);
+ UplinkSock->Connect(!rep.answers.empty() ? rep.answers.front().rdata : u->host, u->port);
}
/*************************************************************************/