Add TLSA support to DNSSEC fetching
This commit is contained in:
parent
7ca4ef0d74
commit
386ef03be3
|
@ -37,6 +37,7 @@
|
|||
#include <boost/thread/mutex.hpp>
|
||||
#include <boost/algorithm/string/join.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/utility/string_ref.hpp>
|
||||
using namespace epee;
|
||||
|
||||
#undef MONERO_DEFAULT_LOG_CATEGORY
|
||||
|
@ -124,6 +125,7 @@ static const char *get_record_name(int record_type)
|
|||
case DNS_TYPE_A: return "A";
|
||||
case DNS_TYPE_TXT: return "TXT";
|
||||
case DNS_TYPE_AAAA: return "AAAA";
|
||||
case DNS_TYPE_TLSA: return "TLSA";
|
||||
default: return "unknown";
|
||||
}
|
||||
}
|
||||
|
@ -186,6 +188,13 @@ boost::optional<std::string> txt_to_string(const char* src, size_t len)
|
|||
return std::string(src+1, len-1);
|
||||
}
|
||||
|
||||
boost::optional<std::string> tlsa_to_string(const char* src, size_t len)
|
||||
{
|
||||
if (len < 4)
|
||||
return boost::none;
|
||||
return std::string(src, len);
|
||||
}
|
||||
|
||||
// custom smart pointer.
|
||||
// TODO: see if std::auto_ptr and the like support custom destructors
|
||||
template<typename type, void (*freefunc)(type*)>
|
||||
|
@ -326,11 +335,15 @@ std::vector<std::string> DNSResolver::get_record(const std::string& url, int rec
|
|||
// destructor takes care of cleanup
|
||||
ub_result_ptr result;
|
||||
|
||||
MDEBUG("Performing DNSSEC " << get_record_name(record_type) << " record query for " << url);
|
||||
|
||||
// call DNS resolver, blocking. if return value not zero, something went wrong
|
||||
if (!ub_resolve(m_data->m_ub_context, string_copy(url.c_str()), record_type, DNS_CLASS_IN, &result))
|
||||
{
|
||||
dnssec_available = (result->secure || result->bogus);
|
||||
dnssec_valid = result->secure && !result->bogus;
|
||||
if (dnssec_available && !dnssec_valid)
|
||||
MWARNING("Invalid DNSSEC " << get_record_name(record_type) << " record signature for " << url << ": " << result->why_bogus);
|
||||
if (result->havedata)
|
||||
{
|
||||
for (size_t i=0; result->data[i] != NULL; i++)
|
||||
|
@ -338,8 +351,9 @@ std::vector<std::string> DNSResolver::get_record(const std::string& url, int rec
|
|||
boost::optional<std::string> res = (*reader)(result->data[i], result->len[i]);
|
||||
if (res)
|
||||
{
|
||||
MINFO("Found \"" << *res << "\" in " << get_record_name(record_type) << " record for " << url);
|
||||
addresses.push_back(*res);
|
||||
// do not dump dns record directly from dns into log
|
||||
MINFO("Found " << get_record_name(record_type) << " record for " << url);
|
||||
addresses.push_back(std::move(*res));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -363,6 +377,17 @@ std::vector<std::string> DNSResolver::get_txt_record(const std::string& url, boo
|
|||
return get_record(url, DNS_TYPE_TXT, txt_to_string, dnssec_available, dnssec_valid);
|
||||
}
|
||||
|
||||
std::vector<std::string> DNSResolver::get_tlsa_tcp_record(const boost::string_ref url, const boost::string_ref port, bool& dnssec_available, bool& dnssec_valid)
|
||||
{
|
||||
std::string service_addr;
|
||||
service_addr.reserve(url.size() + port.size() + 7);
|
||||
service_addr.push_back('_');
|
||||
service_addr.append(port.data(), port.size());
|
||||
service_addr.append("._tcp.");
|
||||
service_addr.append(url.data(), url.size());
|
||||
return get_record(service_addr, DNS_TYPE_TLSA, tlsa_to_string, dnssec_available, dnssec_valid);
|
||||
}
|
||||
|
||||
std::string DNSResolver::get_dns_format_from_oa_address(const std::string& oa_addr)
|
||||
{
|
||||
std::string addr(oa_addr);
|
||||
|
|
|
@ -31,15 +31,17 @@
|
|||
#include <string>
|
||||
#include <functional>
|
||||
#include <boost/optional/optional_fwd.hpp>
|
||||
#include <boost/utility/string_ref_fwd.hpp>
|
||||
|
||||
namespace tools
|
||||
{
|
||||
|
||||
// RFC defines for record types and classes for DNS, gleaned from ldns source
|
||||
const static int DNS_CLASS_IN = 1;
|
||||
const static int DNS_TYPE_A = 1;
|
||||
const static int DNS_TYPE_TXT = 16;
|
||||
const static int DNS_TYPE_AAAA = 8;
|
||||
constexpr const int DNS_CLASS_IN = 1;
|
||||
constexpr const int DNS_TYPE_A = 1;
|
||||
constexpr const int DNS_TYPE_TXT = 16;
|
||||
constexpr const int DNS_TYPE_AAAA = 8;
|
||||
constexpr const int DNS_TYPE_TLSA = 52;
|
||||
|
||||
struct DNSResolverData;
|
||||
|
||||
|
@ -105,6 +107,17 @@ public:
|
|||
// TODO: modify this to accommodate DNSSEC
|
||||
std::vector<std::string> get_txt_record(const std::string& url, bool& dnssec_available, bool& dnssec_valid);
|
||||
|
||||
/**
|
||||
* @brief gets all TLSA TCP records from a DNS query for the supplied URL;
|
||||
* if no TLSA record present returns an empty vector.
|
||||
*
|
||||
* @param url A string containing a URL to query for
|
||||
* @param port The service port number (as string) to query
|
||||
*
|
||||
* @return A vector of strings containing all TLSA records; or an empty vector
|
||||
*/
|
||||
std::vector<std::string> get_tlsa_tcp_record(boost::string_ref url, boost::string_ref port, bool& dnssec_available, bool& dnssec_valid);
|
||||
|
||||
/**
|
||||
* @brief Gets a DNS address from OpenAlias format
|
||||
*
|
||||
|
|
|
@ -26,10 +26,10 @@
|
|||
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
set(net_sources dandelionpp.cpp error.cpp http.cpp i2p_address.cpp parse.cpp socks.cpp
|
||||
socks_connect.cpp tor_address.cpp zmq.cpp)
|
||||
set(net_headers dandelionpp.h error.h http.cpp i2p_address.h parse.h socks.h socks_connect.h
|
||||
tor_address.h zmq.h)
|
||||
set(net_sources dandelionpp.cpp error.cpp http.cpp i2p_address.cpp parse.cpp resolve.cpp
|
||||
socks.cpp socks_connect.cpp tor_address.cpp zmq.cpp)
|
||||
set(net_headers dandelionpp.h error.h http.cpp i2p_address.h parse.h socks.h resolve.h
|
||||
socks_connect.h tor_address.h zmq.h)
|
||||
|
||||
monero_add_library(net ${net_sources} ${net_headers})
|
||||
target_link_libraries(net common epee ${ZMQ_LIB} ${Boost_ASIO_LIBRARY})
|
||||
|
|
|
@ -47,12 +47,18 @@ namespace
|
|||
{
|
||||
switch (net::error(value))
|
||||
{
|
||||
case net::error::bogus_dnssec:
|
||||
return "Invalid response signature from DNSSEC enabled domain";
|
||||
case net::error::dns_query_failure:
|
||||
return "Failed to retrieve desired DNS record";
|
||||
case net::error::expected_tld:
|
||||
return "Expected top-level domain";
|
||||
case net::error::invalid_host:
|
||||
return "Host value is not valid";
|
||||
case net::error::invalid_i2p_address:
|
||||
return "Invalid I2P address";
|
||||
case net::error::invalid_mask:
|
||||
return "CIDR netmask outside of 0-32 range";
|
||||
case net::error::invalid_port:
|
||||
return "Invalid port value (expected 0-65535)";
|
||||
case net::error::invalid_tor_address:
|
||||
|
@ -71,6 +77,7 @@ namespace
|
|||
switch (net::error(value))
|
||||
{
|
||||
case net::error::invalid_port:
|
||||
case net::error::invalid_mask:
|
||||
return std::errc::result_out_of_range;
|
||||
case net::error::expected_tld:
|
||||
case net::error::invalid_tor_address:
|
||||
|
|
|
@ -37,13 +37,16 @@ namespace net
|
|||
enum class error : int
|
||||
{
|
||||
// 0 reserved for success (as per expect<T>)
|
||||
expected_tld = 1, //!< Expected a tld
|
||||
bogus_dnssec = 1, //!< Invalid response signature from DNSSEC enabled domain
|
||||
dns_query_failure, //!< Failed to retrieve desired DNS record
|
||||
expected_tld, //!< Expected a tld
|
||||
invalid_host, //!< Hostname is not valid
|
||||
invalid_i2p_address,
|
||||
invalid_mask, //!< Outside of 0-32 range
|
||||
invalid_port, //!< Outside of 0-65535 range
|
||||
invalid_tor_address,//!< Invalid base32 or length
|
||||
unsupported_address,//!< Type not supported by `get_network_address`
|
||||
invalid_mask, //!< Outside of 0-32 range
|
||||
|
||||
};
|
||||
|
||||
//! \return `std::error_category` for `net` namespace.
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
// Copyright (c) 2020, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "net/resolve.h"
|
||||
|
||||
#include <boost/utility/string_ref.hpp>
|
||||
#include "common/dns_utils.h"
|
||||
#include "common/expect.h"
|
||||
#include "net/error.h"
|
||||
|
||||
namespace net
|
||||
{
|
||||
namespace dnssec
|
||||
{
|
||||
expect<service_response> resolve_hostname(const std::string& addr, const std::string& tlsa_port)
|
||||
{
|
||||
// use basic (blocking) unbound for now, possibly refactor later
|
||||
tools::DNSResolver& resolver = tools::DNSResolver::instance();
|
||||
|
||||
bool dnssec_available = false;
|
||||
bool dnssec_valid = false;
|
||||
std::vector<std::string> ip_records = resolver.get_ipv4(addr, dnssec_available, dnssec_valid);
|
||||
|
||||
if (dnssec_available && !dnssec_valid)
|
||||
return {net::error::bogus_dnssec};
|
||||
|
||||
if (ip_records.empty())
|
||||
{
|
||||
ip_records = resolver.get_ipv6(addr, dnssec_available, dnssec_valid);
|
||||
if (dnssec_available && !dnssec_valid)
|
||||
return {net::error::bogus_dnssec};
|
||||
if (ip_records.empty())
|
||||
return {net::error::dns_query_failure};
|
||||
}
|
||||
|
||||
std::vector<std::string> tlsa{};
|
||||
if (dnssec_available && !tlsa_port.empty())
|
||||
{
|
||||
tlsa = resolver.get_tlsa_tcp_record(addr, tlsa_port, dnssec_available, dnssec_valid);
|
||||
if (!dnssec_valid)
|
||||
return {net::error::bogus_dnssec};
|
||||
}
|
||||
return {{std::move(ip_records), std::move(tlsa)}};
|
||||
}
|
||||
} // dnssec
|
||||
} // net
|
|
@ -0,0 +1,47 @@
|
|||
// Copyright (c) 2020, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
template<typename> class expect;
|
||||
|
||||
namespace net
|
||||
{
|
||||
namespace dnssec
|
||||
{
|
||||
struct service_response
|
||||
{
|
||||
std::vector<std::string> ip; //!< IPv4/6 records in dotted or semicolon notation
|
||||
std::vector<std::string> tlsa; //!< DANE/TLSA records
|
||||
};
|
||||
|
||||
//! \return IP + (optionally) DANE/TLSA records, failing if DNSSEC signature is "bogus"
|
||||
expect<service_response> resolve_hostname(const std::string& addr, const std::string& tlsa_port = {});
|
||||
} // dnssec
|
||||
} // net
|
Loading…
Reference in New Issue