diff options
author | Adam <Adam@anope.org> | 2011-08-23 19:06:15 -0400 |
---|---|---|
committer | Adam <Adam@anope.org> | 2011-09-10 01:55:37 -0400 |
commit | b504791bad450dfc9d28faa8b2009f8eaef0f485 (patch) | |
tree | efd935358ade31dfb0987091d75864d7c3985443 /src | |
parent | d5749c11f3ad7288c9307e156f263a2223ecd1a1 (diff) |
Cleaned up the dns engine, and fixed sometimes parsing multiple answer queries incorrectly
Diffstat (limited to 'src')
-rw-r--r-- | src/dns.cpp | 806 | ||||
-rw-r--r-- | src/main.cpp | 4 |
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); } /*************************************************************************/ |