aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/build.yml1
-rw-r--r--CMakeLists.txt4
-rw-r--r--README.md4
-rw-r--r--contrib/epee/include/net/abstract_http_client.h1
-rw-r--r--contrib/epee/include/net/http_base.h12
-rw-r--r--contrib/epee/include/net/http_client.h52
-rw-r--r--contrib/epee/src/CMakeLists.txt1
-rw-r--r--contrib/epee/src/http_base.cpp71
-rw-r--r--src/common/dns_utils.cpp15
-rw-r--r--src/common/dns_utils.h9
-rw-r--r--src/net/parse.cpp8
-rw-r--r--src/net/parse.h10
-rw-r--r--src/p2p/net_node.inl18
-rw-r--r--tests/unit_tests/dns_resolver.cpp11
-rw-r--r--tests/unit_tests/net.cpp35
-rw-r--r--utils/gpg_keys/moneromooo.asc26
16 files changed, 86 insertions, 192 deletions
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 7cdd2d4e2..1f8e39903 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -158,6 +158,7 @@ jobs:
- name: tests
env:
CTEST_OUTPUT_ON_FAILURE: ON
+ DNS_PUBLIC: tcp://9.9.9.9
run: |
${{env.CCACHE_SETTINGS}}
${{env.BUILD_DEFAULT_LINUX}}
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c0846280c..45bc12712 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1136,7 +1136,9 @@ if (HIDAPI_FOUND OR LibUSB_COMPILE_TEST_PASSED)
endif()
endif()
-option(USE_READLINE "Build with GNU readline support." ON)
+if(NOT OPENBSD)
+ option(USE_READLINE "Build with GNU readline support." ON)
+endif()
if(USE_READLINE AND NOT DEPENDS)
find_package(Readline)
if(READLINE_FOUND AND GNU_READLINE_FOUND)
diff --git a/README.md b/README.md
index 0249c084a..c5b22b07e 100644
--- a/README.md
+++ b/README.md
@@ -511,9 +511,9 @@ Monero is also available as a port or package as `monero-cli`.
You will need to add a few packages to your system. `pkg_add cmake gmake zeromq libiconv boost`.
The `doxygen` and `graphviz` packages are optional and require the xbase set.
-Running the test suite also requires `py-requests` package.
+Running the test suite also requires `py3-requests` package.
-Build monero: `env DEVELOPER_LOCAL_TOOLS=1 BOOST_ROOT=/usr/local gmake release-static`
+Build monero: `gmake`
Note: you may encounter the following error when compiling the latest version of Monero as a normal user:
diff --git a/contrib/epee/include/net/abstract_http_client.h b/contrib/epee/include/net/abstract_http_client.h
index 5270824e1..46b3747cd 100644
--- a/contrib/epee/include/net/abstract_http_client.h
+++ b/contrib/epee/include/net/abstract_http_client.h
@@ -72,7 +72,6 @@ namespace http
virtual bool is_connected(bool *ssl = NULL) = 0;
virtual bool invoke(const boost::string_ref uri, const boost::string_ref method, const boost::string_ref body, std::chrono::milliseconds timeout, const http_response_info** ppresponse_info = NULL, const fields_list& additional_params = fields_list()) = 0;
virtual bool invoke_get(const boost::string_ref uri, std::chrono::milliseconds timeout, const std::string& body = std::string(), const http_response_info** ppresponse_info = NULL, const fields_list& additional_params = fields_list()) = 0;
- virtual bool invoke_post(const boost::string_ref uri, const std::string& body, std::chrono::milliseconds timeout, const http_response_info** ppresponse_info = NULL, const fields_list& additional_params = fields_list()) = 0;
virtual uint64_t get_bytes_sent() const = 0;
virtual uint64_t get_bytes_received() const = 0;
};
diff --git a/contrib/epee/include/net/http_base.h b/contrib/epee/include/net/http_base.h
index 4af4da790..15fd30bf3 100644
--- a/contrib/epee/include/net/http_base.h
+++ b/contrib/epee/include/net/http_base.h
@@ -55,20 +55,8 @@ namespace net_utils
http_method_unknown
};
- enum http_content_type
- {
- http_content_type_text_html,
- http_content_type_image_gif,
- http_content_type_other,
- http_content_type_not_set
- };
-
typedef std::list<std::pair<std::string, std::string> > fields_list;
- std::string get_value_from_fields_list(const std::string& param_name, const net_utils::http::fields_list& fields);
-
- std::string get_value_from_uri_line(const std::string& param_name, const std::string& uri);
-
static inline void add_field(std::string& out, const boost::string_ref name, const boost::string_ref value)
{
out.append(name.data(), name.size()).append(": ");
diff --git a/contrib/epee/include/net/http_client.h b/contrib/epee/include/net/http_client.h
index 9ce30b620..8cee399cb 100644
--- a/contrib/epee/include/net/http_client.h
+++ b/contrib/epee/include/net/http_client.h
@@ -32,7 +32,6 @@
#include <boost/regex.hpp>
#include <boost/optional/optional.hpp>
#include <boost/utility/string_ref.hpp>
-//#include <mbstring.h>
#include <algorithm>
#include <cctype>
#include <functional>
@@ -48,57 +47,13 @@
#include "net_parse_helpers.h"
#include "syncobj.h"
-//#include "shlwapi.h"
-
-//#pragma comment(lib, "shlwapi.lib")
-
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "net.http"
-extern epee::critical_section gregexp_lock;
-
-
namespace epee
{
namespace net_utils
{
-
- /*struct url
- {
- public:
- void parse(const std::string& url_s)
- {
- const string prot_end("://");
- string::const_iterator prot_i = search(url_s.begin(), url_s.end(),
- prot_end.begin(), prot_end.end());
- protocol_.reserve(distance(url_s.begin(), prot_i));
- transform(url_s.begin(), prot_i,
- back_inserter(protocol_),
- ptr_fun<int,int>(tolower)); // protocol is icase
- if( prot_i == url_s.end() )
- return;
- advance(prot_i, prot_end.length());
- string::const_iterator path_i = find(prot_i, url_s.end(), '/');
- host_.reserve(distance(prot_i, path_i));
- transform(prot_i, path_i,
- back_inserter(host_),
- ptr_fun<int,int>(tolower)); // host is icase
- string::const_iterator query_i = find(path_i, url_s.end(), '?');
- path_.assign(path_i, query_i);
- if( query_i != url_s.end() )
- ++query_i;
- query_.assign(query_i, url_s.end());
- }
-
- std::string protocol_;
- std::string host_;
- std::string path_;
- std::string query_;
- };*/
-
-
-
-
//---------------------------------------------------------------------------
namespace http
{
@@ -135,7 +90,6 @@ namespace net_utils
http_response_info m_response_info;
size_t m_len_in_summary;
size_t m_len_in_remain;
- //std::string* m_ptarget_buffer;
boost::shared_ptr<i_sub_handler> m_pcontent_encoding_handler;
reciev_machine_state m_state;
chunked_state m_chunked_state;
@@ -300,12 +254,6 @@ namespace net_utils
return false;
}
//---------------------------------------------------------------------------
- inline bool invoke_post(const boost::string_ref uri, const std::string& body, std::chrono::milliseconds timeout, const http_response_info** ppresponse_info = NULL, const fields_list& additional_params = fields_list()) override
- {
- CRITICAL_REGION_LOCAL(m_lock);
- return invoke(uri, "POST", body, timeout, ppresponse_info, additional_params);
- }
- //---------------------------------------------------------------------------
bool test(const std::string &s, std::chrono::milliseconds timeout) // TEST FUNC ONLY
{
CRITICAL_REGION_LOCAL(m_lock);
diff --git a/contrib/epee/src/CMakeLists.txt b/contrib/epee/src/CMakeLists.txt
index 808b9f09e..4e920f39c 100644
--- a/contrib/epee/src/CMakeLists.txt
+++ b/contrib/epee/src/CMakeLists.txt
@@ -37,7 +37,6 @@ monero_add_library(epee byte_slice.cpp byte_stream.cpp hex.cpp abstract_http_cli
misc_language.cpp
file_io_utils.cpp
net_parse_helpers.cpp
- http_base.cpp
${EPEE_HEADERS_PUBLIC}
)
diff --git a/contrib/epee/src/http_base.cpp b/contrib/epee/src/http_base.cpp
deleted file mode 100644
index f6d7568c5..000000000
--- a/contrib/epee/src/http_base.cpp
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * 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.
-// * Neither the name of the Andrey N. Sabelnikov 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 OWNER 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/http_base.h"
-#include "memwipe.h"
-#include "string_tools.h"
-
-#include <boost/regex.hpp>
-#include <string>
-#include <utility>
-
-#undef MONERO_DEFAULT_LOG_CATEGORY
-#define MONERO_DEFAULT_LOG_CATEGORY "net.http"
-
-namespace epee
-{
-namespace net_utils
-{
-namespace http
-{
- std::string get_value_from_fields_list(const std::string& param_name, const net_utils::http::fields_list& fields)
- {
- fields_list::const_iterator it = fields.begin();
- for(; it != fields.end(); ++it)
- if(!string_tools::compare_no_case(param_name, it->first))
- break;
-
- if(it==fields.end())
- return std::string();
-
- return it->second;
- }
-
- std::string get_value_from_uri_line(const std::string& param_name, const std::string& uri)
- {
- std::string buff = "([\\?|&])";
- buff += param_name + "=([^&]*)";
- boost::regex match_param(buff.c_str(), boost::regex::icase | boost::regex::normal);
- boost::smatch result;
- if(boost::regex_search(uri, result, match_param, boost::match_default) && result[0].matched)
- {
- return result[2];
- }
- return std::string();
- }
-}
-}
-}
diff --git a/src/common/dns_utils.cpp b/src/common/dns_utils.cpp
index e00421f87..aceefcf36 100644
--- a/src/common/dns_utils.cpp
+++ b/src/common/dns_utils.cpp
@@ -326,11 +326,6 @@ std::vector<std::string> DNSResolver::get_record(const std::string& url, int rec
dnssec_available = false;
dnssec_valid = false;
- if (!check_address_syntax(url.c_str()))
- {
- return addresses;
- }
-
// destructor takes care of cleanup
ub_result_ptr result;
@@ -413,16 +408,6 @@ DNSResolver DNSResolver::create()
return DNSResolver();
}
-bool DNSResolver::check_address_syntax(const char *addr) const
-{
- // if string doesn't contain a dot, we won't consider it a url for now.
- if (strchr(addr,'.') == NULL)
- {
- return false;
- }
- return true;
-}
-
namespace dns_utils
{
diff --git a/src/common/dns_utils.h b/src/common/dns_utils.h
index f9507b42a..81079ba30 100644
--- a/src/common/dns_utils.h
+++ b/src/common/dns_utils.h
@@ -159,15 +159,6 @@ private:
// TODO: modify this to accommodate DNSSEC
std::vector<std::string> get_record(const std::string& url, int record_type, boost::optional<std::string> (*reader)(const char *,size_t), bool& dnssec_available, bool& dnssec_valid);
- /**
- * @brief Checks a string to see if it looks like a URL
- *
- * @param addr the string to be checked
- *
- * @return true if it looks enough like a URL, false if not
- */
- bool check_address_syntax(const char *addr) const;
-
DNSResolverData *m_data;
}; // class DNSResolver
diff --git a/src/net/parse.cpp b/src/net/parse.cpp
index 1df6175b4..92be492a3 100644
--- a/src/net/parse.cpp
+++ b/src/net/parse.cpp
@@ -38,7 +38,7 @@ namespace net
{
void get_network_address_host_and_port(const std::string& address, std::string& host, std::string& port)
{
- // require ipv6 address format "[addr:addr:addr:...:addr]:port"
+ // If IPv6 address format with port "[addr:addr:addr:...:addr]:port"
if (address.find(']') != std::string::npos)
{
host = address.substr(1, address.rfind(']') - 1);
@@ -47,6 +47,12 @@ namespace net
port = address.substr(address.rfind(':') + 1);
}
}
+ // Else if IPv6 address format without port e.g. "addr:addr:addr:...:addr"
+ else if (std::count(address.begin(), address.end(), ':') >= 2)
+ {
+ host = address;
+ }
+ // Else IPv4, Tor, I2P address or hostname
else
{
host = address.substr(0, address.rfind(':'));
diff --git a/src/net/parse.h b/src/net/parse.h
index 648076d7b..6ece931c6 100644
--- a/src/net/parse.h
+++ b/src/net/parse.h
@@ -38,6 +38,16 @@
namespace net
{
+ /*!
+ * \brief Takes a valid address string (IP, Tor, I2P, or DNS name) and splits it into host and port
+ *
+ * The host of an IPv6 addresses in the format "[x:x:..:x]:port" will have the braces stripped.
+ * For example, when the address is "[ffff::2023]", host will be set to "ffff::2023".
+ *
+ * \param address The address string one wants to split
+ * \param[out] host The host part of the address string. Is always set.
+ * \param[out] port The port part of the address string. Is only set when address string contains a port.
+ */
void get_network_address_host_and_port(const std::string& address, std::string& host, std::string& port);
/*!
diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl
index f33ce977d..df67734d5 100644
--- a/src/p2p/net_node.inl
+++ b/src/p2p/net_node.inl
@@ -645,20 +645,10 @@ namespace nodetool
{
using namespace boost::asio;
- std::string host = addr;
+ // Split addr string into host string and port string
+ std::string host;
std::string port = std::to_string(default_port);
- size_t colon_pos = addr.find_last_of(':');
- size_t dot_pos = addr.find_last_of('.');
- size_t square_brace_pos = addr.find('[');
-
- // IPv6 will have colons regardless. IPv6 and IPv4 address:port will have a colon but also either a . or a [
- // as IPv6 addresses specified as address:port are to be specified as "[addr:addr:...:addr]:port"
- // One may also specify an IPv6 address as simply "[addr:addr:...:addr]" without the port; in that case
- // the square braces will be stripped here.
- if ((std::string::npos != colon_pos && std::string::npos != dot_pos) || std::string::npos != square_brace_pos)
- {
- net::get_network_address_host_and_port(addr, host, port);
- }
+ net::get_network_address_host_and_port(addr, host, port);
MINFO("Resolving node address: host=" << host << ", port=" << port);
io_service io_srv;
@@ -2413,7 +2403,7 @@ namespace nodetool
return false;
}
return true;
- });
+ }, "0.0.0.0", m_ssl_support);
if(!r)
{
LOG_WARNING_CC(context, "Failed to call connect_async, network error.");
diff --git a/tests/unit_tests/dns_resolver.cpp b/tests/unit_tests/dns_resolver.cpp
index a6b733592..d56cbe45b 100644
--- a/tests/unit_tests/dns_resolver.cpp
+++ b/tests/unit_tests/dns_resolver.cpp
@@ -158,6 +158,17 @@ TEST(DNSResolver, GetTXTRecord)
EXPECT_STREQ("donate.getmonero.org", addr.c_str());
}
+TEST(DNSResolver, Localhost)
+{
+ tools::DNSResolver resolver = tools::DNSResolver::create();
+
+ bool avail, valid;
+ std::vector<std::string> ips = resolver.get_ipv4("localhost", avail, valid);
+
+ ASSERT_EQ(1, ips.size());
+ ASSERT_EQ("127.0.0.1", ips[0]);
+}
+
bool is_equal(const char *s, const std::vector<std::string> &v) { return v.size() == 1 && v[0] == s; }
TEST(DNS_PUBLIC, empty) { EXPECT_TRUE(tools::dns_utils::parse_dns_public("").empty()); }
diff --git a/tests/unit_tests/net.cpp b/tests/unit_tests/net.cpp
index 838f1af37..af0f07db0 100644
--- a/tests/unit_tests/net.cpp
+++ b/tests/unit_tests/net.cpp
@@ -938,6 +938,41 @@ TEST(get_network_address, ipv4subnet)
namespace
{
+ void na_host_and_port_test(std::string addr, std::string exp_host, std::string exp_port)
+ {
+ std::string host{"xxxxx"};
+ std::string port{"xxxxx"};
+ net::get_network_address_host_and_port(addr, host, port);
+ EXPECT_EQ(exp_host, host);
+ EXPECT_EQ(exp_port, port);
+ }
+} // anonymous namespace
+
+TEST(get_network_address_host_and_port, ipv4)
+{
+ na_host_and_port_test("9.9.9.9", "9.9.9.9", "xxxxx");
+ na_host_and_port_test("9.9.9.9:18081", "9.9.9.9", "18081");
+}
+
+TEST(get_network_address_host_and_port, ipv6)
+{
+ na_host_and_port_test("::ffff", "::ffff", "xxxxx");
+ na_host_and_port_test("[::ffff]", "::ffff", "xxxxx");
+ na_host_and_port_test("[::ffff]:00231", "::ffff", "00231");
+ na_host_and_port_test("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", "xxxxx");
+ na_host_and_port_test("[7777:7777:7777:7777:7777:7777:7777:7777]", "7777:7777:7777:7777:7777:7777:7777:7777", "xxxxx");
+ na_host_and_port_test("[7777:7777:7777:7777:7777:7777:7777:7777]:48080", "7777:7777:7777:7777:7777:7777:7777:7777", "48080");
+}
+
+TEST(get_network_address_host_and_port, hostname)
+{
+ na_host_and_port_test("localhost", "localhost", "xxxxx");
+ na_host_and_port_test("bar:29080", "bar", "29080"); // Issue https://github.com/monero-project/monero/issues/8633
+ na_host_and_port_test("xmrchain.net:18081", "xmrchain.net", "18081");
+}
+
+namespace
+{
using stream_type = boost::asio::ip::tcp;
struct io_thread
diff --git a/utils/gpg_keys/moneromooo.asc b/utils/gpg_keys/moneromooo.asc
index 959148e41..2060e99b7 100644
--- a/utils/gpg_keys/moneromooo.asc
+++ b/utils/gpg_keys/moneromooo.asc
@@ -13,18 +13,18 @@ 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
-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
+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
@@ -48,5 +48,5 @@ DZc98z3iS2EU3dFSCCCkrC9xupJ63hbWYzsUcox28hqqssBCFRqn9Unn4h05zhvl
YmZWTzkSzbu4/vwI08g51kfQyekeG8ho2LSTlbgyStqQhmmu+VMk6MhqqrGI4aQl
ZULb8ntkFefYwVy7jw0nxkc8jAhHrkKFPb7JR3vePfmUgfFMHjBraPWIcESgsMDA
3j6BmRzehQtWCsEg4JtSsKxcKt6kazvPDpbsyDqmfY/AVeIrFdY2ZQ==
-=BfKA
+=lTeM
-----END PGP PUBLIC KEY BLOCK-----