From e8a7525cebf5c8b00aa3cffa14221640acaa43f3 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 16 Feb 2017 20:03:26 +0000 Subject: [PATCH 01/10] dns_utils: factor TXT record loading code from checkpoint code --- src/common/dns_utils.cpp | 103 +++++++++++++++++++++++++ src/common/dns_utils.h | 2 + src/cryptonote_basic/checkpoints.cpp | 109 ++------------------------- 3 files changed, 110 insertions(+), 104 deletions(-) diff --git a/src/common/dns_utils.cpp b/src/common/dns_utils.cpp index 5ff39574..4feab16f 100644 --- a/src/common/dns_utils.cpp +++ b/src/common/dns_utils.cpp @@ -37,6 +37,7 @@ #include #include "include_base_utils.h" +#include #include using namespace epee; namespace bf = boost::filesystem; @@ -451,6 +452,108 @@ std::string get_account_address_as_str_from_url(const std::string& url, bool& dn return addresses[0]; } +namespace +{ + bool dns_records_match(const std::vector& a, const std::vector& b) + { + if (a.size() != b.size()) return false; + + for (const auto& record_in_a : a) + { + bool ok = false; + for (const auto& record_in_b : b) + { + if (record_in_a == record_in_b) + { + ok = true; + break; + } + } + if (!ok) return false; + } + + return true; + } +} + +bool load_txt_records_from_dns(std::vector &good_records, const std::vector &dns_urls) +{ + std::vector > records; + records.resize(dns_urls.size()); + + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution dis(0, dns_urls.size() - 1); + size_t first_index = dis(gen); + + bool avail, valid; + size_t cur_index = first_index; + do + { + std::string url = dns_urls[cur_index]; + + records[cur_index] = tools::DNSResolver::instance().get_txt_record(url, avail, valid); + if (!avail) + { + records[cur_index].clear(); + LOG_PRINT_L2("DNSSEC not available for checkpoint update at URL: " << url << ", skipping."); + } + if (!valid) + { + records[cur_index].clear(); + LOG_PRINT_L2("DNSSEC validation failed for checkpoint update at URL: " << url << ", skipping."); + } + + cur_index++; + if (cur_index == dns_urls.size()) + { + cur_index = 0; + } + records[cur_index].clear(); + } while (cur_index != first_index); + + size_t num_valid_records = 0; + + for( const auto& record_set : records) + { + if (record_set.size() != 0) + { + num_valid_records++; + } + } + + if (num_valid_records < 2) + { + LOG_PRINT_L0("WARNING: no two valid MoneroPulse DNS checkpoint records were received"); + return false; + } + + int good_records_index = -1; + for (size_t i = 0; i < records.size() - 1; ++i) + { + if (records[i].size() == 0) continue; + + for (size_t j = i + 1; j < records.size(); ++j) + { + if (dns_records_match(records[i], records[j])) + { + good_records_index = i; + break; + } + } + if (good_records_index >= 0) break; + } + + if (good_records_index < 0) + { + LOG_PRINT_L0("WARNING: no two MoneroPulse DNS checkpoint records matched"); + return false; + } + + good_records = records[good_records_index]; + return true; +} + } // namespace tools::dns_utils } // namespace tools diff --git a/src/common/dns_utils.h b/src/common/dns_utils.h index 6ecf5595..2e881f0e 100644 --- a/src/common/dns_utils.h +++ b/src/common/dns_utils.h @@ -165,6 +165,8 @@ std::vector addresses_from_url(const std::string& url, bool& dnssec std::string get_account_address_as_str_from_url(const std::string& url, bool& dnssec_valid); +bool load_txt_records_from_dns(std::vector &records, const std::vector &dns_urls); + } // namespace tools::dns_utils } // namespace tools diff --git a/src/cryptonote_basic/checkpoints.cpp b/src/cryptonote_basic/checkpoints.cpp index 3cf804ed..1e775488 100644 --- a/src/cryptonote_basic/checkpoints.cpp +++ b/src/cryptonote_basic/checkpoints.cpp @@ -42,30 +42,6 @@ using namespace epee; #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "checkpoints" -namespace -{ - bool dns_records_match(const std::vector& a, const std::vector& b) - { - if (a.size() != b.size()) return false; - - for (const auto& record_in_a : a) - { - bool ok = false; - for (const auto& record_in_b : b) - { - if (record_in_a == record_in_b) - { - ok = true; - break; - } - } - if (!ok) return false; - } - - return true; - } -} // anonymous namespace - namespace cryptonote { //--------------------------------------------------------------------------- @@ -230,6 +206,8 @@ namespace cryptonote bool checkpoints::load_checkpoints_from_dns(bool testnet) { + std::vector records; + // All four MoneroPulse domains have DNSSEC on and valid static const std::vector dns_urls = { "checkpoints.moneropulse.se" , "checkpoints.moneropulse.org" @@ -243,87 +221,10 @@ namespace cryptonote , "testpoints.moneropulse.co" }; - std::vector > records; - records.resize(dns_urls.size()); + if (!tools::dns_utils::load_txt_records_from_dns(records, testnet ? testnet_dns_urls : dns_urls)) + return true; // why true ? - std::random_device rd; - std::mt19937 gen(rd()); - std::uniform_int_distribution dis(0, dns_urls.size() - 1); - size_t first_index = dis(gen); - - bool avail, valid; - size_t cur_index = first_index; - do - { - std::string url; - if (testnet) - { - url = testnet_dns_urls[cur_index]; - } - else - { - url = dns_urls[cur_index]; - } - - records[cur_index] = tools::DNSResolver::instance().get_txt_record(url, avail, valid); - if (!avail) - { - records[cur_index].clear(); - LOG_PRINT_L2("DNSSEC not available for checkpoint update at URL: " << url << ", skipping."); - } - if (!valid) - { - records[cur_index].clear(); - LOG_PRINT_L2("DNSSEC validation failed for checkpoint update at URL: " << url << ", skipping."); - } - - cur_index++; - if (cur_index == dns_urls.size()) - { - cur_index = 0; - } - records[cur_index].clear(); - } while (cur_index != first_index); - - size_t num_valid_records = 0; - - for( const auto& record_set : records) - { - if (record_set.size() != 0) - { - num_valid_records++; - } - } - - if (num_valid_records < 2) - { - LOG_PRINT_L0("WARNING: no two valid MoneroPulse DNS checkpoint records were received"); - return true; - } - - int good_records_index = -1; - for (size_t i = 0; i < records.size() - 1; ++i) - { - if (records[i].size() == 0) continue; - - for (size_t j = i + 1; j < records.size(); ++j) - { - if (dns_records_match(records[i], records[j])) - { - good_records_index = i; - break; - } - } - if (good_records_index >= 0) break; - } - - if (good_records_index < 0) - { - LOG_PRINT_L0("WARNING: no two MoneroPulse DNS checkpoint records matched"); - return true; - } - - for (auto& record : records[good_records_index]) + for (const auto& record : records) { auto pos = record.find(":"); if (pos != std::string::npos) From 08c3f380313a1262729bd0dbbbf5a85b6f695e81 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 16 Feb 2017 22:30:01 +0000 Subject: [PATCH 02/10] util: add a vercmp function to compare version numbers It is simple, supports simple x.y.z type numeric versions, and does not attempt any kind of validation --- src/common/util.cpp | 17 +++++++++++++ src/common/util.h | 1 + tests/unit_tests/CMakeLists.txt | 3 ++- tests/unit_tests/vercmp.cpp | 43 +++++++++++++++++++++++++++++++++ 4 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 tests/unit_tests/vercmp.cpp diff --git a/src/common/util.cpp b/src/common/util.cpp index bfcf86bc..2741497d 100644 --- a/src/common/util.cpp +++ b/src/common/util.cpp @@ -568,4 +568,21 @@ std::string get_nix_version_display_string() MDEBUG("Address '" << address << "' is not local"); return false; } + int vercmp(const char *v0, const char *v1) + { + std::vector f0, f1; + boost::split(f0, v0, boost::is_any_of(".")); + boost::split(f1, v1, boost::is_any_of(".")); + while (f0.size() < f1.size()) + f0.push_back("0"); + while (f1.size() < f0.size()) + f1.push_back("0"); + for (size_t i = 0; i < f0.size(); ++i) { + int f0i = atoi(f0[i].c_str()), f1i = atoi(f1[i].c_str()); + int n = f0i - f1i; + if (n) + return n; + } + return 0; + } } diff --git a/src/common/util.h b/src/common/util.h index c2ffc44c..bef4b620 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -183,4 +183,5 @@ namespace tools unsigned get_max_concurrency(); bool is_local_address(const std::string &address); + int vercmp(const char *v0, const char *v1); // returns < 0, 0, > 0, similar to strcmp, but more human friendly than lexical - does not attempt to validate } diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt index e70ed1a2..04e47e49 100644 --- a/tests/unit_tests/CMakeLists.txt +++ b/tests/unit_tests/CMakeLists.txt @@ -58,7 +58,8 @@ set(unit_tests_sources uri.cpp varint.cpp ringct.cpp - output_selection.cpp) + output_selection.cpp + vercmp.cpp) set(unit_tests_headers unit_tests_utils.h) diff --git a/tests/unit_tests/vercmp.cpp b/tests/unit_tests/vercmp.cpp new file mode 100644 index 00000000..d48dfdf7 --- /dev/null +++ b/tests/unit_tests/vercmp.cpp @@ -0,0 +1,43 @@ +// Copyright (c) 2017, 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 "gtest/gtest.h" + +#include "common/util.h" + +TEST(vercmp, empty) { ASSERT_TRUE(tools::vercmp("", "") == 0); } +TEST(vercmp, empty0) { ASSERT_TRUE(tools::vercmp("", "0") == 0); } +TEST(vercmp, empty1) { ASSERT_TRUE(tools::vercmp("0", "") == 0); } +TEST(vercmp, zero_zero) { ASSERT_TRUE(tools::vercmp("0", "0") == 0); } +TEST(vercmp, one_one) { ASSERT_TRUE(tools::vercmp("1", "1") == 0); } +TEST(vercmp, one_two) { ASSERT_TRUE(tools::vercmp("1", "2") < 0); } +TEST(vercmp, two_one) { ASSERT_TRUE(tools::vercmp("2", "1") > 0); } +TEST(vercmp, ten_nine) { ASSERT_TRUE(tools::vercmp("10", "9") > 0); } +TEST(vercmp, one_dot_ten_one_dot_nine) { ASSERT_TRUE(tools::vercmp("1.10", "1.9") > 0); } +TEST(vercmp, one_one_dot_nine) { ASSERT_TRUE(tools::vercmp("1", "1.9") < 0); } + From 969ad710baabec35df77ae8817e227937aa62601 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 16 Feb 2017 23:29:29 +0000 Subject: [PATCH 03/10] dns_utils: fix first checked DNS entry being ignored --- src/common/dns_utils.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/common/dns_utils.cpp b/src/common/dns_utils.cpp index 4feab16f..f7655e3c 100644 --- a/src/common/dns_utils.cpp +++ b/src/common/dns_utils.cpp @@ -509,7 +509,6 @@ bool load_txt_records_from_dns(std::vector &good_records, const std { cur_index = 0; } - records[cur_index].clear(); } while (cur_index != first_index); size_t num_valid_records = 0; From e3cae4ae2ec974f9ad3f1dd7767518e35c095977 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 16 Feb 2017 23:46:42 +0000 Subject: [PATCH 04/10] core: display any fork warning at startup too This is a good time for the operator to be around and see it --- src/cryptonote_core/cryptonote_core.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 1463c2fe..0361d4f5 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -783,7 +783,7 @@ namespace cryptonote cryptonote_protocol_stub m_protocol_stub; //!< cryptonote protocol stub instance epee::math_helper::once_a_time_seconds<60*60*12, false> m_store_blockchain_interval; //!< interval for manual storing of Blockchain, if enabled - epee::math_helper::once_a_time_seconds<60*60*2, false> m_fork_moaner; //!< interval for checking HardFork status + epee::math_helper::once_a_time_seconds<60*60*2, true> m_fork_moaner; //!< interval for checking HardFork status epee::math_helper::once_a_time_seconds<60*2, false> m_txpool_auto_relayer; //!< interval for checking re-relaying txpool transactions friend class tx_validate_inputs; From f640512c53caca9d7c3c69802841d174818cf953 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 18 Feb 2017 16:30:29 +0000 Subject: [PATCH 05/10] Optionally query moneropulse DNS records to check for updates It just checks and prints a message if there is a new version for now. --- CMakeLists.txt | 8 ++ Makefile | 44 +++++---- src/common/CMakeLists.txt | 6 +- src/common/command_line.cpp | 5 ++ src/common/command_line.h | 1 + src/common/updates.cpp | 114 ++++++++++++++++++++++++ src/common/updates.h | 37 ++++++++ src/cryptonote_core/cryptonote_core.cpp | 49 ++++++++++ src/cryptonote_core/cryptonote_core.h | 15 ++++ 9 files changed, 261 insertions(+), 18 deletions(-) create mode 100644 src/common/updates.cpp create mode 100644 src/common/updates.h diff --git a/CMakeLists.txt b/CMakeLists.txt index f5949334..c01be24d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -88,6 +88,14 @@ else() set(OPT_FLAGS_RELEASE "-Ofast") endif() +# BUILD_TAG is used to select the build type to check for a new version +if(BUILD_TAG) + message(STATUS "Building build tag ${BUILD_TAG}") + add_definitions("-DBUILD_TAG=${BUILD_TAG}") +else() + message(STATUS "Building without build tag") +endif() + set(CMAKE_C_FLAGS_RELEASE "-DNDEBUG ${OPT_FLAGS_RELEASE}") set(CMAKE_CXX_FLAGS_RELEASE "-DNDEBUG ${OPT_FLAGS_RELEASE}") diff --git a/Makefile b/Makefile index e6517eba..3ee9d0e3 100644 --- a/Makefile +++ b/Makefile @@ -58,43 +58,55 @@ release-all: mkdir -p build/release cd build/release && cmake -D BUILD_TESTS=ON -D CMAKE_BUILD_TYPE=release ../.. && $(MAKE) +release-static: + mkdir -p build/release + cd build/release && cmake -D STATIC=ON -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=release ../.. && $(MAKE) + coverage: mkdir -p build/debug cd build/debug && cmake -D BUILD_TESTS=ON -D CMAKE_BUILD_TYPE=Debug -D COVERAGE=ON ../.. && $(MAKE) && $(MAKE) test -release-static-armv6: - mkdir -p build/release - cd build/release && cmake -D BUILD_TESTS=OFF -D ARCH="armv6zk" -D STATIC=ON -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=release ../.. && $(MAKE) +# Targets for specific prebuilt builds which will be advertised for updates by their build tag -release-static-armv7: +release-static-linux-armv6: mkdir -p build/release - cd build/release && cmake -D BUILD_TESTS=OFF -D ARCH="armv7-a" -D STATIC=ON -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=release ../.. && $(MAKE) + cd build/release && cmake -D BUILD_TESTS=OFF -D ARCH="armv6zk" -D STATIC=ON -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=release -D BUILD_TAG="linux-armv6" ../.. && $(MAKE) + +release-static-linux-armv7: + mkdir -p build/release + cd build/release && cmake -D BUILD_TESTS=OFF -D ARCH="armv7-a" -D STATIC=ON -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=release -D BUILD_TAG="linux-armv7" ../.. && $(MAKE) release-static-android: mkdir -p build/release - cd build/release && cmake -D BUILD_TESTS=OFF -D ARCH="armv7-a" -D STATIC=ON -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=release -D ANDROID=true -D INSTALL_VENDORED_LIBUNBOUND=ON ../.. && $(MAKE) + cd build/release && cmake -D BUILD_TESTS=OFF -D ARCH="armv7-a" -D STATIC=ON -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=release -D ANDROID=true -D INSTALL_VENDORED_LIBUNBOUND=ON -D BUILD_TAG="android"../.. && $(MAKE) -release-static-armv8: +release-static-linux-armv8: mkdir -p build/release - cd build/release && cmake -D BUILD_TESTS=OFF -D ARCH="armv8-a" -D STATIC=ON -D BUILD_64=ON -D CMAKE_BUILD_TYPE=release ../.. && $(MAKE) + cd build/release && cmake -D BUILD_TESTS=OFF -D ARCH="armv8-a" -D STATIC=ON -D BUILD_64=ON -D CMAKE_BUILD_TYPE=release -D BUILD_TAG="linux-armv8" ../.. && $(MAKE) -release-static: release-static-64 - -release-static-64: +release-static-linux-x86_64: mkdir -p build/release - cd build/release && cmake -D STATIC=ON -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=release ../.. && $(MAKE) + cd build/release && cmake -D STATIC=ON -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=release -D BUILD_TAG="linux-x64" ../.. && $(MAKE) -release-static-32: +release-static-freebsd-x86_64: mkdir -p build/release - cd build/release && cmake -D STATIC=ON -D ARCH="i686" -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=release ../.. && $(MAKE) + cd build/release && cmake -D STATIC=ON -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=release -D BUILD_TAG="freebsd-x64" ../.. && $(MAKE) + +release-static-freebsd-x86_64: + mkdir -p build/release + cd build/release && cmake -D STATIC=ON -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=release -D BUILD_TAG="mac-x64" ../.. && $(MAKE) + +release-static-linux-i686: + mkdir -p build/release + cd build/release && cmake -D STATIC=ON -D ARCH="i686" -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=release -D BUILD_TAG="linux-x86" ../.. && $(MAKE) release-static-win64: mkdir -p build/release - cd build/release && cmake -G "MSYS Makefiles" -D STATIC=ON -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=Release -D CMAKE_TOOLCHAIN_FILE=../../cmake/64-bit-toolchain.cmake -D MSYS2_FOLDER=c:/msys64 ../.. && $(MAKE) + cd build/release && cmake -G "MSYS Makefiles" -D STATIC=ON -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=Release -D BUILD_TAG="win-x64" -D CMAKE_TOOLCHAIN_FILE=../../cmake/64-bit-toolchain.cmake -D MSYS2_FOLDER=c:/msys64 ../.. && $(MAKE) release-static-win32: mkdir -p build/release - cd build/release && cmake -G "MSYS Makefiles" -D STATIC=ON -D ARCH="i686" -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=Release -D CMAKE_TOOLCHAIN_FILE=../../cmake/32-bit-toolchain.cmake -D MSYS2_FOLDER=c:/msys32 ../.. && $(MAKE) + cd build/release && cmake -G "MSYS Makefiles" -D STATIC=ON -D ARCH="i686" -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=Release -D BUILD_TAG="win-x32" -D CMAKE_TOOLCHAIN_FILE=../../cmake/32-bit-toolchain.cmake -D MSYS2_FOLDER=c:/msys32 ../.. && $(MAKE) clean: @echo "WARNING: Back-up your wallet if it exists within ./build!" ; \ diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index c63d9d0a..9227e745 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -35,7 +35,8 @@ set(common_sources password.cpp perf_timer.cpp task_region.cpp - thread_group.cpp) + thread_group.cpp + updates.cpp) if (STACK_TRACE) list(APPEND common_sources stack_trace.cpp) @@ -62,7 +63,8 @@ set(common_private_headers perf_timer.h stack_trace.h task_region.h - thread_group.h) + thread_group.h + updates.h) monero_private_headers(common ${common_private_headers}) diff --git a/src/common/command_line.cpp b/src/common/command_line.cpp index c3df5c09..8739a93c 100644 --- a/src/common/command_line.cpp +++ b/src/common/command_line.cpp @@ -120,4 +120,9 @@ namespace command_line , "How many blocks to sync at once during chain synchronization." , BLOCKS_SYNCHRONIZING_DEFAULT_COUNT }; + const command_line::arg_descriptor arg_check_updates = { + "check-updates" + , "Check for new versions of monero: [disabled|notify|download|update]" + , "notify" + }; } diff --git a/src/common/command_line.h b/src/common/command_line.h index a09365a6..f10e68e1 100644 --- a/src/common/command_line.h +++ b/src/common/command_line.h @@ -218,4 +218,5 @@ namespace command_line extern const arg_descriptor arg_prep_blocks_threads; extern const arg_descriptor arg_show_time_stats; extern const arg_descriptor arg_block_sync_size; + extern const arg_descriptor arg_check_updates; } diff --git a/src/common/updates.cpp b/src/common/updates.cpp new file mode 100644 index 00000000..93610688 --- /dev/null +++ b/src/common/updates.cpp @@ -0,0 +1,114 @@ +// Copyright (c) 2017, 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 "util.h" +#include "dns_utils.h" +#include "updates.h" + +#undef MONERO_DEFAULT_LOG_CATEGORY +#define MONERO_DEFAULT_LOG_CATEGORY "updates" + +namespace tools +{ + bool check_updates(const std::string &software, const std::string &buildtag, bool testnet, std::string &version, std::string &hash) + { + std::vector records; + bool found = false; + + MDEBUG("Checking updates for " << buildtag << " " << software); + + // All four MoneroPulse domains have DNSSEC on and valid + static const std::vector dns_urls = { + }; + + static const std::vector testnet_dns_urls = { "testver.moneropulse.net" + }; + + if (!tools::dns_utils::load_txt_records_from_dns(records, testnet ? testnet_dns_urls : dns_urls)) + return false; + + for (const auto& record : records) + { + std::vector fields; + boost::split(fields, record, boost::is_any_of(":")); + if (fields.size() != 4) + { + MWARNING("Updates record does not have 4 fields: " << record); + continue; + } + + if (software != fields[0] || buildtag != fields[1]) + continue; + + bool alnum = true; + for (auto c: hash) + if (!isalnum(c)) + alnum = false; + if (hash.size() != 64 && !alnum) + { + MWARNING("Invalid hash: " << hash); + continue; + } + + // use highest version + if (found) + { + int cmp = vercmp(version.c_str(), fields[2].c_str()); + if (cmp > 0) + continue; + if (cmp == 0 && hash != fields[3]) + MWARNING("Two matches found for " << software << " version " << version << " on " << buildtag); + } + + version = fields[2]; + hash = fields[3]; + + MINFO("Found new version " << version << " with hash " << hash); + found = true; + } + return found; + } + + std::string get_update_url(const std::string &software, const std::string &subdir, const std::string &buildtag, const std::string &version) + { + static const char base[] = "https://downloads.getmonero.org/"; +#ifdef _WIN32 + static const char extension[] = ".zip"; +#else + static const char extension[] = ".tar.bz2"; +#endif + + std::string url; + + url = base; + if (!subdir.empty()) + url += subdir + "/"; + url = url + software + "-" + buildtag + "-v" + version + extension; + return url; + } +} diff --git a/src/common/updates.h b/src/common/updates.h new file mode 100644 index 00000000..1a70e06f --- /dev/null +++ b/src/common/updates.h @@ -0,0 +1,37 @@ +// Copyright (c) 2017, 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. + +#pragma once + +#include + +namespace tools +{ + bool check_updates(const std::string &software, const std::string &buildtag, bool bestnet, std::string &version, std::string &hash); + std::string get_update_url(const std::string &software, const std::string &subdir, const std::string &buildtag, const std::string &version); +} diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 239ef5d8..8c3db4c0 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -35,6 +35,7 @@ using namespace epee; #include "cryptonote_core.h" #include "common/command_line.h" #include "common/util.h" +#include "common/updates.h" #include "warnings.h" #include "crypto/crypto.h" #include "cryptonote_config.h" @@ -148,6 +149,7 @@ namespace cryptonote command_line::add_arg(desc, command_line::arg_db_sync_mode); command_line::add_arg(desc, command_line::arg_show_time_stats); command_line::add_arg(desc, command_line::arg_block_sync_size); + command_line::add_arg(desc, command_line::arg_check_updates); } //----------------------------------------------------------------------------------------------- bool core::handle_command_line(const boost::program_options::variables_map& vm) @@ -242,6 +244,7 @@ namespace cryptonote std::string db_sync_mode = command_line::get_arg(vm, command_line::arg_db_sync_mode); bool fast_sync = command_line::get_arg(vm, command_line::arg_fast_block_sync) != 0; uint64_t blocks_threads = command_line::get_arg(vm, command_line::arg_prep_blocks_threads); + std::string check_updates_string = command_line::get_arg(vm, command_line::arg_check_updates); boost::filesystem::path folder(m_config_folder); if (m_fakechain) @@ -379,6 +382,20 @@ namespace cryptonote // with respect to what blocks we already have CHECK_AND_ASSERT_MES(update_checkpoints(), false, "One or more checkpoints loaded from json or dns conflicted with existing checkpoints."); + // DNS versions checking + if (check_updates_string == "disabled") + check_updates_level = UPDATES_DISABLED; + else if (check_updates_string == "notify") + check_updates_level = UPDATES_NOTIFY; + else if (check_updates_string == "download") + check_updates_level = UPDATES_DOWNLOAD; + else if (check_updates_string == "update") + check_updates_level = UPDATES_UPDATE; + else { + MERROR("Invalid argument to --dns-versions-check: " << check_updates_string); + return false; + } + r = m_miner.init(vm, m_testnet); CHECK_AND_ASSERT_MES(r, false, "Failed to initialize miner instance"); @@ -1000,6 +1017,7 @@ namespace cryptonote m_fork_moaner.do_call(boost::bind(&core::check_fork_time, this)); m_txpool_auto_relayer.do_call(boost::bind(&core::relay_txpool_transactions, this)); + m_check_updates_interval.do_call(boost::bind(&core::check_updates, this)); m_miner.on_idle(); m_mempool.on_idle(); return true; @@ -1027,6 +1045,37 @@ namespace cryptonote return true; } //----------------------------------------------------------------------------------------------- + bool core::check_updates() + { + static const char software[] = "monerod"; + static const char subdir[] = "cli"; // because it can never be simple +#ifdef BUILD_TAG + static const char buildtag[] = BOOST_PP_STRINGIZE(BUILD_TAG); +#else + static const char buildtag[] = "source"; +#endif + + if (check_updates_level == UPDATES_DISABLED) + return true; + + std::string version, hash; + MDEBUG("Checking for a new " << software << " version for " << buildtag); + if (!tools::check_updates(software, buildtag, m_testnet, version, hash)) + return false; + + if (tools::vercmp(version.c_str(), MONERO_VERSION) <= 0) + return true; + + std::string url = tools::get_update_url(software, subdir, buildtag, version); + MGINFO("Version " << version << " of " << software << " for " << buildtag << " is available: " << url << ", SHA256 hash " << hash); + + if (check_updates_level == UPDATES_NOTIFY) + return true; + + MERROR("Download/update not implemented yet"); + return true; + } + //----------------------------------------------------------------------------------------------- void core::set_target_blockchain_height(uint64_t target_blockchain_height) { m_target_blockchain_height = target_blockchain_height; diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 0361d4f5..d82466fe 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -763,6 +763,13 @@ namespace cryptonote */ bool relay_txpool_transactions(); + /** + * @brief checks DNS versions + * + * @return true on success, false otherwise + */ + bool check_updates(); + bool m_test_drop_download = true; //!< whether or not to drop incoming blocks (for testing) uint64_t m_test_drop_download_height = 0; //!< height under which to drop incoming blocks, if doing so @@ -785,6 +792,7 @@ namespace cryptonote epee::math_helper::once_a_time_seconds<60*60*12, false> m_store_blockchain_interval; //!< interval for manual storing of Blockchain, if enabled epee::math_helper::once_a_time_seconds<60*60*2, true> m_fork_moaner; //!< interval for checking HardFork status epee::math_helper::once_a_time_seconds<60*2, false> m_txpool_auto_relayer; //!< interval for checking re-relaying txpool transactions + epee::math_helper::once_a_time_seconds<60*60*12, true> m_check_updates_interval; //!< interval for checking for new versions friend class tx_validate_inputs; std::atomic m_starter_message_showed; //!< has the "daemon will sync now" message been shown? @@ -808,6 +816,13 @@ namespace cryptonote time_t start_time; std::unordered_set bad_semantics_txes; + + enum { + UPDATES_DISABLED, + UPDATES_NOTIFY, + UPDATES_DOWNLOAD, + UPDATES_UPDATE, + } check_updates_level; }; } From efb72e74e260bfbde14e5562abe4f46bf418e080 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 20 Feb 2017 09:58:11 +0000 Subject: [PATCH 06/10] http_client: add a couple consts --- contrib/epee/include/net/http_client.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/epee/include/net/http_client.h b/contrib/epee/include/net/http_client.h index 3d8c759c..d73eda39 100644 --- a/contrib/epee/include/net/http_client.h +++ b/contrib/epee/include/net/http_client.h @@ -107,14 +107,14 @@ using namespace std; //--------------------------------------------------------------------------- static inline const char* get_hex_vals() { - static char hexVals[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; + static const char hexVals[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; return hexVals; } static inline const char* get_unsave_chars() { //static char unsave_chars[] = "\"<>%\\^[]`+$,@:;/!#?=&"; - static char unsave_chars[] = "\"<>%\\^[]`+$,@:;!#&"; + static const char unsave_chars[] = "\"<>%\\^[]`+$,@:;!#&"; return unsave_chars; } From d282cfcc46d39dc49e97f9ec5cedf7425e74d71f Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 20 Feb 2017 17:41:39 +0000 Subject: [PATCH 07/10] core: test key images against validity domain --- src/cryptonote_core/cryptonote_core.cpp | 18 ++++++++++++++++++ src/cryptonote_core/cryptonote_core.h | 10 ++++++++++ src/ringct/rctOps.h | 4 ++++ 3 files changed, 32 insertions(+) diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 8c3db4c0..7ed01ca1 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -616,6 +616,12 @@ namespace cryptonote return false; } + if (!check_tx_inputs_keyimages_domain(tx)) + { + MERROR_VER("tx uses key image not in the valid domain"); + return false; + } + if (tx.version >= 2) { const rct::rctSig &rv = tx.rct_signatures; @@ -699,6 +705,18 @@ namespace cryptonote return true; } //----------------------------------------------------------------------------------------------- + bool core::check_tx_inputs_keyimages_domain(const transaction& tx) const + { + std::unordered_set ki; + for(const auto& in: tx.vin) + { + CHECKED_GET_SPECIFIC_VARIANT(in, const txin_to_key, tokey_in, false); + if (!(rct::scalarmultKey(rct::ki2rct(tokey_in.k_image), rct::curveOrder()) == rct::identity())) + return false; + } + return true; + } + //----------------------------------------------------------------------------------------------- bool core::add_new_tx(const transaction& tx, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay) { crypto::hash tx_hash = get_transaction_hash(tx); diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index d82466fe..43435b0c 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -744,6 +744,16 @@ namespace cryptonote */ bool check_tx_inputs_keyimages_diff(const transaction& tx) const; + /** + * @brief verify that each input key image in a transaction is in + * the valid domain + * + * @param tx the transaction to check + * + * @return false if any key image is not in the valid domain, otherwise true + */ + bool check_tx_inputs_keyimages_domain(const transaction& tx) const; + /** * @brief checks HardFork status and prints messages about it * diff --git a/src/ringct/rctOps.h b/src/ringct/rctOps.h index 90f54b05..cb19bbbd 100644 --- a/src/ringct/rctOps.h +++ b/src/ringct/rctOps.h @@ -66,6 +66,7 @@ namespace rct { static const key Z = { {0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 } }; static const key I = { {0x01, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 } }; + static const key L = { {0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10 } }; //Creates a zero scalar inline key zero() { return Z; } @@ -73,6 +74,9 @@ namespace rct { //Creates a zero elliptic curve point inline key identity() { return I; } inline void identity(key &Id) { memcpy(&Id, &I, 32); } + //Creates a key equal to the curve order + inline key curveOrder() { return L; } + inline void curveOrder(key &l) { l = L; } //copies a scalar or point inline void copy(key &AA, const key &A) { memcpy(&AA, &A, 32); } inline key copy(const key & A) { key AA; memcpy(&AA, &A, 32); return AA; } From 4bf7849297b618502a9c55da4fb03825d6db01af Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 20 Feb 2017 19:06:38 +0000 Subject: [PATCH 08/10] mlog: only silence errors for net by default, not net.* --- contrib/epee/src/mlog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/epee/src/mlog.cpp b/contrib/epee/src/mlog.cpp index c212456f..995674ab 100644 --- a/contrib/epee/src/mlog.cpp +++ b/contrib/epee/src/mlog.cpp @@ -88,7 +88,7 @@ static const char *get_default_categories(int level) switch (level) { case 0: - categories = "*:WARNING,net*:FATAL,global:INFO,verify:FATAL,stacktrace:INFO"; + categories = "*:WARNING,net:FATAL,global:INFO,verify:FATAL,stacktrace:INFO"; break; case 1: categories = "*:WARNING,global:INFO,stacktrace:INFO"; From 216f062eb875eb0769d63c65ed0f5c8de51a9590 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 20 Feb 2017 20:46:50 +0000 Subject: [PATCH 09/10] util: add a SHA256 function --- src/common/CMakeLists.txt | 1 + src/common/util.cpp | 35 ++++++++++++++++++++++++++++++++++- src/common/util.h | 2 ++ 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 9227e745..8f25a43d 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -76,6 +76,7 @@ target_link_libraries(common PUBLIC epee crypto + -lcrypto ${UNBOUND_LIBRARY} ${LIBUNWIND_LIBRARIES} ${Boost_DATE_TIME_LIBRARY} diff --git a/src/common/util.cpp b/src/common/util.cpp index 2741497d..90748ddb 100644 --- a/src/common/util.cpp +++ b/src/common/util.cpp @@ -31,6 +31,7 @@ #include #include "include_base_utils.h" +#include "file_io_utils.h" using namespace epee; #include "util.h" @@ -46,7 +47,7 @@ using namespace epee; #endif #include #include - +#include namespace tools { @@ -585,4 +586,36 @@ std::string get_nix_version_display_string() } return 0; } + + bool sha256sum(const std::string &filename, crypto::hash &hash) + { + if (!epee::file_io_utils::is_file_exist(filename)) + return false; + std::ifstream f; + f.exceptions(std::ifstream::failbit | std::ifstream::badbit); + f.open(filename, std::ios_base::binary | std::ios_base::in | std::ios::ate); + if (!f) + return false; + std::ifstream::pos_type file_size = f.tellg(); + SHA256_CTX ctx; + if (!SHA256_Init(&ctx)) + return false; + size_t size_left = file_size; + f.seekg(0, std::ios::beg); + while (size_left) + { + char buf[4096]; + std::ifstream::pos_type read_size = size_left > sizeof(buf) ? sizeof(buf) : size_left; + f.read(buf, read_size); + if (!f || !f.good()) + return false; + if (!SHA256_Update(&ctx, buf, read_size)) + return false; + size_left -= read_size; + } + f.close(); + if (!SHA256_Final((unsigned char*)hash.data, &ctx)) + return false; + return true; + } } diff --git a/src/common/util.h b/src/common/util.h index bef4b620..8ab46912 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -184,4 +184,6 @@ namespace tools bool is_local_address(const std::string &address); int vercmp(const char *v0, const char *v1); // returns < 0, 0, > 0, similar to strcmp, but more human friendly than lexical - does not attempt to validate + + bool sha256sum(const std::string &filename, crypto::hash &hash); } From a5a0a3c8944f7cd6b5b72aaf2b9c41c7b3c40358 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 20 Feb 2017 20:48:36 +0000 Subject: [PATCH 10/10] core: updates can now be downloaded (and SHA256 hash checked) --- src/common/CMakeLists.txt | 2 + src/common/download.cpp | 138 ++++++++++++++++++++++++ src/common/download.h | 36 +++++++ src/cryptonote_core/cryptonote_core.cpp | 30 ++++++ 4 files changed, 206 insertions(+) create mode 100644 src/common/download.cpp create mode 100644 src/common/download.h diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 8f25a43d..eb4d4c25 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -30,6 +30,7 @@ set(common_sources base58.cpp command_line.cpp dns_utils.cpp + download.cpp util.cpp i18n.cpp password.cpp @@ -50,6 +51,7 @@ set(common_private_headers command_line.h common_fwd.h dns_utils.h + download.h http_connection.h int-util.h pod-class.h diff --git a/src/common/download.cpp b/src/common/download.cpp new file mode 100644 index 00000000..c5ee797d --- /dev/null +++ b/src/common/download.cpp @@ -0,0 +1,138 @@ +// Copyright (c) 2017, 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 +#include +#include +#include +#include "cryptonote_config.h" +#include "include_base_utils.h" +#include "net/http_client.h" +#include "download.h" + +#undef MONERO_DEFAULT_LOG_CATEGORY +#define MONERO_DEFAULT_LOG_CATEGORY "net.dl" + +namespace tools +{ + static bool download_thread(const std::string &path, const std::string &url) + { + try + { + MINFO("Downloading " << url << " to " << path); + std::ofstream f; + f.open(path, std::ios_base::binary | std::ios_base::out | std::ios_base::trunc); + if (!f.good()) { + MERROR("Failed to open file " << path); + return false; + } + class download_client: public epee::net_utils::http::http_simple_client + { + public: + download_client(std::ofstream &f): f(f) {} + virtual ~download_client() { f.close(); } + virtual bool handle_target_data(std::string &piece_of_transfer) + { + try + { + f << piece_of_transfer; + return f.good(); + } + catch (const std::exception &e) + { + MERROR("Error writing data: " << e.what()); + return false; + } + } + private: + std::ofstream &f; + } client(f); + epee::net_utils::http::url_content u_c; + if (!epee::net_utils::parse_url(url, u_c)) + { + MWARNING("Failed to parse URL " << url); + return false; + } + if (u_c.host.empty()) + { + MWARNING("Failed to determine address from URL " << url); + return false; + } + uint16_t port = u_c.port ? u_c.port : 80; + MDEBUG("Connecting to " << u_c.host << ":" << port); + client.set_server(u_c.host, std::to_string(port), boost::none); + if (!client.connect(std::chrono::seconds(30))) + { + MERROR("Failed to connect to " << url); + return false; + } + MDEBUG("GETting " << u_c.uri); + const epee::net_utils::http::http_response_info *info = NULL; + if (!client.invoke_get(u_c.uri, std::chrono::seconds(30), "", &info)) + { + MERROR("Failed to connect to " << url); + client.disconnect(); + return false; + } + if (!info) + { + MERROR("Failed invoking GET command to " << url << ", no status info returned"); + client.disconnect(); + return false; + } + MDEBUG("response code: " << info->m_response_code); + MDEBUG("response comment: " << info->m_response_comment); + MDEBUG("response body: " << info->m_body); + for (const auto &f: info->m_additional_fields) + MDEBUG("additional field: " << f.first << ": " << f.second); + if (info->m_response_code != 200) + { + MERROR("Status code " << info->m_response_code); + client.disconnect(); + return false; + } + client.disconnect(); + f.close(); + MDEBUG("Download complete"); + return true; + } + catch (const std::exception &e) + { + MERROR("Exception in download thread: " << e.what()); + return false; + } + } + + bool download(const std::string &path, const std::string &url) + { + bool success; + std::unique_ptr thread(new boost::thread([&](){ success = download_thread(path, url); })); + thread->join(); + return success; + } +} diff --git a/src/common/download.h b/src/common/download.h new file mode 100644 index 00000000..ab764468 --- /dev/null +++ b/src/common/download.h @@ -0,0 +1,36 @@ +// Copyright (c) 2017, 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. + +#pragma once + +#include + +namespace tools +{ + bool download(const std::string &path, const std::string &url); +} diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 7ed01ca1..fd509b60 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -36,6 +36,7 @@ using namespace epee; #include "common/command_line.h" #include "common/util.h" #include "common/updates.h" +#include "common/download.h" #include "warnings.h" #include "crypto/crypto.h" #include "cryptonote_config.h" @@ -1090,6 +1091,35 @@ namespace cryptonote if (check_updates_level == UPDATES_NOTIFY) return true; + std::string filename; + const char *slash = strrchr(url.c_str(), '/'); + if (slash) + filename = slash + 1; + else + filename = std::string(software) + "-update-" + version; + boost::filesystem::path path(epee::string_tools::get_current_module_folder()); + path /= filename; + if (!tools::download(path.string(), url)) + { + MERROR("Failed to download " << url); + return false; + } + crypto::hash file_hash; + if (!tools::sha256sum(path.string(), file_hash)) + { + MERROR("Failed to hash " << path); + return false; + } + if (hash != epee::string_tools::pod_to_hex(file_hash)) + { + MERROR("Download from " << url << " does not match the expected hash"); + return false; + } + MGINFO("New version downloaded to " << path); + + if (check_updates_level == UPDATES_DOWNLOAD) + return true; + MERROR("Download/update not implemented yet"); return true; }