From 1114ac3cecb1ff9e7a78b183930a367ba25346af Mon Sep 17 00:00:00 2001 From: Tobias Blomberg Date: Sun, 3 Apr 2022 20:50:16 +0200 Subject: [PATCH] Host lookups did not work for hosts file --- src/async/core/AsyncDnsLookup.cpp | 2 - src/async/core/AsyncDnsLookupWorker.h | 15 + src/async/core/AsyncIpAddress.h | 10 +- src/async/cpp/AsyncCppDnsLookupWorker.cpp | 420 +++++++++++++--------- src/async/cpp/AsyncCppDnsLookupWorker.h | 3 + src/async/demo/AsyncDnsLookup_demo.cpp | 194 +++++----- src/versions | 4 +- 7 files changed, 371 insertions(+), 277 deletions(-) diff --git a/src/async/core/AsyncDnsLookup.cpp b/src/async/core/AsyncDnsLookup.cpp index ce160a61..8f6a426f 100644 --- a/src/async/core/AsyncDnsLookup.cpp +++ b/src/async/core/AsyncDnsLookup.cpp @@ -125,8 +125,6 @@ DnsLookup::DnsLookup(void) m_worker = Application::app().newDnsLookupWorker(*this); m_worker->resultsReady.connect( sigc::mem_fun(*this, &DnsLookup::onResultsReady)); - //std::cout << "### DnsLookup::DnsLookup: isPending()=" - // << isPending() << std::endl; } /* DnsLookup::DnsLookup */ diff --git a/src/async/core/AsyncDnsLookupWorker.h b/src/async/core/AsyncDnsLookupWorker.h index 0ce83d00..5f321b18 100644 --- a/src/async/core/AsyncDnsLookupWorker.h +++ b/src/async/core/AsyncDnsLookupWorker.h @@ -310,6 +310,14 @@ class DnsLookupWorker */ virtual void abortLookup(void) = 0; + /** + * @brief Add a new resource record to the result list + * @param rr A new resource record + * + * The lookup worker use this function to add resource records to the + * result list. The resource record will be owned by this class so must not + * be deleted by the caller. + */ void addResourceRecord(DnsResourceRecord* rr) { if (rr->type() == DnsResourceRecordSRV::staticType()) @@ -324,6 +332,9 @@ class DnsLookupWorker } } + /** + * @brief Called by the lookup worker when done + */ void workerDone(void) { m_lookup_pending = false; @@ -404,6 +415,10 @@ class DnsLookupWorker resultsReady(); } + /** + * @brief Called by the lookup worker to mark the lookup as failed + * @param failed Set to \em false to clear the failure state + */ void setLookupFailed(bool failed=true) { m_lookup_failed = failed; } private: diff --git a/src/async/core/AsyncIpAddress.h b/src/async/core/AsyncIpAddress.h index 1a4d8cc5..54da2388 100644 --- a/src/async/core/AsyncIpAddress.h +++ b/src/async/core/AsyncIpAddress.h @@ -9,7 +9,7 @@ Rigt now it can only handle IPv4 addresses. \verbatim Async - A library for programming event driven applications -Copyright (C) 2003 Tobias Blomberg +Copyright (C) 2003-2022 Tobias Blomberg This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -176,7 +176,13 @@ class IpAddress * @return The IP address string */ std::string toString(void) const; - + + /** + * @brief Set the IP address + * @param addr The IP address to set + */ + void setIp(const Ip4Addr& addr) { m_addr = addr; } + /** * @brief Set the IP address from a string * @param str The string to parse (e.g. "192.168.0.1") diff --git a/src/async/cpp/AsyncCppDnsLookupWorker.cpp b/src/async/cpp/AsyncCppDnsLookupWorker.cpp index afb2b145..b3882150 100644 --- a/src/async/cpp/AsyncCppDnsLookupWorker.cpp +++ b/src/async/cpp/AsyncCppDnsLookupWorker.cpp @@ -169,15 +169,6 @@ bool CppDnsLookupWorker::doLookup(void) setLookupFailed(false); - struct in_addr inp; - if (inet_aton(dns().label().c_str(), &inp) == 1) - { - addResourceRecord( - new DnsResourceRecordA(dns().label(), 1, IpAddress(inp))); - workerDone(); - return true; - } - int fd[2]; if (pipe(fd) != 0) { @@ -206,7 +197,16 @@ bool CppDnsLookupWorker::doLookup(void) void CppDnsLookupWorker::abortLookup(void) { - m_result = std::move(std::future()); + if (m_result.valid()) + { + const ThreadContext& ctx(m_result.get()); + + if (ctx.addrinfo != nullptr) + { + freeaddrinfo(ctx.addrinfo); + } + //m_result = std::move(std::future()); + } int fd = m_notifier_watch.fd(); if (fd >= 0) @@ -244,51 +244,101 @@ CppDnsLookupWorker::ThreadContext CppDnsLookupWorker::workerFunc( { std::ostream& th_cerr = ctx.thread_cerr; - struct __res_state state; - int ret = res_ninit(&state); - if (ret != -1) + int qtype = 0; + switch (ctx.type) { - state.options = RES_DEFAULT; - - int qtype = 0; - switch (ctx.type) + case DnsLookup::Type::A: { - case DnsLookup::Type::A: - qtype = ns_t_a; - break; - case DnsLookup::Type::PTR: - qtype = ns_t_ptr; - break; - case DnsLookup::Type::CNAME: - qtype = ns_t_cname; - break; - case DnsLookup::Type::SRV: - qtype = ns_t_srv; - break; - default: - assert(0); + struct addrinfo hints = {0}; + hints.ai_family = AF_INET; + int ret = getaddrinfo(ctx.label.c_str(), NULL, &hints, &ctx.addrinfo); + if (ret != 0) + { + th_cerr << "*** WARNING[getaddrinfo]: Could not look up host \"" + << ctx.label << "\": " << gai_strerror(ret) << std::endl; + } + else if (ctx.addrinfo == nullptr) + { + th_cerr << "*** WARNING[getaddrinfo]: No address info returned " + "for host \"" << ctx.label << "\"" << std::endl; + } + break; } - - const char *dname = ctx.label.c_str(); - ctx.anslen = res_nsearch(&state, dname, ns_c_in, qtype, - ctx.answer, NS_PACKETSZ); - if (ctx.anslen == -1) + case DnsLookup::Type::PTR: { - th_cerr << "*** ERROR: Name resolver failure -- res_nsearch: " + IpAddress ip_addr; + size_t arpa_domain_pos = ctx.label.find(".in-addr.arpa"); + if (arpa_domain_pos != std::string::npos) + { + ip_addr.setIpFromString(ctx.label.substr(0, arpa_domain_pos)); + struct in_addr addr = ip_addr.ip4Addr(); + addr.s_addr = htonl(addr.s_addr); + ip_addr.setIp(addr); + } + else + { + ip_addr.setIpFromString(ctx.label); + } + if (!ip_addr.isEmpty()) + { + struct sockaddr_in in_addr = {0}; + in_addr.sin_family = AF_INET; + in_addr.sin_addr = ip_addr.ip4Addr(); + int ret = getnameinfo(reinterpret_cast(&in_addr), + sizeof(in_addr), + ctx.host, sizeof(ctx.host), + NULL, 0, NI_NAMEREQD); + if (ret != 0) + { + th_cerr << "*** WARNING[getnameinfo]: Could not look up IP \"" + << ctx.label << "\": " << gai_strerror(ret) << std::endl; + } + } + else + { + th_cerr << "*** WARNING: Failed to parse PTR label \"" + << ctx.label << "\"" << std::endl; + } + break; + } + case DnsLookup::Type::CNAME: + qtype = ns_t_cname; + break; + case DnsLookup::Type::SRV: + qtype = ns_t_srv; + break; + default: + assert(0); + } + + if (qtype != 0) + { + struct __res_state state; + int ret = res_ninit(&state); + if (ret != -1) + { + state.options = RES_DEFAULT; + const char *dname = ctx.label.c_str(); + ctx.anslen = res_nsearch(&state, dname, ns_c_in, qtype, + ctx.answer, NS_PACKETSZ); + if (ctx.anslen == -1) + { + th_cerr << "*** ERROR: Name resolver failure -- res_nsearch: " + << hstrerror(h_errno) << std::endl; + } + + // FIXME: Valgrind complain about leaked memory in the resolver library + // when a lookup fails. It seems to be a one time leak though so it + // does not grow with every failed lookup. But even so, it seems + // that res_close is not cleaning up properly. + // Glibc 2.33-18 on Fedora 34. + res_nclose(&state); + } + else + { + th_cerr << "*** ERROR: Name resolver failure -- res_ninit: " << hstrerror(h_errno) << std::endl; } - - // FIXME: Valgrind complain about leaked memory in the resolver library - // when a lookup fails. It seems to be a one time leak though so it - // does not grow with every failed lookup. But even so, it seems - // that res_close is not cleaning up properly. - // Glibc 2.33-18 on Fedora 34. - res_nclose(&state); - } - else - { - th_cerr << "*** ERROR: Name resolver failure -- res_ninit: " - << hstrerror(h_errno) << std::endl; } close(ctx.notifier_wr); @@ -329,137 +379,173 @@ void CppDnsLookupWorker::notificationReceived(FdWatch *w) setLookupFailed(); } - if (ctx.anslen == -1) + if (ctx.type == DnsResourceRecord::Type::A) { - workerDone(); - return; + if (ctx.addrinfo != nullptr) + { + struct addrinfo *entry; + std::vector the_addresses; + for (entry = ctx.addrinfo; entry != 0; entry = entry->ai_next) + { + IpAddress ip_addr( + reinterpret_cast(entry->ai_addr)->sin_addr); + //std::cout << "### ai_family=" << entry->ai_family + // << " ai_socktype=" << entry->ai_socktype + // << " ai_protocol=" << entry->ai_protocol + // << " ip=" << ip_addr << std::endl; + if (find(the_addresses.begin(), the_addresses.end(), ip_addr) == + the_addresses.end()) + { + the_addresses.push_back(ip_addr); + addResourceRecord( + new DnsResourceRecordA(ctx.label, 0, ip_addr)); + } + } + freeaddrinfo(ctx.addrinfo); + } } + else if (ctx.type == DnsResourceRecord::Type::PTR) + { + if (ctx.host[0] != '\0') + { + addResourceRecord( + new DnsResourceRecordPTR(ctx.label, 0, ctx.host)); + } + } + else + { + if (ctx.anslen == -1) + { + workerDone(); + return; + } - char errbuf[256]; - ns_msg msg; - int ret = ns_initparse(ctx.answer, ctx.anslen, &msg); - if (ret == -1) - { - strerror_r(ret, errbuf, sizeof(errbuf)); - std::cerr << "*** WARNING: ns_initparse failed: " << errbuf << std::endl; - setLookupFailed(); - workerDone(); - return; - } - - uint16_t msg_cnt = ns_msg_count(msg, ns_s_an); - if (msg_cnt == 0) - { - setLookupFailed(); - } - for (uint16_t rrnum=0; rrnum #include #include +#include /**************************************************************************** @@ -165,6 +166,8 @@ class CppDnsLookupWorker : public DnsLookupWorker, public sigc::trackable int notifier_wr = -1; unsigned char answer[NS_PACKETSZ]; int anslen = 0; + struct addrinfo* addrinfo = nullptr; + char host[NI_MAXHOST] = {0}; std::ostringstream thread_cerr; }; diff --git a/src/async/demo/AsyncDnsLookup_demo.cpp b/src/async/demo/AsyncDnsLookup_demo.cpp index 4a7360d4..08638cd8 100644 --- a/src/async/demo/AsyncDnsLookup_demo.cpp +++ b/src/async/demo/AsyncDnsLookup_demo.cpp @@ -2,8 +2,6 @@ #include #include -#include - using namespace Async; class MyClass : public sigc::trackable @@ -11,123 +9,111 @@ class MyClass : public sigc::trackable public: MyClass(void) { - dns.resultsReady.connect(sigc::bind(mem_fun(*this, &MyClass::onResultsReady), 1)); - std::cout << "Starting DNS query..." << std::endl; - //dns.lookup("www.svxlink.org"); - //dns.lookup("185.199.110.153"); - //dns.lookup("153.110.199.185.in-addr.arpa.", - // DnsLookup::Type::PTR); - std::string srv = "_svxreflector._tcp.test.svxlink.org"; - dns.addStaticResourceRecord( - new DnsResourceRecordSRV(srv, 3600, 15, 10, 5304, "localhost.")); - dns.lookup(srv, DnsLookup::Type::SRV); + dns.resultsReady.connect(mem_fun(*this, &MyClass::onResultsReady)); - //dns2 = std::move(dns); - //dns2.resultsReady.connect(sigc::bind(mem_fun(*this, &MyClass::onResultsReady), 2)); + dns.lookup("www.svxlink.org"); + //dns.lookup("www.svxlink.org", DnsLookup::Type::CNAME); + //dns.lookup("185.199.110.153"); + //dns.lookup("185.199.110.153", DnsLookup::Type::PTR); + //dns.lookup("153.110.199.185.in-addr.arpa.", DnsLookup::Type::PTR); + + //std::string srv = "_svxreflector._tcp.test.svxlink.org"; + //dns.addStaticResourceRecord( + // new DnsResourceRecordSRV(srv, 3600, 15, 10, 5304, "localhost.")); + //dns.lookup(srv, DnsLookup::Type::SRV); + + std::cout << "Starting " << dns.typeStr() << " record query for \"" + << dns.label() << "\"..." << std::endl; } - void onResultsReady(DnsLookup& dns, int id) + void onResultsReady(DnsLookup& dns) { - std::cout << "### MyClass::onResultsReady: id=" << id << std::endl; + if (!dns.lookupFailed()) + { + // Simple IP address lookup API + std::cout << "IP addresses received:\n"; + for (auto& addr : dns.addresses()) + { + std::cout << addr << std::endl; + } + std::cout << std::endl; - if (dns.empty()) + // Access to all resource records + std::cout << "All resource records received:\n"; + for (auto& rr : dns.resourceRecords()) + { + std::cout << rr->toString() << std::endl; + } + std::cout << std::endl; + + // Access A records with full detail + DnsResourceRecordA::List a_rrs; + dns.resourceRecords(a_rrs); + if (!a_rrs.empty()) + { + std::cout << "A records received:\n"; + for (auto& rr : a_rrs) + { + std::cout << rr->name() << "\t" << rr->ttl() << "\t" << rr->ip() + << std::endl; + } + std::cout << std::endl; + } + + // Access PTR records with full detail + DnsResourceRecordPTR::List ptr_rrs; + dns.resourceRecords(ptr_rrs); + if (!ptr_rrs.empty()) + { + std::cout << "PTR records received:\n"; + for (auto& rr : ptr_rrs) + { + std::cout << rr->name() << "\t" << rr->ttl() << "\t" << rr->dname() + << std::endl; + } + std::cout << std::endl; + } + + // Access CNAME records with full detail + DnsResourceRecordCNAME::List cname_rrs; + dns.resourceRecords(cname_rrs); + if (!cname_rrs.empty()) + { + std::cout << "CNAME records received:\n"; + for (auto& rr : cname_rrs) + { + std::cout << rr->name() << "\t" << rr->ttl() << "\t" << rr->cname() + << std::endl; + } + std::cout << std::endl; + } + + // Access SRV records with full detail + DnsResourceRecordSRV::List srv_rrs; + dns.resourceRecords(srv_rrs); + if (!srv_rrs.empty()) + { + std::cout << "SRV records received:\n"; + for (auto& rr : srv_rrs) + { + std::cout << rr->name() << "\t" << rr->ttl() << "\t" << rr->prio() + << " " << rr->weight() << " " << rr->port() << " " + << rr->target() << std::endl; + } + } + } + else { std::cout << "*** ERROR: The " << dns.typeStr() << " record DNS lookup for " << dns.label() << " failed" << std::endl; - Application::app().quit(); - return; } - // Simple IP address lookup API - std::cout << "IP addresses received:\n"; - for (auto& addr : dns.addresses()) - { - std::cout << addr << std::endl; - } - std::cout << std::endl; - - // Access to all resource records - std::cout << "All resource records received:\n"; - for (auto& rr : dns.resourceRecords()) - { - std::cout << rr->toString() << std::endl; - } - std::cout << std::endl; - - // Access A records with full detail - DnsResourceRecordA::List a_rrs; - dns.resourceRecords(a_rrs); - if (!a_rrs.empty()) - { - std::cout << "A records received:\n"; - for (auto& rr : a_rrs) - { - std::cout << rr->name() << "\t" << rr->ttl() << "\t" << rr->ip() - << std::endl; - } - std::cout << std::endl; - } - - // Access PTR records with full detail - DnsResourceRecordPTR::List ptr_rrs; - dns.resourceRecords(ptr_rrs); - if (!ptr_rrs.empty()) - { - std::cout << "PTR records received:\n"; - for (auto& rr : ptr_rrs) - { - std::cout << rr->name() << "\t" << rr->ttl() << "\t" << rr->dname() - << std::endl; - } - std::cout << std::endl; - } - - // Access CNAME records with full detail - DnsResourceRecordCNAME::List cname_rrs; - dns.resourceRecords(cname_rrs); - if (!cname_rrs.empty()) - { - std::cout << "CNAME records received:\n"; - for (auto& rr : cname_rrs) - { - std::cout << rr->name() << "\t" << rr->ttl() << "\t" << rr->cname() - << std::endl; - } - std::cout << std::endl; - } - - // Access SRV records with full detail - DnsResourceRecordSRV::List srv_rrs; - dns.resourceRecords(srv_rrs); - if (!srv_rrs.empty()) - { - std::cout << "SRV records received:\n"; - for (auto& rr : srv_rrs) - { - std::cout << rr->name() << "\t" << rr->ttl() << "\t" << rr->prio() - << " " << rr->weight() << " " << rr->port() << " " - << rr->target() << std::endl; - } - } - - static int cnt = 0; - if (++cnt == 10) - { - Application::app().quit(); - } - else - { - sleep(1); - std::cout << "\nStarting next lookup" << std::endl; - //dns.clear(); - dns.lookup(); - } + Application::app().quit(); } private: DnsLookup dns; - DnsLookup dns2; }; int main(int argc, char **argv) diff --git a/src/versions b/src/versions index 4ff0c25f..fa1c749c 100644 --- a/src/versions +++ b/src/versions @@ -8,10 +8,10 @@ QTEL=1.2.4.99.5 LIBECHOLIB=1.3.3.99.1 # Version for the Async library -LIBASYNC=1.6.99.18 +LIBASYNC=1.6.99.19 # SvxLink versions -SVXLINK=1.7.99.63 +SVXLINK=1.7.99.64 MODULE_HELP=1.0.0 MODULE_PARROT=1.1.1 MODULE_ECHO_LINK=1.5.99.2