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/thread/mutex.hpp>
|
||||||
#include <boost/algorithm/string/join.hpp>
|
#include <boost/algorithm/string/join.hpp>
|
||||||
#include <boost/optional.hpp>
|
#include <boost/optional.hpp>
|
||||||
|
#include <boost/utility/string_ref.hpp>
|
||||||
using namespace epee;
|
using namespace epee;
|
||||||
|
|
||||||
#undef MONERO_DEFAULT_LOG_CATEGORY
|
#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_A: return "A";
|
||||||
case DNS_TYPE_TXT: return "TXT";
|
case DNS_TYPE_TXT: return "TXT";
|
||||||
case DNS_TYPE_AAAA: return "AAAA";
|
case DNS_TYPE_AAAA: return "AAAA";
|
||||||
|
case DNS_TYPE_TLSA: return "TLSA";
|
||||||
default: return "unknown";
|
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);
|
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.
|
// custom smart pointer.
|
||||||
// TODO: see if std::auto_ptr and the like support custom destructors
|
// TODO: see if std::auto_ptr and the like support custom destructors
|
||||||
template<typename type, void (*freefunc)(type*)>
|
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
|
// destructor takes care of cleanup
|
||||||
ub_result_ptr result;
|
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
|
// 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))
|
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_available = (result->secure || result->bogus);
|
||||||
dnssec_valid = 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)
|
if (result->havedata)
|
||||||
{
|
{
|
||||||
for (size_t i=0; result->data[i] != NULL; i++)
|
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]);
|
boost::optional<std::string> res = (*reader)(result->data[i], result->len[i]);
|
||||||
if (res)
|
if (res)
|
||||||
{
|
{
|
||||||
MINFO("Found \"" << *res << "\" in " << get_record_name(record_type) << " record for " << url);
|
// do not dump dns record directly from dns into log
|
||||||
addresses.push_back(*res);
|
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);
|
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 DNSResolver::get_dns_format_from_oa_address(const std::string& oa_addr)
|
||||||
{
|
{
|
||||||
std::string addr(oa_addr);
|
std::string addr(oa_addr);
|
||||||
|
|
|
@ -31,15 +31,17 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <boost/optional/optional_fwd.hpp>
|
#include <boost/optional/optional_fwd.hpp>
|
||||||
|
#include <boost/utility/string_ref_fwd.hpp>
|
||||||
|
|
||||||
namespace tools
|
namespace tools
|
||||||
{
|
{
|
||||||
|
|
||||||
// RFC defines for record types and classes for DNS, gleaned from ldns source
|
// RFC defines for record types and classes for DNS, gleaned from ldns source
|
||||||
const static int DNS_CLASS_IN = 1;
|
constexpr const int DNS_CLASS_IN = 1;
|
||||||
const static int DNS_TYPE_A = 1;
|
constexpr const int DNS_TYPE_A = 1;
|
||||||
const static int DNS_TYPE_TXT = 16;
|
constexpr const int DNS_TYPE_TXT = 16;
|
||||||
const static int DNS_TYPE_AAAA = 8;
|
constexpr const int DNS_TYPE_AAAA = 8;
|
||||||
|
constexpr const int DNS_TYPE_TLSA = 52;
|
||||||
|
|
||||||
struct DNSResolverData;
|
struct DNSResolverData;
|
||||||
|
|
||||||
|
@ -105,6 +107,17 @@ public:
|
||||||
// TODO: modify this to accommodate DNSSEC
|
// TODO: modify this to accommodate DNSSEC
|
||||||
std::vector<std::string> get_txt_record(const std::string& url, bool& dnssec_available, bool& dnssec_valid);
|
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
|
* @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
|
# 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.
|
# 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
|
set(net_sources dandelionpp.cpp error.cpp http.cpp i2p_address.cpp parse.cpp resolve.cpp
|
||||||
socks_connect.cpp tor_address.cpp zmq.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
|
set(net_headers dandelionpp.h error.h http.cpp i2p_address.h parse.h socks.h resolve.h
|
||||||
tor_address.h zmq.h)
|
socks_connect.h tor_address.h zmq.h)
|
||||||
|
|
||||||
monero_add_library(net ${net_sources} ${net_headers})
|
monero_add_library(net ${net_sources} ${net_headers})
|
||||||
target_link_libraries(net common epee ${ZMQ_LIB} ${Boost_ASIO_LIBRARY})
|
target_link_libraries(net common epee ${ZMQ_LIB} ${Boost_ASIO_LIBRARY})
|
||||||
|
|
|
@ -47,12 +47,18 @@ namespace
|
||||||
{
|
{
|
||||||
switch (net::error(value))
|
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:
|
case net::error::expected_tld:
|
||||||
return "Expected top-level domain";
|
return "Expected top-level domain";
|
||||||
case net::error::invalid_host:
|
case net::error::invalid_host:
|
||||||
return "Host value is not valid";
|
return "Host value is not valid";
|
||||||
case net::error::invalid_i2p_address:
|
case net::error::invalid_i2p_address:
|
||||||
return "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:
|
case net::error::invalid_port:
|
||||||
return "Invalid port value (expected 0-65535)";
|
return "Invalid port value (expected 0-65535)";
|
||||||
case net::error::invalid_tor_address:
|
case net::error::invalid_tor_address:
|
||||||
|
@ -71,6 +77,7 @@ namespace
|
||||||
switch (net::error(value))
|
switch (net::error(value))
|
||||||
{
|
{
|
||||||
case net::error::invalid_port:
|
case net::error::invalid_port:
|
||||||
|
case net::error::invalid_mask:
|
||||||
return std::errc::result_out_of_range;
|
return std::errc::result_out_of_range;
|
||||||
case net::error::expected_tld:
|
case net::error::expected_tld:
|
||||||
case net::error::invalid_tor_address:
|
case net::error::invalid_tor_address:
|
||||||
|
|
|
@ -37,13 +37,16 @@ namespace net
|
||||||
enum class error : int
|
enum class error : int
|
||||||
{
|
{
|
||||||
// 0 reserved for success (as per expect<T>)
|
// 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_host, //!< Hostname is not valid
|
||||||
invalid_i2p_address,
|
invalid_i2p_address,
|
||||||
|
invalid_mask, //!< Outside of 0-32 range
|
||||||
invalid_port, //!< Outside of 0-65535 range
|
invalid_port, //!< Outside of 0-65535 range
|
||||||
invalid_tor_address,//!< Invalid base32 or length
|
invalid_tor_address,//!< Invalid base32 or length
|
||||||
unsupported_address,//!< Type not supported by `get_network_address`
|
unsupported_address,//!< Type not supported by `get_network_address`
|
||||||
invalid_mask, //!< Outside of 0-32 range
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//! \return `std::error_category` for `net` namespace.
|
//! \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