aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLee Clagett <code@leeclagett.com>2020-12-06 16:59:08 -0500
committerLee Clagett <code@leeclagett.com>2020-10-07 15:43:31 +0000
commit386ef03be3c1e8b183a70b2fc7048fd6e8ca2f79 (patch)
tree04389a43b151da780af6685ab4d3894fdf060122 /src
parentMerge pull request #7068 (diff)
downloadmonero-386ef03be3c1e8b183a70b2fc7048fd6e8ca2f79.tar.xz
Add TLSA support to DNSSEC fetching
Diffstat (limited to 'src')
-rw-r--r--src/common/dns_utils.cpp29
-rw-r--r--src/common/dns_utils.h21
-rw-r--r--src/net/CMakeLists.txt8
-rw-r--r--src/net/error.cpp7
-rw-r--r--src/net/error.h7
-rw-r--r--src/net/resolve.cpp71
-rw-r--r--src/net/resolve.h47
7 files changed, 178 insertions, 12 deletions
diff --git a/src/common/dns_utils.cpp b/src/common/dns_utils.cpp
index 4f4efcd81..b741889ef 100644
--- a/src/common/dns_utils.cpp
+++ b/src/common/dns_utils.cpp
@@ -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);
diff --git a/src/common/dns_utils.h b/src/common/dns_utils.h
index 30c4cced2..99e91bc54 100644
--- a/src/common/dns_utils.h
+++ b/src/common/dns_utils.h
@@ -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;
@@ -106,6 +108,17 @@ public:
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
*
* If the address looks good, but contains one @ symbol, replace that with a .
diff --git a/src/net/CMakeLists.txt b/src/net/CMakeLists.txt
index afcd42ef7..e93e27bcd 100644
--- a/src/net/CMakeLists.txt
+++ b/src/net/CMakeLists.txt
@@ -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})
diff --git a/src/net/error.cpp b/src/net/error.cpp
index 037f44d52..d2e713bc5 100644
--- a/src/net/error.cpp
+++ b/src/net/error.cpp
@@ -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:
diff --git a/src/net/error.h b/src/net/error.h
index 7c852dd20..746eb0ecb 100644
--- a/src/net/error.h
+++ b/src/net/error.h
@@ -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.
diff --git a/src/net/resolve.cpp b/src/net/resolve.cpp
new file mode 100644
index 000000000..1b43cf6c4
--- /dev/null
+++ b/src/net/resolve.cpp
@@ -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
diff --git a/src/net/resolve.h b/src/net/resolve.h
new file mode 100644
index 000000000..46bd8e617
--- /dev/null
+++ b/src/net/resolve.h
@@ -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